tl; dr
除了通过手动更改主机上的系统时钟之外,是否可以通过代码或使用JVM参数覆盖通过System.currentTimeMillis显示的当前时间?
是。
Instant.now(
Clock.fixed(
Instant.parse( "2016-01-23T12:34:56Z"), ZoneOffset.UTC
)
)
Clock
在java.time中
我们为可插拔时钟替换问题提供了一种新的解决方案,以方便使用人造日期时间值进行测试。该java.time包中的Java 8包含一个抽象类java.time.Clock
,有明确的目的:
允许在需要时插入备用时钟
您可以插入自己的实现Clock
,尽管您可能会找到已经满足您需要的实现。为了方便起见,java.time包含静态方法以产生特殊的实现。这些替代的实现在测试过程中可能很有价值。
节奏改变
各种 tick…
方法产生的时钟以不同的节奏增加当前时刻。
默认值Clock
报告的时间在Java 8中的更新频率为毫秒,而在Java 9中的更新时间为纳秒 (取决于您的硬件)。您可以要求以不同的粒度报告真实的当前时刻。
错误的时钟
可能存在一些时钟,产生的结果与主机OS硬件时钟的结果不同。
例如,锁定今年最早的圣诞节的第一刻。换句话说,当圣诞老人和他的驯鹿首次出发时。如今,最早的时区似乎Pacific/Kiritimati
在+14:00
。
LocalDate ld = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate xmasThisYear = MonthDay.of( Month.DECEMBER , 25 ).atYear( ld.getYear() );
ZoneId earliestXmasZone = ZoneId.of( "Pacific/Kiritimati" ) ;
ZonedDateTime zdtEarliestXmasThisYear = xmasThisYear.atStartOfDay( earliestXmasZone );
Instant instantEarliestXmasThisYear = zdtEarliestXmasThisYear.toInstant();
Clock clockEarliestXmasThisYear = Clock.fixed( instantEarliestXmasThisYear , earliestXmasZone );
使用该特殊的固定时钟始终返回同一时刻。我们在基里蒂马蒂(Kiritimati)取得圣诞节的第一刻,UTC显示的时钟时间早于12月24日的上午10点,即十四小时。
Instant instant = Instant.now( clockEarliestXmasThisYear );
ZonedDateTime zdt = ZonedDateTime.now( clockEarliestXmasThisYear );
Instant.toString():2016-12-24T10:00:00Z
zdt.toString():2016-12-25T00:00 + 14:00 [太平洋/基里提马蒂]
请参阅IdeOne.com中的实时代码。
真实时间,不同时区
您可以控制Clock
实现分配的时区。这在某些测试中可能很有用。但是我不建议在生产代码中使用此代码,在生产代码中,您应始终明确指定可选参数ZoneId
或ZoneOffset
参数。
您可以指定UTC为默认区域。
ZonedDateTime zdtClockSystemUTC = ZonedDateTime.now ( Clock.systemUTC () );
您可以指定任何特定的时区。指定适当的时区名称,格式continent/region
,如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用3-4字母的缩写,例如EST
或,IST
因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。
ZonedDateTime zdtClockSystem = ZonedDateTime.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
您可以指定JVM的当前默认时区应为特定Clock
对象的默认时区。
ZonedDateTime zdtClockSystemDefaultZone = ZonedDateTime.now ( Clock.systemDefaultZone () );
运行此代码进行比较。请注意,它们都报告同一时刻,时间轴上的同一点。它们只是挂钟时间有所不同;换句话说,三种表达同一事物的方式,三种表达同一时刻的方式。
System.out.println ( "zdtClockSystemUTC.toString(): " + zdtClockSystemUTC );
System.out.println ( "zdtClockSystem.toString(): " + zdtClockSystem );
System.out.println ( "zdtClockSystemDefaultZone.toString(): " + zdtClockSystemDefaultZone );
America/Los_Angeles
是运行此代码的计算机上JVM当前的默认区域。
zdtClockSystemUTC.toString():2016-12-31T20:52:39.688Z
zdtClockSystem.toString():2016-12-31T15:52:39.750-05:00 [美国/蒙特利尔]
zdtClockSystemDefaultZone.toString():2016-12-31T12:52:39.762-08:00 [America / Los_Angeles]
根据Instant
定义,该类始终使用UTC。因此,这三个与区域相关的Clock
用法具有完全相同的效果。
Instant instantClockSystemUTC = Instant.now ( Clock.systemUTC () );
Instant instantClockSystem = Instant.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
Instant instantClockSystemDefaultZone = Instant.now ( Clock.systemDefaultZone () );
InstantClockSystemUTC.toString():2016-12-31T20:52:39.763Z
InstantClockSystem.toString():2016-12-31T20:52:39.763Z
InstantClockSystemDefaultZone.toString():2016-12-31T20:52:39.763Z
默认时钟
默认情况下用于的实现Instant.now
是所返回的实现Clock.systemUTC()
。这是未指定时使用的实现Clock
。亲自查看Java的预发行版9源代码Instant.now
。
public static Instant now() {
return Clock.systemUTC().instant();
}
默认Clock
的OffsetDateTime.now
和ZonedDateTime.now
是Clock.systemDefaultZone()
。参见源代码。
public static ZonedDateTime now() {
return now(Clock.systemDefaultZone());
}
在Java 8和Java 9之间,默认实现的行为发生了变化。在Java 8中,尽管类具有存储纳秒级分辨率的能力,但当前时刻仅以毫秒级的分辨率捕获。Java 9带来了一个新的实现,该实现能够以纳秒的分辨率捕获当前时刻-当然,这取决于计算机硬件时钟的能力。
关于java.time
该java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.Date
,Calendar
,和SimpleDateFormat
。
要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
您可以直接与数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。不需要字符串,不需要类。Hibernate 5和JPA 2.2支持java.time。java.sql.*
在哪里获取java.time类?