tl;博士
除了手动更改主机上的系统时钟之外,有没有办法(无论是在代码中还是使用JVM参数)来覆盖当前时间,如System.currentTimeMillis所示?
是的。
Instant.now(
Clock.fixed(
Instant.parse( "2016-01-23T12:34:56Z"), ZoneOffset.UTC
)
)
Clock
在 java.time 中
对于可插拔时钟更换的问题,我们有了一个新的解决方案,以方便使用虚假的日期时间值进行测试。Java 8 中的 java.time 包包含一个抽象类 java.time.Clock
,具有明确的用途:
允许在需要时插入备用时钟
您可以插入自己的 实现,尽管您可能可以找到一个已经满足您需求的实现。为了您的方便,java.time包含静态方法以产生特殊的实现。这些替代实现在测试期间可能很有价值。Clock
改变的节奏
各种方法产生的时钟以不同的节奏增加当前矩。tick…
缺省值报告更新时间在 Java 8 中为毫秒,在 Java 9 中更新为纳秒(具体取决于您的硬件)。您可以要求以不同的粒度报告真实的当前时刻。Clock
假时钟
某些时钟可能会丢失,从而产生与主机操作系统硬件时钟不同的结果。
-
固定
- 将单个不变(非递增)力矩报告为当前矩。
-
偏移
量 - 报告当前时刻,但由传递的持续时间
参数平移。
例如,锁定今年最早的圣诞节的第一刻。换句话说,当圣诞老人和他的驯鹿第一站时。现在最早的时区似乎是 太平洋/基里蒂马蒂
在 。+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点早了14个小时。
Instant instant = Instant.now( clockEarliestXmasThisYear );
ZonedDateTime zdt = ZonedDateTime.now( clockEarliestXmasThisYear );
瞬时至弦(): 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 () );
您可以指定任何特定的时区。以 的格式指定适当的时区名称,例如 美国/蒙特利尔
、非洲/卡萨布兰卡
或 。切勿使用3-4个字母的缩写,例如或因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。continent/region
Pacific/Auckland
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[美国/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 () );
即时时钟系统UTC.toString(): 2016-12-31T20:52:39.763Z
即时时钟系统到字符串(): 2016-12-31T20:52:39.763Z
即时时钟系统默认Zone.toString(): 2016-12-31T20:52:39.763Z
默认时钟
默认情况下使用的实现是 Clock.systemUTC()
返回的实现。这是在未指定 .在 Instant.now
的预发布 Java 9 源代码中亲自查看。Instant.now
Clock
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 类?