簡體   English   中英

Joda-Time,夏令時計算,時區獨立測試

[英]Joda-Time, daylight savings calculation, timezone independent tests

我們在應用程序中使用固定時間段。 當用戶添加新的時段時,默認情況下應該是第二天的上午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.

我們遇到了這個問題並嘗試編寫單元測試以防止它再次出現。 測試在我們的計算機上成功通過,但在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,endDate是10月28日8:00 CET,所以測試甚至不在我們這邊。

先感謝您。

指定時區

我們如何能夠獨立於機器的時區設計我們的單元測試?

始終指定時區

幾乎總是,您的代碼應指定時區。 僅當您真正需要用戶/服務器的默認時區時才省略時區; 即使這樣,您也應該明確訪問默認時區以使您的代碼自我記錄。

如果省略時區,將使用JVM的默認時區。

Joda-Time有多個地方可以傳遞DateTimeZone對象或者調用withZone方法。

避免使用3-4個字母時區代碼

避免使用CESTCET等時區代碼。 它們既不標准也不獨特。 當混淆/忽略夏令時(DST)時,它們經常被錯誤地使用。

而是使用適當的時區名稱 大多數名稱是時區區域的大陸和主要城市的組合,用純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小時或通過調整時間來匹配相同的掛鍾時間,因為夏令時或其他異常。

示例代碼

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

據我了解您的問題,您希望在服務器上設置一個時區,以便在白天時間切換期間測試正確的小時數,而不是使用標准服務器時區。 為此,時區需要能夠使用夏令時偏移。

您在示例中在服務器上使用的時區

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

不使用日光時間: zone.useDaylightTime()返回false

請嘗試使用特定的時區,例如

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

有夏令時偏移。

您還可以在Joda Time的DateTime類中使用時區,並避免使用Java的Calendar和Date類。

為什么不使用普通的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() );

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM