2017-04-27 50 views
3

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

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); 
} 

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

+0

不会这项工作'返回新的SimpleDateFormat(DATE_TIME_FORMAT“)格式(dateTimestamp);' –

+0

什么的System.out.println(DateTimeZone.getDefault())的'输出;' – 2017-04-27 12:20:40

+0

? @Hugo“Europe/London” –

回答

3

我做了一个测试用的Java 7和乔达时间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 21 ABR 2017年

要检查什么是错的,我已经ç忌用日期格式:

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

a表示“上午或下午”,Z是时区偏移/ ID,z是时区“短”名称和zzzz是时区“长”的名字。使用这种格式,输出为:

下午6时46分21 ABR 2017年+0100 BST英国夏令时间

所以创建的日期时间是下午6时,只需一个小时提前输入的,而不是11小时如你所想(实际上,如果将格式更改为HH而不是hh,则时间将为18而不是06)。

另请注意,时区字段:+0100 BST British Summer Time。第一部分(+0100)表示此DateTime比格林威治标准时间提前1小时,而BST British Summer Time表示它在British's Daylight Saving Time


因此,有你的输出等于你的投入,你有2种选择:

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

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

输出将是:

05:46 2017年4月21日

如果你想改变的时间是17:46,更改日期格式,取代hh通过HH

2。使用DateTime构造函数接收一个DateTimeZone

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
+1

谢谢你这样一个详细和明确的答案。正如你所说,指定UTC并使用24小时格式可以避免这些问题。 –

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) ; 

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

如果你想通过一个区域的挂钟时间的镜头,看这同一时刻,如Europe/LondonPacific/Auckland,申请时间区域获得ZonedDateTime

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

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

请注意,由于夏令时(DST),每天的时间为1小时。

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

见本code run live at IdeOne.com

关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代了日期时间类legacy,如java.util.Date,Calendar,& SimpleDateFormat

Joda-Time项目现在位于maintenance mode,建议迁移到java.time类。请参阅Oracle Tutorial。并搜索堆栈溢出了很多例子和解释。规格是JSR 310

从何处获取java.time类?

相关问题