简述
- 纳秒只能跟踪三个世纪。
- 永远不要自己编写时间记录代码。☛ 使用 java.time ☚
- 在交换日期时间值时,永远不要使用从纪元开始的计数,而应使用标准 ISO 8601 格式的文本。
做数学运算
您必须选择一个纪元参考日期。尽管行业中使用了数十种不同的纪元,但其中一种更常见的是通常称为Unix 时间:UTC 中的 1970 年第一个时刻,即 1970-01-01T00:00:00Z。该点之后的时刻使用正数计数,之前的时刻使用负数计数。
当然,一年的长度因闰年而异,但我们可以轻松地近似。使用 TimeUnit
类及其方便的转换方法获取一年中的纳秒数。
进行一些计算。
long nanosPerDay = TimeUnit.DAYS.toNanos( 1L );
long nanosPerYear = ( long ) ( nanosPerDay * 365.25 );
long maximumYears = ( Long.MAX_VALUE / nanosPerYear );
将内容输出到控制台。
System.out.println( "nanosPerDay: " + nanosPerDay );
System.out.println( "nanosPerYear: " + nanosPerYear );
System.out.println( "Long.MAX_VALUE: " + Long.MAX_VALUE + " provides for maximumYears: " + maximumYears );
请看这段代码在IdeOne.com上的实时运行code run live at IdeOne.com。
nanosPerDay: 86400000000000
nanosPerYear: 31557600000000000
Long.MAX_VALUE: 9223372036854775807提供了最大年数:292
所以我们可以获得1970年前后不到±3个世纪。如果几个世纪对您的应用程序足够好,那么这种方法确实有效。至少当日历耗尽时,您将无法修复它。 ;-)
Instant
幸运的是,我们不需要自己进行这些计算。
现代Java恰好带有业务导向应用程序可用的最先进的日期时间处理框架:JSR 310中定义的java.time类。
java.time.Instant
类使用不同的策略来跟踪时间。该类在1970年UTC时期之前/之后保持整个秒的计数,加上瞬间的分数纳秒的单独计数。
把它想象成:
moment = ( whole-seconds + fractional-second ) ; // Since 1970-01-01T00:00:00Z.
这使得最小值/最大值为:
请注意,java.time类具有纳秒的分辨率,而它们替代的遗留类(Date
、Calendar
等)的分辨率为毫秒。
有关详细信息,请参见class JavaDoc和OpenJDK source code(在Mercurial中)。
提示:对于业务目的,最适合使用
java.time类。如果您从事科学、工程、考古或历史工作,请使用适用于该领域的其他符号,通常为
String
类型。
粒度
请注意,不同的计算机系统在其时间记录中使用不同的粒度。
如上所述,
java.time类解析为纳秒,有九个数字的小数秒。旧版类使用毫秒,有三个数字的小数秒。其他系统,例如
Postgres数据库,使用
微秒,有六个数字的小数秒。在跨系统交换或比较数据时,您可能需要在各种
java.time类上使用
truncatedTo
方法。
当前常规计算机硬件时钟仅精确到微秒或更粗糙。它们可能报告纳秒,但精度不到那个程度。因此,您将发现Java 9、10和11中的
Instant.now()
方法捕获当前时刻到微秒(虽然数据类型可以处理纳秒,但它检测当前时间是以微秒为单位)。
Instant.now().toString(): 2019-01-23T12:34:56.123456Z
关于java.time
java.time框架内置于Java 8及更高版本中。这些类取代了令人头疼的旧legacy日期时间类,如java.util.Date
、Calendar
和SimpleDateFormat
。
要了解更多信息,请参见Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是JSR 310。
Joda-Time项目现在处于维护模式,建议迁移到java.time类。
您可以直接使用java.time对象与数据库交换。使用符合JDBC 4.2或更高版本的JDBC驱动程序。无需字符串,无需java.sql.*
类。Hibernate 5和JPA 2.2支持java.time。
如何获取java.time类?
System.out.println(new Date(Long.MAX_VALUE / 1000000L));
- Elliott Frisch