为什么JodaTime时区会改变日期时间?

时间:2017-04-27 10:45:19

标签: android jodatime android-jodatime

当字符串“2017-04-21T17:46:00Z”传递给第一种方法时,生成的格式化日期字符串为“2017年4月21日06:46”。为什么小时移动了11个小时?输入字符串由JSON中的HTTP服务器应用程序提供。我认为Z后缀是指Zulu,即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时区运行。

2 个答案:

答案 0 :(得分:3)

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

这就是我如何重现这个问题:

// 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 21 Abr 2017

要查看错误,我已将日期格式更改为:

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

a表示" AM或PM",Z是时区偏移量/ ID,z是时区"短"名称和zzzz是时区" long"名称。使用此格式,输出为:

  

06:46 PM 21 Abr 2017 +0100 BST英国夏令时

因此创建的日期时间是下午6点,比输入提前一小时,而不是您想的十一小时(实际上,如果您将格式更改为HH而不是hh,则小时数将为{{ 1}}而不是18)。

另请注意时区字段:06。第一部分(+0100 BST British Summer Time)表示此+0100比GMT提前一小时,而DateTime表示它位于British's Daylight Saving Time

因此,要使您的输出等于您的输入,您有两种选择:

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

BST British Summer Time

输出将是:

  

2017年4月21日2017年4月

如果您想将小时数更改为DateTimeZone.setDefault(DateTimeZone.UTC); System.out.println(formatTimestamp("2017-04-21T17:46:00Z")); ,请更改日期格式,将17:46替换为hh

2。使用收到HH的{​​{1}}构造函数:

DateTime

输出与替代1相同,但在这种情况下,您不需要更改默认时区。

对我来说,替代2更有意义,因为:

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

答案 1 :(得分:1)

使用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。切勿使用诸如ESTISTBST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的( !)。

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.DateCalendar和& SimpleDateFormat

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

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?