在java.util.Calendar
,1月定义为第0个月,而不是第1个月.是否有任何具体原因?
我见过很多人对此感到困惑......
它只是Java日期/时间API的可怕混乱的一部分.列出它的错误需要很长时间(而且我确定我不知道一半的问题).无可否认地处理日期和时间是棘手的,但无论如何,aaargh.
帮自己一个忙,而不是使用Joda Time,或者可能使用JSR-310.
编辑:至于原因 - 正如在其他答案中所指出的那样,很可能是由于旧的C API,或者只是从0开始所有事情的一般感觉......当然,除了那天从1开始.我怀疑最初的实施团队之外的人是否真的可以说明理由 - 但是,我再次强烈建议读者不要过分担心为什么会做出错误的决定,以便查看整个范围内的肮脏java.util.Calendar
并找到更好的东西.
一个点,这是赞成使用基于0的索引的是,它使事情变得像"名字的阵列"更容易:
// I "know" there are 12 months String[] monthNames = new String[12]; // and populate... String name = monthNames[calendar.get(Calendar.MONTH)];
当然,只要您获得13个月的日历,这就会失败......但至少指定的大小是您期望的月数.
这不是一个好理由,但这是一个原因......
编辑:作为一种评论请求一些关于我认为日期/日历错误的想法:
令人惊讶的基数(1900年作为Date的年份基础,不可否认的是被弃用的构造函数; 0作为两者的月基数)
可变性 - 使用不可变类型可以更简单地处理真正有效的值
一组不充分的类型:拥有Date
和Calendar
不同的东西很好,但缺少"本地"与"分区"值的分离,日期/时间与日期与时间的关系也是如此
一个API导致带有魔术常量的丑陋代码,而不是明确命名的方法
一个非常难以推理的API - 关于重新计算事物的所有业务等
使用无参数构造函数默认为"now",这导致难以测试的代码
在Date.toString()
它总是使用系统本地时区的实现(这是以前,现在弄得很多堆栈溢出的用户)
因为用数月做数学会容易得多.
12月之后的1个月是1月,但通常你需要拿出月份数并做数学
12 + 1 = 13 // What month is 13?
我知道!我可以使用12的模数快速解决这个问题.
(12 + 1) % 12 = 1
直到11月,这个工作正常11个月......
(11 + 1) % 12 = 0 // What month is 0?
您可以通过在添加月份之前减去1来再次使所有这些工作,然后执行模数并最终再次添加1 ...也就是解决潜在问题.
((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!
现在让我们考虑一下0到11个月的问题.
(0 + 1) % 12 = 1 // February (1 + 1) % 12 = 2 // March (2 + 1) % 12 = 3 // April (3 + 1) % 12 = 4 // May (4 + 1) % 12 = 5 // June (5 + 1) % 12 = 6 // July (6 + 1) % 12 = 7 // August (7 + 1) % 12 = 8 // September (8 + 1) % 12 = 9 // October (9 + 1) % 12 = 10 // November (10 + 1) % 12 = 11 // December (11 + 1) % 12 = 0 // January
所有月份的工作都相同,并且不需要解决问题.
基于C语言在某种程度上复制C. 的tm
结构(在定义的time.h
)具有一个整数字段tm_mon
具有0-11的(注释)范围.
基于C语言在索引0处启动数组.因此,这对于输出月份名称数组中的字符串非常方便,并将其tm_mon
作为索引.
对此有很多答案,但无论如何我都会就这个问题发表看法.如前所述,这种奇怪行为背后的原因来自POSIX C time.h
,其中存储在0-11范围内的int的月份.为了解释原因,请这样看; 年和日被认为是口语中的数字,但是几个月都有自己的名字.因此,1月是第一个月,它将被存储为第一个数组元素offset 0.monthname[JANUARY]
会的"January"
.一年中的第一个月是第一个月的数组元素.
另一方面,日期数字,因为它们没有名称,将它们存储在int中作为0-30会令人困惑,添加大量day+1
输出指令,当然,容易出现很多错误.
话虽这么说,不一致是令人困惑的,特别是在javascript(它也继承了这个"功能"),一种脚本语言,这应该被抽象远离langague.
TL; DR:因为月份有名字,而月份没有.
在Java 8中,有一个更新的日期/时间API JSR 310更加理智.spec规范与JodaTime的主要作者相同,它们共享许多类似的概念和模式.
我会说懒惰.数组从0开始(每个人都知道); 一年中的几个月是一个阵列,这使我相信Sun的一些工程师只是不愿意把这一点放在Java代码中.
可能是因为C的"struct tm"也是如此.
因为程序员痴迷于基于0的索引.好吧,它比这更复杂:当你使用低级逻辑来使用基于0的索引时,它更有意义.但总的来说,我仍然坚持我的第一句话.