2011-02-28 92 views
4

我遇到和問題喬達,我認爲這可能是一個錯誤。不過,我很可能使用該庫時發生錯誤,請給我反饋。喬達時間轉換與非DST時區不正確

我們在生產中使用joda將我們的數據庫存儲格式(UTC)與用戶的時區首選項之間的時間轉換。使用該系統的員工都來自亞利桑那州,因爲他們沒有遵守夏令時。除此之外,這個系統跟蹤全國體育賽事的開始時間。

喬達對我們很好,直到我們注意到在timechange前一天我們得到了一些不正確的結果。我們發現喬達在UTC的午夜似乎正在改變爲夏令時,而不是在特定時區的適當時間。此外,僅在將DST觀察狀態轉換爲非亞利桑那州等非DST狀態時,纔會出現此問題。

我製作了一個完整的測試案例來說明這個問題。正如您將看到的,joda提供了所有美國/東部 - >美國/太平洋地區測試用例的預期結果。對於美國/亞利桑那州 - >美國/太平洋地區,它在11月份之前和之後的整個一年都有效。然而,在時間改變的那天(第6日),時間不正確。在遊行時間變化周圍也可能存在一個問題,儘管我還沒有對它進行徹底的測試。

下面是提供的測試輸出(爲11月6日的條目顯示錯誤):

=== November 1st, Expected Result (0 hour) === 
java: 
Converting 2010-11-01 09:00 from US/Arizona to US/Pacific. 
Result: 2010-11-01 09:00. Change (0 hour). 

joda: 
Converting 2010-11-01 09:00 from US/Arizona to US/Pacific. 
Result: 2010-11-01 09:00. Change (0 hour). 

======================================= 

=== November 6th, Expected Result (0 hour) === 
java: 
Converting 2010-11-06 09:00 from US/Arizona to US/Pacific. 
Result: 2010-11-06 09:00. Change (0 hour). 

joda: 
Converting 2010-11-06 09:00 from US/Arizona to US/Pacific. 
Result: 2010-11-06 08:00. Change (-1 hour). 

======================================= 

=== November 12th, Expected Result (-1 hour) === 
java: 
Converting 2010-11-12 09:00 from US/Arizona to US/Pacific. 
Result: 2010-11-12 08:00. Change (-1 hour). 

joda: 
Converting 2010-11-12 09:00 from US/Arizona to US/Pacific. 
Result: 2010-11-12 08:00. Change (-1 hour). 

======================================= 

=== March 12th, Expected Result (-1 hour) === 
java: 
Converting 2010-03-12 09:00 from US/Arizona to US/Pacific. 
Result: 2010-03-12 08:00. Change (-1 hour). 

joda: 
Converting 2010-03-12 09:00 from US/Arizona to US/Pacific. 
Result: 2010-03-12 08:00. Change (-1 hour). 

======================================= 

=== March 14th, Expected Result (0 hour) === 
java: 
Converting 2010-03-14 09:00 from US/Arizona to US/Pacific. 
Result: 2010-03-14 09:00. Change (0 hour). 

joda: 
Converting 2010-03-14 09:00 from US/Arizona to US/Pacific. 
Result: 2010-03-14 09:00. Change (0 hour). 

======================================= 

=== November 1st, Expected Result (-3 hour) === 
java: 
Converting 2010-11-01 09:00 from US/Eastern to US/Pacific. 
Result: 2010-11-01 06:00. Change (-3 hour). 

joda: 
Converting 2010-11-01 09:00 from US/Eastern to US/Pacific. 
Result: 2010-11-01 06:00. Change (-3 hour). 

======================================= 

=== November 6th, Expected Result (-3 hour) === 
java: 
Converting 2010-11-06 09:00 from US/Eastern to US/Pacific. 
Result: 2010-11-06 06:00. Change (-3 hour). 

joda: 
Converting 2010-11-06 09:00 from US/Eastern to US/Pacific. 
Result: 2010-11-06 06:00. Change (-3 hour). 

======================================= 

=== November 12th, Expected Result (-3 hour) === 
java: 
Converting 2010-11-12 09:00 from US/Eastern to US/Pacific. 
Result: 2010-11-12 06:00. Change (-3 hour). 

joda: 
Converting 2010-11-12 09:00 from US/Eastern to US/Pacific. 
Result: 2010-11-12 06:00. Change (-3 hour). 

======================================= 

=== March 12th, Expected Result (-3 hour) === 
java: 
Converting 2010-03-12 09:00 from US/Eastern to US/Pacific. 
Result: 2010-03-12 06:00. Change (-3 hour). 

joda: 
Converting 2010-03-12 09:00 from US/Eastern to US/Pacific. 
Result: 2010-03-12 06:00. Change (-3 hour). 

======================================= 

=== March 14th, Expected Result (-3 hour) === 
java: 
Converting 2010-03-14 09:00 from US/Eastern to US/Pacific. 
Result: 2010-03-14 06:00. Change (-3 hour). 

joda: 
Converting 2010-03-14 09:00 from US/Eastern to US/Pacific. 
Result: 2010-03-14 06:00. Change (-3 hour). 

======================================= 

這裏是完整的測試案例:

package com.test.time; 

import java.text.SimpleDateFormat; 
import java.util.Calendar; 
import java.util.Date; 
import java.util.TimeZone; 

import org.joda.time.DateTimeZone; 
import org.junit.Before; 
import org.junit.Test; 

public class TimeTest { 
    Calendar nov6; 
    Calendar nov1; 
    Calendar nov12; 

    Calendar mar12; 
    Calendar mar14; 

    @Before 
    public void doBefore() { 
     // November 1st 2010, 9:00pm (DST is active) 
     nov1 = Calendar.getInstance(); 
     nov1.setTimeZone(TimeZone.getTimeZone("US/Arizona")); 
     nov1.set(Calendar.HOUR_OF_DAY, 21); 
     nov1.set(Calendar.MINUTE, 0); 
     nov1.set(Calendar.SECOND, 0); 
     nov1.set(Calendar.YEAR, 2010); 
     nov1.set(Calendar.MONTH, 10); // November 
     nov1.set(Calendar.DATE, 1); 

     // November 6st 2010, 9:00pm (DST is still active until early AM 
     // november 7th) 
     nov6 = Calendar.getInstance(); 
     nov6.setTimeZone(TimeZone.getTimeZone("US/Arizona")); 
     nov6.set(Calendar.HOUR_OF_DAY, 21); 
     nov6.set(Calendar.MINUTE, 0); 
     nov6.set(Calendar.SECOND, 0); 
     nov6.set(Calendar.YEAR, 2010); 
     nov6.set(Calendar.MONTH, 10); // November 
     nov6.set(Calendar.DATE, 6); 

     // November 12th 2010, 9:00pm (DST has ended) 
     nov12 = Calendar.getInstance(); 
     nov12.setTimeZone(TimeZone.getTimeZone("US/Arizona")); 
     nov12.set(Calendar.HOUR_OF_DAY, 21); 
     nov12.set(Calendar.MINUTE, 0); 
     nov12.set(Calendar.SECOND, 0); 
     nov12.set(Calendar.YEAR, 2010); 
     nov12.set(Calendar.MONTH, 10); // November 
     nov12.set(Calendar.DATE, 12); 

     // March 12th 2011, 9:00pm (DST has ended, will begin early a.m. march 
     // 13th) 
     mar12 = Calendar.getInstance(); 
     mar12.setTimeZone(TimeZone.getTimeZone("US/Arizona")); 
     mar12.set(Calendar.HOUR_OF_DAY, 21); 
     mar12.set(Calendar.MINUTE, 0); 
     mar12.set(Calendar.SECOND, 0); 
     mar12.set(Calendar.YEAR, 2010); 
     mar12.set(Calendar.MONTH, 2); // March 
     mar12.set(Calendar.DATE, 12); 

     // March 14th 2011, 9:00pm (DST has started) 
     mar14 = Calendar.getInstance(); 
     mar14.setTimeZone(TimeZone.getTimeZone("US/Arizona")); 
     mar14.set(Calendar.HOUR_OF_DAY, 21); 
     mar14.set(Calendar.MINUTE, 0); 
     mar14.set(Calendar.SECOND, 0); 
     mar14.set(Calendar.YEAR, 2010); 
     mar14.set(Calendar.MONTH, 2); // March 
     mar14.set(Calendar.DATE, 14); 
    } 

    @Test 
    public void testArizonaToPacific() { 
     System.out.println("=== November 1st, Expected Result (0 hour) ==="); 
     timeTestJava(nov1.getTime(), "US/Arizona", "US/Pacific"); 
     timeTestJoda(nov1.getTime(), "US/Arizona", "US/Pacific"); 
     System.out.println("=======================================\n"); 

     System.out.println("=== November 6th, Expected Result (0 hour) ==="); 
     timeTestJava(nov6.getTime(), "US/Arizona", "US/Pacific"); 
     timeTestJoda(nov6.getTime(), "US/Arizona", "US/Pacific"); 
     System.out.println("=======================================\n"); 

     System.out.println("=== November 12th, Expected Result (-1 hour) ==="); 
     timeTestJava(nov12.getTime(), "US/Arizona", "US/Pacific"); 
     timeTestJoda(nov12.getTime(), "US/Arizona", "US/Pacific"); 
     System.out.println("=======================================\n"); 

     System.out.println("=== March 12th, Expected Result (-1 hour) ==="); 
     timeTestJava(mar12.getTime(), "US/Arizona", "US/Pacific"); 
     timeTestJoda(mar12.getTime(), "US/Arizona", "US/Pacific"); 
     System.out.println("=======================================\n"); 

     System.out.println("=== March 14th, Expected Result (0 hour) ==="); 
     timeTestJava(mar14.getTime(), "US/Arizona", "US/Pacific"); 
     timeTestJoda(mar14.getTime(), "US/Arizona", "US/Pacific"); 
     System.out.println("=======================================\n"); 
    } 

    @Test 
    public void testEasternToPacific() { 
     System.out.println("=== November 1st, Expected Result (-3 hour) ==="); 
     timeTestJava(nov1.getTime(), "US/Eastern", "US/Pacific"); 
     timeTestJoda(nov1.getTime(), "US/Eastern", "US/Pacific"); 
     System.out.println("=======================================\n"); 

     System.out.println("=== November 6th, Expected Result (-3 hour) ==="); 
     timeTestJava(nov6.getTime(), "US/Eastern", "US/Pacific"); 
     timeTestJoda(nov6.getTime(), "US/Eastern", "US/Pacific"); 
     System.out.println("=======================================\n"); 

     System.out.println("=== November 12th, Expected Result (-3 hour) ==="); 
     timeTestJava(nov12.getTime(), "US/Eastern", "US/Pacific"); 
     timeTestJoda(nov12.getTime(), "US/Eastern", "US/Pacific"); 
     System.out.println("=======================================\n"); 

     System.out.println("=== March 12th, Expected Result (-3 hour) ==="); 
     timeTestJava(mar12.getTime(), "US/Eastern", "US/Pacific"); 
     timeTestJoda(mar12.getTime(), "US/Eastern", "US/Pacific"); 
     System.out.println("=======================================\n"); 

     System.out.println("=== March 14th, Expected Result (-3 hour) ==="); 
     timeTestJava(mar14.getTime(), "US/Eastern", "US/Pacific"); 
     timeTestJoda(mar14.getTime(), "US/Eastern", "US/Pacific"); 
     System.out.println("=======================================\n"); 
    } 

    // print some output from the test 
    private void print(Date startTime, String text, String from, String to, 
      Date output) { 
     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm"); 

     System.out.println(text + ":"); 
     System.out.println("Converting " + sdf.format(startTime) + " from " 
       + from + " to " + to + "."); 
     long difference = output.getTime() - startTime.getTime(); 
     System.out.println("Result: " + sdf.format(output) + ". Change (" 
       + difference/1000/60/60 + " hour).\n"); 
    } 

    // wrapper around joda test 
    private void timeTestJoda(Date startTime, String from, String to) { 
     Date output = convertJodaOld(startTime, TimeZone.getTimeZone(from), 
       TimeZone.getTimeZone(to)); 
     print(startTime, "joda", from, to, output); 
    } 

    // wrapper around java test 
    private void timeTestJava(Date startTime, String from, String to) { 
     Date output = convertJava(startTime, TimeZone.getTimeZone(from), 
       TimeZone.getTimeZone(to)); 
     print(startTime, "java", from, to, output); 
    } 

    // Joda implementation, works before and after DST change, but not during 
    // the period from 2am-7am UTC on the day of the change 
    public Date convertJodaOld(Date dt, TimeZone from, TimeZone to) { 
     DateTimeZone tzFrom = DateTimeZone.forTimeZone(from); 
     DateTimeZone tzTo = DateTimeZone.forTimeZone(to); 

     Date utc = new Date(tzFrom.convertLocalToUTC(dt.getTime(), false)); 
     Date convertedTime = new Date(tzTo.convertUTCToLocal(utc.getTime())); 
     return convertedTime; 
    } 

    // Java implementation. Works. 
    public Date convertJava(Date dt, TimeZone from, TimeZone to) { 
     long fromOffset = from.getOffset(dt.getTime()); 
     long toOffset = to.getOffset(dt.getTime()); 

     long convertedTime = dt.getTime() - (fromOffset - toOffset); 
     return new Date(convertedTime); 
    } 
} 

謝謝!

回答

4

不要使用'US/Arizona',它已經過時了。

使用 '美國/鳳凰'

同去的 '美國/太平洋' 中,使用 '美國/洛杉磯' 來代替。

+0

很高興知道,但是使用您建議的時區不會影響測試結果。你有鏈接到美國/ *時區被淘汰的信息嗎? – samspot 2011-02-28 22:23:56

+1

@samspot。這裏是'官方'列表 - > http://en.wikipedia.org/wiki/List_of_tz_database_time_zones。這是一些非官方的信息 - > http://www.timezoneconverter.com/cgi-bin/zoneinfo.tzc?s=default&tz=US/Pacific – 2011-03-01 12:00:46

+0

@samspot。在我看來,你實際上沒有使用喬達時間。構建DateTime對象並看到您仍然有問題。我一直在我的代碼中使用DateTime,並且嚴格測試標準/日光變化,因爲它們對我們的系統至關重要。喬達時間班總是返回正確的結果。 – 2011-03-01 12:08:08