Android 时区不正确的问题

Problems with incorrect timezone on Android

现在莫斯科的正确时区是UTC +3。但是 Android 4.4.4 只知道 UTC +4 莫斯科。

java.util.Date给错时间:

Date now = new Date();    
Sat Nov 21 00:37:24 GMT+04:00 2015
1448051844024 in (milliseconds)

使用 Joda org.joda.time

DateTime nowWithCorrect = new DateTime()
                              .withZone(DateTimeZone.forID("Europe/Moscow"));
2015-11-20T23:37:24.023+03:00
1448051844024 in (milliseconds)

但问题是用户(在时区不正确的设备上)手动更正了设备上的时间。 jodajava 以毫秒为单位给出了错误的时间。

我从网络服务时间戳中获取,并且在设备上计算的时间与本地时间不同。但是当地时间不正确,结果不正确。

我想检查 jodajava 时间并手动更正时间戳。

问题:我可以得到不同的时区和不同的时间(nownowWithCorrect)吗? 例如:differentTimeZone = 1differentTimeHours = 1

俄罗斯令人困惑的时区变化

俄罗斯近年来经历了一些令人困惑的时区变化,莫斯科 offset-from-UTC shifting back-and-forth between +03:00 and +04:00. See Wikipedia pages on Time in Russia and Moscow Time

2011 年秋天之前,莫斯科的标准时间是 +03:00Daylight Saving Time (DST)+04:00

从 2011 年秋季开始,俄罗斯决定永久保留夏令时 +04:00,并废除标准时间 +03:00。参见 this RT.com article

2014 年 7 月,该决定发生了翻天覆地的变化。现在俄罗斯永久使用 +03:00 标准时间并废除了 DST(不再 +04:00)。

已过时tz 数据库

所以我认为您的麻烦是由于您的 time zone tz database(以前称为 Olson 数据库)已经过时了。真正的 Java 平台有一个 tz 数据库,它的主机操作系统很可能有一个 tz 数据库。 Joda-Time 有自己的 tz 数据库。我假设 Android 也是如此,尽管我不知道 Android。

显然,使所有这些 tz 数据库保持最新是一项真正的苦差事。

对于 Joda-Time,只需将您的 Joda-Time 库替换为最新的即可。虽然您可以在 Joda-Time 中仅替换 tz 数据库,但据我所知,Joda-Time 2 几乎没有向后兼容性问题,因此没有理由不更新整个库。但请务必阅读发行说明。如果使用 Joda-Time,这是更新的唯一最低要求;我建议同时更新 Android、Java 和主机 OS,但没有必要。

对于真正的 Java 平台,最新版本使更新 tz 数据库变得更加容易。以前的版本需要一些黑客攻击。或者,更新到最新的 Java 8 以获取最新的库。

对于您的主机OS,其定期更新系统可能包含tz 更新。但是,其中一些更新可能会滞后。因此,您可能需要进行手动更新。

在 UTC 工作

日期时间工作的最佳做​​法通常是仅当 expected/desired 由用户或数据接收器完成时,才在 UTC. Business logic, data storage, database, data exchange, and such should all be in UTC. Adjust into a time zone such as Europe/Moscow 中完成所有后端工作。

在 Java 8 及更高版本中,我们将使用内置的 java.time 框架。在 UTC 中暂时使用 Instant。在需要时区时指定 ZoneId 以获得 ZonedDateTime。但是 Java8 技术在 Android 中尚不可用。我相信 Android 有 java.time 的后向端口库,但我不知道细节。

对于 Joda-Time,询问 UTC 中的当前时刻。

DateTime nowUtc = DateTime.now( DateTimeZone.UTC );

如果您不能相信用户的本地 device/computer 时钟准确且设置正确,那么此 UTC 日期时间将是错误的。你对此无能为力。取而代之的是信任外部来源,但这假定有网络连接。

根据需要调整为莫斯科时间。

DateTime nowMoscow = nowUtc.withZone( DateTimeZone.forID( "Europe/Moscow" ) );

如果用户的 Joda-Time tz 数据库已过时,这将 return 一个错误的结果 。没有办法阻止这种情况,因为您无法预测俄罗斯当局将根据他们的计时规则采取什么行动。唯一的解决方案是使用具有更新的 tz 数据库的 Joda-Time 库来更新您的应用程序。或者,再次信任外部来源,例如您的服务器或某些其他 Web 服务,但前提是网络连接。

验证纪元计数

如果您感到困惑并想验证日期时间对象的真实值,请查看 count-from-epoch

Joda-Time 和旧的 java.util.Date/.Calendar classes 从 1970 UTC 的第一时刻开始计数 milliseconds。这些值显示在问题中。看看问题中的两个毫秒值是如何相同的,所以时间轴上的同一时刻但是 Android 的 Date class 对莫斯科时间的错误调整(几乎可以肯定因为一个过时的 tz 数据库)。

请注意 java.time 使用不同的从纪元计数,计算 nanoseconds from same epoch (1970 UTC). In Joda-Time, call getMillis to get the count-from-epoch. In java.util.Date, call getTime

您可以找到 joda-time 时区(基于上次 IANA)和 android 系统时区之间的差异:

long diff = DateTimeZone.getDefault().getOffset(yourTimeStapm) - TimeZone.getDefault().getOffset(yourTimeStapm);

您也可以使用 joda-time 的分支,它使用系统时区: https://gitlab.com/olekdia/common/utils/support-joda-time