【问题标题】:Why is JodaTime timezone shifting a date time?为什么 JodaTime 时区会改变日期时间?
【发布时间】:2017-09-25 02:47:16
【问题描述】:

当字符串“2017-04-21T17:46:00Z”被传递到第一个方法时,生成的格式化日期字符串为“06:46 21 Apr 2017”。为什么一小时会移动十一小时?输入字符串由 HTTP 服务器应用程序以 JSON 格式提供。我以为 Z 后缀指的是祖鲁语,即 GMT。

private static final String DATE_TIME_FORMAT = "hh:mm dd MMM yyyy";

public static String formatTimestamp(String dateTimestamp) {
    DateTime dateTime = getDateTimeFromTimestamp(dateTimestamp);
    DateTimeFormatter fmt = DateTimeFormat.forPattern(DATE_TIME_FORMAT);
    return fmt.print(dateTime);
}

private static DateTime getDateTimeFromTimestamp(String dateTimestamp) {
    return new DateTime(dateTimestamp);
}

我怀疑它与时区有关,但不清楚如何或在哪里。该代码在英国的 Android 设备上运行,采用 GMT 时区。

【问题讨论】:

  • 这行不通return new SimpleDateFormat(DATE_TIME_FORMAT").format(dateTimestamp);
  • System.out.println(DateTimeZone.getDefault()); 的输出是什么?
  • @Hugo "欧洲/伦敦"
  • 我要补充一点,我正在使用 Dan Lew 的 Android 版本的 JodaTime,并且在 Application 对象的 onCreate() 中调用 JodaTimeAndroid.init() 专门允许 JodaTime 能够访问时区配置。 github.com/dlew/joda-time-android

标签: android jodatime android-jodatime


【解决方案1】:

我已经使用 java 7 和 joda-time 2.7(但不是 Android 版本)进行了测试

这就是我重现问题的方式:

// changing my default timezone (because I'm not in UK)
DateTimeZone.setDefault(DateTimeZone.forID("Europe/London"));
// calling your method
System.out.println(formatTimestamp("2017-04-21T17:46:00Z"));

输出是

06:46 2017 年 1 月 21 日

为了检查有什么问题,我将日期格式更改为:

DATE_TIME_FORMAT2 = "hh:mm a dd MMM yyyy Z z zzzz";

其中a 表示“上午或下午”,Z 是时区偏移量/id,z 是时区“短”名称,zzzz 是时区“长”名称。使用这种格式,输出是:

2017 年 4 月 21 日下午 06:46 +0100 BST 英国夏令时

所以创建的日期时间是下午 6 点,比输入提前一小时,而不是您想象的 11 小时(实际上,如果您将格式更改为 HH 而不是 hh,小时数将是 18 而不是 @ 987654332@).

还要注意时区字段:+0100 BST British Summer Time。第一部分 (+0100) 表示这个DateTime 比格林威治标准时间早一小时,BST British Summer Time 表示它在British's Daylight Saving Time 中。


所以,要让你的输出等于你的输入,你有两种选择:

1. 将您的默认时区更改为 UTC:

DateTimeZone.setDefault(DateTimeZone.UTC);
System.out.println(formatTimestamp("2017-04-21T17:46:00Z"));

输出将是:

2017 年 4 月 21 日 05:46

如果您想将时间更改为 17:46,请更改日期格式,将 hh 替换为 HH

2.使用接收DateTimeZoneDateTime构造函数:

private static DateTime getDateTimeFromTimestamp(String dateTimestamp) {
    // creates a DateTime in UTC
    return new DateTime(dateTimestamp, DateTimeZone.UTC);
}

输出将与备选方案 1 相同,但在这种情况下,您无需更改默认时区。

对我来说,备选方案 2 更有意义,因为:

  • 您无需更改默认时区(这可能会导致应用程序的其他部分出现混乱)
  • 您已经知道此代码处理的所有日期都是 UTC 时间 (because of the "Z" in the end)

【讨论】:

  • 感谢您提供如此详细而清晰的答案。正如您所说,指定 UTC 并使用 24 小时格式可以避免这些问题。
【解决方案2】:

使用 java.time

Answer by Hugo 似乎是正确且内容丰富的。但仅供参考,Joda-Time 项目现在位于maintenance mode,团队建议迁移到java.time 类。对于 Android,请参阅下方底部的最后一个项目符号。

您的输入字符串是标准的ISO 8601 格式。 java.time 类在解析和生成字符串时使用标准格式。所以不需要指定格式模式。

Instant 类表示UTC 中时间轴上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

String input = "2017-04-21T17:46:00Z" ;
Instant instant = Instant.parse( input ) ;

instant.toString(): 2017-04-21T17:46:00Z

如需更灵活的格式,如您所愿,转换为OffsetDateTime 对象,您可以在小时和分钟内指定任何offset-from-UTC。我们想要 UTC 本身(偏移量为零),所以我们可以使用常量 ZoneOffset.UTC

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;

odt.toString(): 2017-04-21T17:46Z

定义格式模式以匹配您所需的格式。请注意,您必须指定 Locale 来确定 (a) 用于翻译日期名称、月份名称等的人类语言,以及 (b) 决定缩写、大写、标点符号、分隔符和这样的。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "hh:mm dd MMM yyyy" , Locale.US ) ;
String output = odt.format( f ) ;

输出:2017 年 4 月 21 日 05:46

如果您想通过某个地区的挂钟时间(例如Europe/LondonPacific/Auckland)来查看同一时刻,请应用时区以获得ZonedDateTime

continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTISTBST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

请注意,由于夏令时 (DST),一天中的时间是一个小时。

zdt.toString(): 2017-04-21T18:46+01:00[欧洲/伦敦]

看到这个code run live at IdeOne.com

关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

从哪里获得 java.time 类?

【讨论】:

    猜你喜欢
    • 2010-10-28
    • 2012-09-06
    • 2012-03-12
    • 2019-10-20
    • 1970-01-01
    • 2015-05-13
    • 1970-01-01
    • 2018-12-17
    • 2011-05-26
    相关资源
    最近更新 更多