StK*_*ler 2 java period jodatime dst
我们在应用程序中使用固定时间段.当用户添加新的时段时,默认情况下应该是第二天的上午6:00到下午6:00.
通常情况下,它是24小时,但有一个问题:当执行夏令时更改时,该时间段的长度会发生变化.例如 :
10月27日上午6:00至10月28日上午6:00.在此期间执行从CEST到CET时区的转换.因此,这个时期包含25个小时:
From 27 October 6:00 AM to 28 October 3:00 AM - there are 21 hours
at 3:00 am the time is shifted back by 1 hour, so there are 4 hours until 28 October 6:00 AM.
Run Code Online (Sandbox Code Playgroud)
我们遇到了这个问题并尝试编写单元测试以防止它再次出现.测试在我们的计算机上成功通过,但在CI服务器上失败(它在另一个时区).
问题是:我们怎样才能独立于机器的时区设计我们的单元测试?
目前,使用Joda-Time计算小时跨度:
if ((aStartDate == null) || (aEndDate == null)) {
return 0;
}
final DateTime startDate = new DateTime(aStartDate);
final DateTime endDate = new DateTime(aEndDate);
return Hours.hoursBetween(startDate, endDate).getHours();
Run Code Online (Sandbox Code Playgroud)
在我们这边传递但在CI服务器上失败的单元测试:
Calendar calendar = Calendar.getInstance();
calendar.set(2012, Calendar.OCTOBER, 27, 6, 0);
startDate= calendar.getTime();
calendar.set(2012, Calendar.OCTOBER, 28, 6, 0);
endDate= calendar.getTime();
Run Code Online (Sandbox Code Playgroud)
我们尝试将时区用于日历:
TimeZone.setDefault(TimeZone.getTimeZone(TimeZone.getTimeZone("GMT").getID()));
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(TimeZone.getTimeZone("GMT").getID()));
calendar.set(2012, Calendar.OCTOBER, 27, 6, 0, 0);
startDate= calendar.getTime();
calendar.set(2012, Calendar.OCTOBER, 28, 6, 0, 0);
endDate= calendar.getTime();
Run Code Online (Sandbox Code Playgroud)
但在这种情况下,结果startDate是10月27日9:00 CEST,endDate是10月28日8:00 CET,所以测试甚至不在我们这边.
先感谢您.
我们如何能够独立于机器的时区设计我们的单元测试?
始终指定时区.
几乎总是,您的代码应指定时区.仅当您真正需要用户/服务器的默认时区时才省略时区; 即使这样,您也应该明确访问默认时区以使您的代码自我记录.
如果省略时区,将使用JVM的默认时区.
Joda-Time有多个地方你可以传递一个DateTimeZone对象,也可以调用withZone方法.
避免使用时区代码,如CEST和CET.它们既不标准也不独特.当混淆/忽略夏令时(DST)时,它们经常被错误地使用.
而是使用适当的时区名称.大多数名称是时区区域的大陆和主要城市的组合,用纯ASCII字符(没有变音符号)编写.例如,Europe/Chisinau.
在Joda-Time,那将是......
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Chisinau" );
Run Code Online (Sandbox Code Playgroud)
您真正的问题是考虑本地日期时间值,或尝试使用本地时间执行业务逻辑.这样做非常困难,因为一般的夏令时及其频繁的变化以及其他异常情况.
相反,要考虑宇宙的时间线而不是壁钟时间.尽可能将日期时间值存储和处理为UTC(GMT)值.转换为本地分区时间以呈现给用户(如本地化字符串)以及业务目的所需的位置,例如确定由特定时区定义的"日期"的开始.
所以,如果你真的想要24小时之后,那就加上24小时,让挂钟时间降到最低点.如果您想要"第二天",意味着用户希望在时钟上看到相同的时间,那么添加1天(可能会在25小时之后).Joda-Time支持两种逻辑.
请注意下面的示例代码,Joda-Time支持计算一天24小时或通过调整时间来匹配相同的挂钟时间,因为夏令时或其他异常.
以下是使用Joda-Time 2.3的一些示例代码.Java 8中的新java.time包应具有与Joda-Time相似的功能.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Chisinau" );
DateTime start = new DateTime( 2012, 10, 27, 6, 0, 0, timeZone );
DateTime stop = start.plusHours( 24 ); // Time will be an hour off because of Daylight Saving Time (DST) anomaly.
DateTime nextDay = start.plusDays( 1 ); // A different behavior (25 hours).
Interval interval = new Interval( start, stop );
Period period = new Period( start, stop );
int hoursBetween = Hours.hoursBetween( start, stop ).getHours();
DateTime startUtc = start.withZone( DateTimeZone.UTC );
DateTime stopUtc = stop.withZone( DateTimeZone.UTC );
Run Code Online (Sandbox Code Playgroud)
转储到控制台......
System.out.println( "start: " + start );
System.out.println( "stop: " + stop );
System.out.println( "nextDay: " + nextDay );
System.out.println( "interval: " + interval );
System.out.println( "period: " + period );
System.out.println( "hoursBetween: " + hoursBetween );
System.out.println( "startUtc: " + startUtc );
System.out.println( "stopUtc: " + stopUtc );
Run Code Online (Sandbox Code Playgroud)
跑的时候......
start: 2012-10-27T06:00:00.000+03:00
stop: 2012-10-28T05:00:00.000+02:00
nextDay: 2012-10-28T06:00:00.000+02:00
interval: 2012-10-27T06:00:00.000+03:00/2012-10-28T05:00:00.000+02:00
period: PT24H
hoursBetween: 24
startUtc: 2012-10-27T03:00:00.000Z
stopUtc: 2012-10-28T03:00:00.000Z
Run Code Online (Sandbox Code Playgroud)
据我了解您的问题,您希望在服务器上设置一个时区,以便在白天时间切换期间测试正确的小时数,而不是使用标准服务器时区.为此,时区需要能够使用夏令时偏移.
您在示例中在服务器上使用的时区
TimeZone zone = TimeZone.getTimeZone("GMT");
Run Code Online (Sandbox Code Playgroud)
不使用日光时间:zone.useDaylightTime()返回false.
请尝试使用特定的时区,例如
TimeZone zone = TimeZone.getTimeZone("Europe/Chisinau");
Run Code Online (Sandbox Code Playgroud)
有夏令时偏移.
您还可以在Joda Time的DateTime类中使用时区,并避免使用Java的Calendar和Date类.