我在使用Date和Calendar类处理模拟商店到达时间的指数分布时发现了这种有趣的行为(学术工作)。代码非常简单,如下所示。假设"this.currentDate"是"2014年2月15日08:00:00 BRST"。如果我将时间向前移动24小时(参数iSeconds=86.400),应该返回什么?预期的字符串是"2014-02-16 08:00:00",但时间缩短了1小时,结果是"2014-03-16 07:00:00",我想知道是否有人能解释为什么我的一个小时被"偷走"。没什么大不了的,但由于我的下一次到达时间取决于前一次,这让我的时间基线也变了一个小时。
我以为可能是TZ的问题,但天哪,我在2月中旬刚搬了24小时。
public String shiftTimeStamp( int iSeconds)
{
Calendar cal = Calendar.getInstance();
cal.setTime(this.currentDate);
cal.add(Calendar.SECOND, iSeconds);
this.currentDate = cal.getTime();
String sTS = new SimpleDateFormat(SCSimLabels.DATE_TS_FORMAT).format(this.currentDate);
return sTS;
}
注意:夏令时问题:)BRT<-->BRST tz
我的解决方法:我只想用一个信标来引导因到达时间间隔而导致的时间跳跃,我对这种日历的特殊性不感兴趣,所以当我需要转移到第二天的第一个工作时间时,我只会强制将时间定为1天轮班后的08:00:00。它就像一个符咒:)
Calendar cal = Calendar.getInstance();
cal.setTime(this.currentDate);
cal.add(Calendar.DATE, 1);
String sDate = (new SimpleDateFormat("yyyy-MM-dd 08:00:00")).format(cal.getTime());
Date newDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(sDate);
this.currentDate = newDate;
将格式调用更改为:
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").format(this.currentDate);
查看格式化调用使用的时区。我敢打赌,对.add()的调用是在修改Calendar对象的时区,因为它跨越了标准时间/夏令时边界。
如果是这种情况,您可以尝试在.add调用后将Calendar对象的Calendar.DAY、1或简单的.setTimeZone(…)添加回原始时区。
避免遗留日期时间类
您正在使用麻烦的旧日期时间类,这些类现在是遗留的,已被java.time类取代。
使用java.time
如果您希望使用没有任何时区或UTC偏移的通用24小时工作日,请使用LocalDateTime
类。如果始终希望在上午8点开始,请指定LocalTime
。
LocalDate ld = LocalDate.of( 2014 , Month.FEBRUARY , 15 ) ;
LocalTime lt = LocalTime.of( 8 , 0 ) ; // Specify hour in 24-hour clock, 0-23.
LocalDateTime ldt = LocalDateTime.of( ld , lt );
将您的24小时跨度表示为Duration
。
Duration d = Duration.ofHours( 24 );
LocalDateTime ldtLater = ldt.plus( d );
如果您想通过某个地区特定的挂钟时间来处理时间线上的特定时刻,请指定ZoneId
以获得ZonedDateTime
。
ZoneId z = ZoneId.of( "America/Sao_Paulo" ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
ZonedDateTime zdtLater = zdt.plus( d );
请注意,将24小时添加到ZonedDateTime是,而不是与添加一天相同。正如你所学到的,夏令时(DST)等异常情况意味着一天可能长达23、24或25小时,甚至其他时间。因此,如果您想添加一天,并让java.time应用其逻辑在一天中的适当时间到达,同时考虑DST等异常情况,请添加天,而不是小时。
ZonedDateTime zdtLater = zdt.plusDays( 1 );
或者添加一整天的Period
,而不是24小时的Duration
。
Period p = Period.ofDays( 1 );
ZonedDateTime zdtLater = zdt.plus( p );
关于java.time
java.time框架是在java 8及更高版本中构建的。这些类取代了诸如java.util.Date
、Calendar
和&CCD_ 10。
JodaTime项目现在处于维护模式,建议迁移到java.Time类。
要了解更多信息,请参阅Oracle教程。并在Stack Overflow中搜索许多示例和解释。规范是JSR310。
从哪里获得java.time类?
- Java SE 8、Java SE 9及更高版本
- 内置
- 标准Java API的一部分,带有捆绑实现
- Java 9添加了一些小功能和修复程序
- Java SE 6和Java SE 7
- 大部分java.time功能都是向后移植到Java6&7英寸ThreeTen背包
- 安卓
- ThreeTenABP项目专门为安卓系统改编了ThreeTen Backport(如上所述)
- 请参阅如何使用ThreeTenABP…
ThreeTen Extra项目通过附加类扩展java.time。这个项目是将来可能添加到java.time的试验场。您可以在这里找到一些有用的类,如Interval
、YearWeek
、YearQuarter
等等。