混淆时区变化在俄罗斯
俄罗斯已经通过一些容易混淆的时区变化了,近年来,与莫斯科的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的性能限制导致最初的放缓。 –