[excel] [xlsx] [閏年計(jì)算] [日期計(jì)算]
起因
最近在使用 golang 解析 xlsx 文件完成一些功能草慧,發(fā)現(xiàn)使用 excelize 庫解析日期類型的值時(shí)稿静,獲取到的是一個(gè)數(shù)字假夺。
Excel中顯示 2012/12/1
烟逊,excelize 解析獲得 41244
。
怎么把獲得的數(shù)字轉(zhuǎn)換成日期類型,好做后面的操作呢。經(jīng)過搜索獲得了解決方案:
import (
"fmt"
"time"
)
func main() {
days := 41244
date := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).AddDate(0, 0, days-2)
fmt.Println(date) // 2012-12-01 00:00:00 +0000 UTC
}
打印結(jié)果是正確的边器,但是為什么要將天數(shù)減2才能獲取的正確的時(shí)間?
關(guān)于閏年的計(jì)算
Excel 日期的存儲(chǔ)
首先托修,需要知道的是為什么日期類型會(huì)變成數(shù)字忘巧,這個(gè)數(shù)字代表什么?
Excel 將日期存儲(chǔ)為數(shù)字诀黍,這個(gè)數(shù)字是從 1900/1/1 起經(jīng)過的天數(shù)袋坑。1900/1/1 日是第1天。
如果是這樣眯勾,應(yīng)該 days - 1
就可以了枣宫,為什么要 days - 2
呢?
如果想要揭開這個(gè)謎團(tuán)吃环,就需要先回答兩個(gè)問題:
- 閏年如何計(jì)算也颤?
- 1900年是閏年么?
如何計(jì)算閏年
關(guān)于閏年如何計(jì)算郁轻。大家一定覺得很簡單:
可以被4整除的年份翅娶,比如大家最熟悉的年份:2008
那么,因?yàn)?1900 % 4 == 0
好唯,所以 1900 年是閏年啊竭沫。
如果你這么想,那就能體會(huì)為什么出現(xiàn)上面的問題了骑篙。
首先蜕提,1900年不是閏年。翻了大半天日歷進(jìn)行確認(rèn):
其次靶端,閏年的計(jì)算公式不是簡單地 “可以被4整除”谎势。閏年的計(jì)算:
非整百年份:能被4整除的是閏年。
整百年份:能被400整除的是閏年杨名。
閏年的計(jì)算脏榆,歸結(jié)起來就是通常說的:四年一閏;百年不閏台谍,四百年再閏须喂。
看到這個(gè)計(jì)算公式時(shí),我也是驚到了趁蕊,因?yàn)橛∠笾心髡郏恢币詠矶紱]有注意過百年的處理?xiàng)l件。
由于1900 年是百年介衔,且不能被 400 整除恨胚,所以 1900 年不是閏年。
Excel的bug
Excel使用 1900日期系統(tǒng)炎咖,使用從1900/1/1日開始經(jīng)過的天數(shù)表示日期赃泡。
但是,Excel錯(cuò)誤地假定 1900 年是閏年乘盼。所以在 Excel 有一個(gè)錯(cuò)誤的日期 1900/2/29
升熊,這個(gè)日期時(shí)不存在的,所以從 1900/3/1
之后绸栅,每個(gè)日期的表示就多了1天级野。
這就是 days - 2
的原因,需要減去多余的 1900/3/1
粹胯。不過 1900/2/28
及其之前的日期計(jì)算就不需要多減去額外的1天了蓖柔。
對于這個(gè)bug辰企,官方是這么解釋的:
簡短的說,就是修改這個(gè)bug會(huì)耗費(fèi)大量的時(shí)間况鸣,并產(chǎn)生兼容問題牢贸,而這個(gè)問題本身產(chǎn)生的問題有限,所以不做修改镐捧。
而且為了計(jì)算方便潜索,Excel 保留了 1900/1/0
這個(gè)日期。
有趣的1582年
在查閱資料的時(shí)候懂酱,發(fā)現(xiàn)了另外一個(gè)有意思的故事:
需要注意的是竹习,公歷是根據(jù)羅馬人的“儒略歷”改編而成的。由于當(dāng)時(shí)沒有了解到每年要多算出0.0078天的問題列牺,從公元前46年到16世紀(jì)整陌,一共累計(jì)多出了10天。為此昔园,當(dāng)時(shí)的教皇格列高利十三世蔓榄,將1582年10月5日改為10月15日,并開始了新的閏年規(guī)定默刚。即規(guī)定公歷年份是整百數(shù)的甥郑,必須是400的倍數(shù)才是閏年,不是400的倍數(shù)的年份就是平年荤西。
— 摘自百度百科
1582年澜搅,為了更正閏年的計(jì)算,整整少了10天邪锌。我又翻了大半天日歷進(jìn)行確認(rèn):
結(jié)語
經(jīng)過這一趟操作下來勉躺,有兩個(gè)感受:
- 方案的確定,一定是有取舍的觅丰。發(fā)生問題后饵溅,需要確定影響的范圍,如果修改帶來的弊遠(yuǎn)大于利妇萄,且問題影響的范圍較小蜕企,那么可以舍小取大。問題的解決方式不是一成不變的冠句,問題也不是一定必須修改的轻掩,需要根據(jù)情況而定。
- 誤差是不可避免的懦底,需要?jiǎng)討B(tài)的更正唇牧、調(diào)整。但是解決問題還要限定問題的邊界,先處理力所能及范圍內(nèi)的丐重。正如閏年的計(jì)算也不是一成不變的腔召,但是對于幾千年之后的計(jì)算誤差,目前也沒法精確給出弥臼,所以還是解決好幾百年內(nèi)的問題吧:
由于地球的自轉(zhuǎn)速度逐漸降低宴咧,而公轉(zhuǎn)速度則相對更加穩(wěn)定根灯,所以上述的系統(tǒng)經(jīng)過更長的周期也會(huì)發(fā)生微小的誤差径缅。據(jù)計(jì)算,每8000年會(huì)有一天的誤差烙肺,所以英國的天文學(xué)家約翰·赫歇耳提議:公元4000年為平年纳猪,以此類推,公元12000年桃笙、20000年也是平年氏堤。但此提議從未被正式采納。原因是到了4000年搏明,地球自轉(zhuǎn)的精確速度并非如今可以預(yù)測鼠锈,所以屆時(shí)參照真實(shí)數(shù)據(jù)方可做出判斷。因此星著,在長遠(yuǎn)的將來购笆,針對閏年的微小調(diào)整應(yīng)該不是由預(yù)定的系統(tǒng)決定,而是隨時(shí)不定性的虚循。