[excel] [xlsx] [闰年计算] [日期计算]
起因
最近在使用 golang 解析 xlsx 文件完成一些功能,发现使用 excelize 库解析日期类型的值时,获取到的是一个数字。
Excel中显示 2012/12/1
,excelize 解析获得 41244
。
怎么把获得的数字转换成日期类型,好做后面的操作呢。经过搜索获得了解决方案:
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
}
打印结果是正确的,但是为什么要将天数减2才能获取的正确的时间?
关于闰年的计算
Excel 日期的存储
首先,需要知道的是为什么日期类型会变成数字,这个数字代表什么?
Excel 将日期存储为数字,这个数字是从 1900/1/1 起经过的天数。1900/1/1 日是第1天。
如果是这样,应该 days - 1
就可以了,为什么要 days - 2
呢?
如果想要揭开这个谜团,就需要先回答两个问题:
- 闰年如何计算?
- 1900年是闰年么?
如何计算闰年
关于闰年如何计算。大家一定觉得很简单:
可以被4整除的年份,比如大家最熟悉的年份:2008
那么,因为 1900 % 4 == 0
,所以 1900 年是闰年啊。
如果你这么想,那就能体会为什么出现上面的问题了。
首先,1900年不是闰年。翻了大半天日历进行确认:
1900年2月其次,闰年的计算公式不是简单地 “可以被4整除”。闰年的计算:
非整百年份:能被4整除的是闰年。
整百年份:能被400整除的是闰年。
闰年的计算,归结起来就是通常说的:四年一闰;百年不闰,四百年再闰。
看到这个计算公式时,我也是惊到了,因为印象中,一直以来都没有注意过百年的处理条件。
由于1900 年是百年,且不能被 400 整除,所以 1900 年不是闰年。
Excel的bug
Excel使用 1900日期系统,使用从1900/1/1日开始经过的天数表示日期。
但是,Excel错误地假定 1900 年是闰年。所以在 Excel 有一个错误的日期 1900/2/29
,这个日期时不存在的,所以从 1900/3/1
之后,每个日期的表示就多了1天。
这就是 days - 2
的原因,需要减去多余的 1900/3/1
。不过 1900/2/28
及其之前的日期计算就不需要多减去额外的1天了。
对于这个bug,官方是这么解释的:
微软关于1900问题的回答简短的说,就是修改这个bug会耗费大量的时间,并产生兼容问题,而这个问题本身产生的问题有限,所以不做修改。
而且为了计算方便,Excel 保留了 1900/1/0
这个日期。
有趣的1582年
在查阅资料的时候,发现了另外一个有意思的故事:
需要注意的是,公历是根据罗马人的“儒略历”改编而成的。由于当时没有了解到每年要多算出0.0078天的问题,从公元前46年到16世纪,一共累计多出了10天。为此,当时的教皇格列高利十三世,将1582年10月5日改为10月15日,并开始了新的闰年规定。即规定公历年份是整百数的,必须是400的倍数才是闰年,不是400的倍数的年份就是平年。
— 摘自百度百科
1582年,为了更正闰年的计算,整整少了10天。我又翻了大半天日历进行确认:
1582年10月结语
经过这一趟操作下来,有两个感受:
- 方案的确定,一定是有取舍的。发生问题后,需要确定影响的范围,如果修改带来的弊远大于利,且问题影响的范围较小,那么可以舍小取大。问题的解决方式不是一成不变的,问题也不是一定必须修改的,需要根据情况而定。
- 误差是不可避免的,需要动态的更正、调整。但是解决问题还要限定问题的边界,先处理力所能及范围内的。正如闰年的计算也不是一成不变的,但是对于几千年之后的计算误差,目前也没法精确给出,所以还是解决好几百年内的问题吧:
由于地球的自转速度逐渐降低,而公转速度则相对更加稳定,所以上述的系统经过更长的周期也会发生微小的误差。据计算,每8000年会有一天的误差,所以英国的天文学家约翰·赫歇耳提议:公元4000年为平年,以此类推,公元12000年、20000年也是平年。但此提议从未被正式采纳。原因是到了4000年,地球自转的精确速度并非如今可以预测,所以届时参照真实数据方可做出判断。因此,在长远的将来,针对闰年的微小调整应该不是由预定的系统决定,而是随时不定性的。
网友评论