不同编程语言的库如何处理日期时间、时间戳和持续时间、闰秒和年份、夏令时和时区等?

26 投票
6 回答
3159 浏览
提问于 2025-04-16 04:08

有没有一个标准的机构或者具体的规范,说明时间相关的东西应该怎么在实际中实现(就像ICU处理Unicode相关任务那样)?还是说现在这方面的实现都是“尽力而为”,主要看语言和库的开发者愿意花多少时间、精力和钱?

有没有一个具体而完整的实现,可以作为时间相关内容处理的示例?

你认为现有的哪些库是糟糕的、还不错的或者好的例子?

6 个回答

11

时间和日期(日历)是两回事
第一个问题是,日期并不是和时间直接相关,而是和地球、月亮等天体的位置,以及人类活动的规律性有关。时间也是主观的、相对的,甚至是相对论的,可以通过天文或原子来测量。

时间和日期/日历的标准
国际标准化组织(ISO) [4] 发布了

  • “ISO 8601 数据元素和交换格式——信息交换——日期和时间的表示” [4a]

这个标准像其他国际标准一样,是一种推荐,基于已经建立的实践。
它(主观上)仅基于公历 [5] 和前瞻性公历(向前推算到它实际被发明之前,所以在处理历史日期时用途有限) [5a]。

世界日历协会 [1d] 从2012年开始推动新的世界日历的引入 [1b-1d],这将使现有的日期库变得没用。同样,主要问题还是在于后面会提到的。

我见过的最全面的与日期时间相关的IT系统比较是 [2],涉及BIG8数据库管理系统(IBM DB2、Informix、Ingres、InterBase、Microsoft SQL Server、MySQL、Oracle和Sybase)。
这些调查显示,即使是相同的公历时间/日期,在所有系统之间以及同一平台内(不同产品和版本之间)的处理方式也不同,见例如 [3]。

所有日期/时间库的主要问题在于所有系统和框架中,它们的日期/时间数据类型不允许包含地理和日历信息
没有这些信息,它们主要是半无用的——在SQL Server的datetime2值中,毫秒有什么意义呢?在7世纪的时候,甚至没有时钟能精确到分钟(例如,伽利略在实验中用心跳来测量时间间隔),而且公历还没有被发明。

因此,很多日期时间类型的空间被误用,未能提供与日期相关的最重要的灵活性,即将地理和/或日历信息与日期关联起来。

简单举几个例子:

  • 现代俄罗斯使用公历,而俄罗斯东正教使用儒略历,因此俄罗斯的许多国家假期是根据儒略历来确定的(例如,俄罗斯的圣诞节在1月7日,旧新年在1月14日,而其他宗教假期的日期则相对于公历是浮动的)。
  • 在1917年之前,作为波兰一部分的俄罗斯使用公历,而其他地区的俄罗斯使用儒略历(在“同一”时区之间有13到18天的浮动差异) [5b];
  • 在MS Windows中双击时钟(或打开控制面板 --> 日期和时间) --> 时区选项卡 --> 在下拉框中查看时区。你会看到从GMT-12:00到GMT+13:00之间有25个小时,超过一百个时区,还有像GMT+5:00、GMT+5:30、GMT+5:45等带小数的时区。

==== 引用:
[1] 新世界日历
[1a]更新:抱歉,不要阅读[1a],作者混淆了日历,写了错误的信息
世界日历2012:每个月35天
http://www.panorama.am/en/society/2010/01/29/newcalendar

[1b] http://en.wikipedia.org/wiki/World_Calendar
[1c] http://www.theworldcalendarin2012.org/Index2.htm
[1d] http://www.theworldcalendar.org/TWCA.htm

[2] Peter Gulutzan, Trudy Pelzer. SQL性能调优:SQL中的日期
http://www.informit.com/articles/printerfriendly.aspx?p=30939

[3] SqlDateTime.MinValue != C# DateTime.MinValue,为什么?
SqlDateTime.MinValue != DateTime.MinValue,为什么?

[4]
国际标准化组织
http://en.wikipedia.org/wiki/International_Organization_for_Standardization
[4a] ISO 8601 数据元素和交换格式——信息交换——日期和时间的表示
http://en.wikipedia.org/wiki/ISO_8601

[5]
公历
http://en.wikipedia.org/wiki/Gregorian_calendar
[5a] 前瞻性公历
http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
[5b] 公历采用
http://en.wikipedia.org/wiki/Gregorian_calendar#Adoption
[6]
http://en.wikipedia.org/wiki/Galileo_Galilei

11

我觉得现在没有一个统一的标准来处理这些事情,不过有几个标准可以参考,比如ISO 8601。

ICU是一个跨语言(C/C++和Java)和多平台的日期/时间处理库。

它内部处理日期和时间,通常使用UDate(C/C++)或java.util.Date/long(Java),这些都是从1970年1月1日开始的毫秒数,或者使用特定日历类型的Calendar对象(比如公历和伊斯兰历等)。它还提供了持续时间的格式化功能。闰年会根据日历系统来计算,而闰秒则由底层操作系统来处理。夏令时和时区的数据会通过一个叫“tz数据库”的东西保持更新,这个数据库有时也用它作者的姓氏Olson来称呼。

希望这些信息能对你了解ICU有所帮助。

17

我会尝试用可能会成为Java 7一部分的Java库来回答第二和第三个问题。

javax.time.* (JSR 310)

这些类是对JodaTime的全面重写,旨在修复util.Dateutil.Time以及JodaTime的设计缺陷。

JSR 310试图提供一个全面的日期和时间模型,这个模型是类型安全的,并且自我说明。它可以与现有的类互操作,同时也考虑了基于XML和数据库的使用场景。类是最终的、不可变的、线程安全的,构造后不能修改。实例是通过一系列丰富的工厂方法创建的,这些方法可以在后台缓存数据。

LocalDate dateToday     = LocalDate.of(2010, 9, 14);
LocalDate oneMonthLater = dateToday.with(OCTOBER);
LocalDate oneYearLater  = dateToday.withYear(2011);

这个API有一些“机器导向”的类和一些“人类导向”的类:

机器导向

Instant

表示一个时间点,可以与Unix或Java时间戳相比较。实际上有InstantTAIInstantUTCInstant,让人们可以精确选择他们需要的时间定义,比如“基于天的”、“线性的,没有闰秒”等等。

Duration

表示一个时间范围,不一定与特定的日期或日历相关联。

人类导向

有丰富的类来处理不同的使用场景,比如仅日期、仅时间、日期和时间,带时区和不带时区,带夏令时和不带夏令时。

DateProvider

OffsetDateLocalDate(兼容java.sql.Date

TimeProvider

OffsetTimeLocalTime(兼容java.sql.Time

DateTimeProvider

ZonedDateTimeOffsetDateTimeLocalDateTime(兼容java.util.GregorianCalendar

InstantProvider

InstantZonedDateTimeOffsetDateTime(兼容java.util.Date

Period

表示一个时间段,比如“5天”,可以加到或减去某个日期/时间。

Matcher

可以用来查询,比如“这个日期是在2006年吗?”或者“这一天是这一年的最后一天吗?”

Adjuster

如果你想做更复杂的修改,比如“给我这个月的最后一天!”或者“圣诞节后的第二个星期二,请!”时,调整器就派上用场了。

Resolver

解析器允许用户定义如果某个日期无效时应该发生什么,比如2010年2月31日:

DateResolver previous = DateResolvers.previousValid();
LocalDate date = date(2010, 2, 30, previous);
// date = 2010-02-28

处理时区和夏令时数据

可以序列化这些类,并使用当前时区数据或它们被序列化时的时区数据进行反序列化。此外,可以比较不同时区的规则:可以找出夏令时规则是否发生了变化,比如在伦敦或莫斯科的2010e和2010f版本之间,并决定如果时间在间隙或重叠中应该怎么处理。

日历系统

虽然一切都是基于ISO-8601,但也提供了简单的希伯来历、伊斯兰历、日本历、泰国佛历等日历系统。

格式化和解析

toString()返回ISO8601格式,支持像SimpleDateFormat中的模式以及更高级的格式。

集成

  • 数据库
  • JodaTime
  • 遗留的JDK类(java.util.*
  • XML

参考资料:

撰写回答