混淆時區變化在俄羅斯
俄羅斯已經通過一些容易混淆的時區變化了,近年來,與莫斯科的offset-from-UTC轉移+03:00
和+04:00
之間的背部和反覆。請參閱Time in Russia和Moscow Time上的Wikipedia頁面。
2011年秋季之前,莫斯科的標準時間爲+03:00
,Daylight Saving Time (DST)爲+04:00
。
從2011年秋季開始,俄羅斯決定永久停留在DST,+04:00
,並取消標準時間+03:00
。請參閱this RT.com article。
2014年7月,該決定發生了巨大變化。現在俄羅斯永久在+03:00
的標準時間,並已廢除夏令時(不再更多+04:00
)。
過時tz
數據庫
所以我假設你的煩惱是由於您的time zone tz database(前身爲奧爾森數據庫)是過時的。真正的Java平臺有一個tz數據庫,它的主機操作系統可能有一個tz數據庫。 Joda-Time擁有自己的tz數據庫。我認爲Android也是如此,儘管我不瞭解Android。
顯然保持所有這些tz數據庫是最新的是一件真正的苦差事。
對於Joda-Time,只需使用最新版本替換您的Joda-Time庫。儘管您可以用Joda-Time替換tz數據庫,但據我記憶,Joda-Time 2幾乎沒有向後兼容性問題,所以沒有理由不更新整個庫。但請仔細閱讀發佈說明。如果使用Joda-Time,這是更新的唯一最低要求;我會建議更新Android,Java和主機操作系統,但不是必需的。
對於真正的Java平臺,最新版本使得更新tz數據庫變得更容易。以前的版本需要一些黑客攻擊。或者,更新到最新的Java 8以獲取最新的庫。
對於您的主機操作系統,其常規更新系統可能包含tz更新。但是,其中一些更新可能會落後。所以你可能需要手動更新。
工作在UTC
日期時間工作最好的做法通常是做所有的後端工作的UTC。業務邏輯,數據存儲,數據庫,數據交換等都應該使用UTC。僅當用戶或數據接收器預期/期望時才調整爲時區,例如Europe/Moscow
。
在Java 8及更高版本中,我們將使用內置的java.time框架。在UTC中使用Instant
片刻。當需要時區時指定ZoneId
以獲得ZonedDateTime
。但Java 8技術尚未在Android上提供。我相信Android的java.time有後端庫,但我不知道細節。
對於Joda-Time,請求UTC中的當前時刻。
DateTime nowUtc = DateTime.now(DateTimeZone.UTC);
如果你不能信任用戶的本地設備/計算機時鐘是準確的設置正確,那麼這UTC日期時間將是錯誤。你無能爲力。取而代之的是信任外部來源,但假設有網絡連接。
根據需要調整爲莫斯科時間。
DateTime nowMoscow = nowUtc.withZone(DateTimeZone.forID("Europe/Moscow"));
如果喬達時間用戶的時區信息數據庫已經過時,這將返回一個錯誤的結果。沒有辦法阻止,因爲你無法預測俄羅斯當局在遵守時間規則方面會做什麼。唯一的解決方案是讓您的應用程序更新爲具有更新後的tz數據庫的Joda-Time庫。或者,再次相信外部來源,例如您的服務器或其他Web服務,但假定網絡連接。
驗證計數,從曆元
如果你感到困惑,並希望確認一個日期時間對象的真實價值,看看計數從 - epoch。
Joda-Time和舊的java.util.Date/.Calendar類都從1970年的UTC開始計數milliseconds。這些值顯示在問題中。看看問題中的兩個毫秒的時間值是相同的,所以時間軸上的時間同樣如此,但是由於Android的Date
類(幾乎肯定是因爲過時的tz數據庫)導致莫斯科時間的調整不正確。
請注意,java.time使用不同的從曆元計數,從同一紀元(1970年UTC)計數nanoseconds。在Joda-Time中,撥打getMillis
即可獲得從時間計數。在java.util.Date中,請致電getTime
。
我只能在外部幫助下解決問題,例如http://tf.nist.gov/tf-cgi/servers.cgi。謝謝,你以正確的方式思考。 – Alexandr
如果從紀元計數是正確的,那麼即使莫斯科的壁鐘時間由於過時的tz數據庫而錯誤,用戶的時鐘也是正確設置的。解決方案是捆綁Joda-Time,保持更新,並避免使用舊的Java.util.Date/.Calendar類。另外,我記得其他人爲Android提供了一個不同版本的Joda-Time,因爲Android的性能限制導致最初的放緩。 –