【问题标题】:Wrong timezone offset on Android after JodaTime parsingJodaTime 解析后,Android 上的时区偏移错误
【发布时间】:2017-04-25 18:01:17
【问题描述】:

我尝试通过 JodaTime 库为 android 解析 ISO 格式的日期,并得到错误的时区偏移量。

String dateString = "1990-05-03T20:00:00.000Z";
DateTimeFormatter inputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(DateTimeZone.UTC);
DateTimeFormatter outputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(DateTimeZone.getDefault());
DateTime dt = inputFormatter.parseDateTime(dateString);
String result = outputFormatter.print(dt); 

我的默认时区是“欧洲/莫斯科” 但结果是“1990-05-04T00:00:00.000+0400”,而应该是“1990-05-03T23:00:00.000+0300”。 Android 版本是 KitKat。

我做错了什么?

【问题讨论】:

  • 我想,没有错。 1990-05-03 是夏季时间(5 月),因此 Joda-Time 使用欧洲/莫斯科的夏季时间偏移 +04:00。也许你只是感到困惑,因为实际上,莫斯科在冬天。
  • @MenoHochschild 自 2014 年以来,俄罗斯只有冬季时间,我们不会每年更改时间。如何在 Joda-Time 中禁用夏季/冬季时间?

标签: android datetime timezone jodatime android-jodatime


【解决方案1】:

如果您只想使用实际的莫斯科区域偏移量来显示历史时间戳,那么您可以使用以下代码:

String dateString = "1990-05-03T20:00:00.000Z";
DateTimeFormatter inputFormatter =
    DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(DateTimeZone.UTC);
DateTime dt = inputFormatter.parseDateTime(dateString);
System.out.println(dt); // 1990-05-03T20:00:00.000Z

// only use actual offset for Moscow
DateTimeZone zoneOffset =
    DateTimeZone.forOffsetMillis(
        DateTimeZone.forID("Europe/Moscow").getOffset(DateTime.now())
    );
DateTimeFormatter outputFormatter =
    DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(zoneOffset);
String result = outputFormatter.print(dt);
System.out.println(result); // 1990-05-03T23:00:00.000+0300

仍然是同一个瞬间,只是您要查找的表单的偏移量和本地时间部分不同。

关于如何从服务器解析 UTC 时间的评论:

String input = "2016-12-10T13:40:38.595";
DateTime utc =
    DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS").parseLocalDateTime(input)
    .toDateTime(DateTimeZone.UTC);
System.out.println(utc); // 2016-12-10T13:40:38.595Z

如果设备数据错误,如何将当前设备时间以 UTC 格式发送到服务器?

首先要说:我仍然认为不需要将当前客户端时间戳发送到服务器是更好的设计,因为服务器在接收客户端消息时应该使用自己的时钟来注册时间戳。这有效地防止了假客户端发送任何无意义的时间。

否则,如果 tz-data 和设备时钟(通过System.currentTimeMillis())都错误,但本地设备时间戳仍然正确,如您的 cmets 所示(年-月-日-小时-分钟的有效字段值) 那么您可以应用以下解决方法:

// incorrect platform data from device clock or outdated tz-data
long platformMillis = System.currentTimeMillis();
int offsetMillis = TimeZone.getDefault().getOffset(platformMillis);
DateTimeZone tzPlatform = DateTimeZone.forOffsetMillis(offsetMillis); // +04 on your device

// we assume correct local time if the errors of platform data compensate each other
LocalDateTime ldt = new LocalDateTime(new Date(platformMillis), tzPlatform);

// now we combine local timestamp with the tz-data of Joda-Time (hopefully up-to-date)
long utcMillis = ldt.toDateTime(DateTimeZone.getDefault()).getMillis();

附注:我的库 Time4A 支持更短的解决方案。

Moment now = SystemClock.inPlatformView().now().inStdTimezone();
long utcMillis = TemporalType.MILLIS_SINCE_UNIX.from(now);

【讨论】:

  • 它的工作。但是,如果我将此类代码用于所有时区怎么办? DateTimeZone zoneOffset = DateTimeZone.forOffsetMillis(DateTimeZone.getDefault().getOffset(DateTime.now())); DateTimeFormatter outputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(zoneOffset);
  • @preceptron 为什么不在其他时区工作?您可以显示具有任意偏移量的任何瞬间,保持瞬间但更改本地时间戳。但是,具有历史偏移量的历史形式对我来说似乎更自然,而不是您要求仅使用与您要显示的时间戳无关的实际偏移量。
  • @preceptron 也许这更清楚:假设您不在莫斯科,而是在巴西,因此您可以显示带有圣保罗区域偏移量的 SAME 瞬间,从而产生巴西的本地时间戳。本地时间戳部分和偏移量发生了变化,但瞬间还是一样的。您要使用哪个偏移量进行显示完全取决于您。如果您只是指示 Joda-Time 使用莫斯科区域(没有说明确的偏移量),那么 Joda-Time 将使用由上下文历史时刻(将被打印)给出的偏移量。
  • 我们在服务器上保持 UTC 时间。在 iOS 和新版本的 Android 上,时区具有正确的偏移量(莫斯科为 +3),但 KitKat 的莫斯科偏移量为 +4(这是错误的)。所以,我应该通过边库来修复它。我还有一个问题。我尝试获取 UTC 字符串,但如果我得到当前时间,我会得到 -4 小时的字符串……但我需要 -3……例如现在 10.12.2016 16:40,我得到像这样的 UTC 字符串“2016 -12-10T13:40:38.595" 我尝试了不同的方法,但它不起作用。你能帮我处理这种情况吗?
  • @preceptron 查看我的答案的更新(底部)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-25
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
  • 2011-12-18
  • 1970-01-01
  • 2015-09-03
相关资源
最近更新 更多