2012-10-26 35 views
1

我们在应用程序中使用固定时间段。当用户添加一个新的时间段时,应默认从第二天上午6:00到上午6:00。Joda-Time,夏令时计算,时区独立测试

通常情况下,这是24小时,但有一个问题:执行夏令时更改时,该时段的长度会发生变化。例如:

10月27日6:00 AM至10月28日6:00 AM。在此期间执行从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. 

我们遇到了这个问题,并试图编写一个单元测试以防止它再次出现。测试在我们的机器上成功通过,但在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(); 
是通过在我们的身边,但未能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(); 

我们试图使用的时区日历:

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(); 

但在这种情况下结果startDate 10月27日9:00 CEST和10月28日8:00 CET结束日期,所以即使在我们这边,测试也失败了。

预先感谢您。

+0

什么是'aStartDate'和'aEndDate'?目前还不清楚你试图获得什么结果,或者你希望使用哪个时区(因为你曾经在一个地方谈过CST/CEST,另一个地方是GMT)。你期望它有什么作用:'TimeZone.getTimeZone(TimeZone.getTimeZone(“GMT”).getID())'? –

+0

@JonSkeet关于示例代码 - 我只是试图得到任何结果,所以现在对我来说没什么意义。关于预期结果 - 我们希望确保在有DST转换时 - 根据用户的时区,1“天”的长度为23或25小时。 – StKiller

回答

5

当我明白你的问题,你要设置的服务器上的时区在白天的时间来检验正确的时间切换,而不是使用标准服务器时区。要实现这一点,时区需要能够使用夏令时偏移。

您在服务器上使用您的示例中

TimeZone zone = TimeZone.getTimeZone("GMT"); 

不使用夏令时的时区:zone.useDaylightTime()回报false

请尝试改用特定时区,例如

TimeZone zone = TimeZone.getTimeZone("Europe/Chisinau"); 

有夏令时补偿。

您还可以在Joda Time的DateTime类中使用时区,并避免使用Java的Calendar和Date类。

+0

非常感谢!我们通过测试中欧时间的变化来解决问题,澳大利亚没有时间转移,反之亦然 - 使用特定的时区:) – StKiller

-2

为什么不使用普通的java.util.Calendar?

final Calendar startTime = new GregorianCalendar(2012, Calendar.OCTOBER, 27, 2, 12, 0); 
    startTime.setTimeZone(TimeZone.getTimeZone(strTimeZone)); 
    System.out.println(startTime.getTimeInMillis()); 
+5

因为它比Joda Time多了很多*糟糕的API?解决问题,但坚持乔达时间长远来看会更好。 –

6

指定时区

怎能可能的设计我们单位从机器的时区独立测试?

总是指定时区

几乎总是,你的代码应该指定一个时区。只有在确实需要用户/服务器的默认时区时才会省略时区;即使如此,您应该明确地访问默认时区以使您的代码自行记录。

如果您省略时区,则将使用JVM的默认时区。

乔达时间有各种地方,您可以通过DateTimeZone对象或您致电withZone方法。

避免3-4字母时区代码

避免使用时区代码,如CESTCET。它们既不标准也不唯一。在混淆/忽略夏令时(DST)时,它们经常被误用。

改为使用proper time zone names。大多数名称都是时区的大陆和主要城市的组合,以纯ASCII字符(无变音符号)编写。例如,Europe/Chisinau

Joda-Time,那将是...

DateTimeZone timeZone = DateTimeZone.forID("Europe/Chisinau"); 

想想/使用UTC

你真正的问题是想在当地的日期时间值方面,或者在试图执行与本地时间业务逻辑。这样做非常困难,因为夏令时总体而言是频繁的变化以及其他异常情况。

取而代之,请考虑宇宙的时间线而不是挂钟时间。尽可能将日期时间值存储并处理为UTC(GMT)值。转换为本地划分的时间以便向用户呈现(就像本地化字符串一样),并在需要时用于业务目的,例如确定由特定时区定义的“日”的开始时间。

24小时,相对1日

所以,如果你真的想24小时后,再加入24小时,让挂钟时间秋天它的可能。如果你想“第二天”,这意味着用户期望看到同一时间的时钟,然后加1天(这可能会在25小时后)。 Joda-Time支持这两种逻辑。

请注意以下示例代码,Joda-Time支持通过24小时计算一天或通过调整时间来匹配相同的wall-clock time,因为夏令时或其他异常。

示例代码

以下是使用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); 

转储到控制台...

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); 

当运行...

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