JodaTime时区为什么会改变日期时间?
当字符串“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设备上运行。
我做了一个测试用的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)
谢谢你这样一个详细和明确的答案。正如你所说,指定UTC并使用24小时格式可以避免这些问题。 –
使用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/London
或Pacific/Auckland
,申请时间区域获得ZonedDateTime
。
在continent/region
的格式指定一个proper time zone name,如America/Montreal
,Africa/Casablanca
,或Pacific/Auckland
。切勿使用3-4字母缩写,例如EST
或IST
或BST
,因为它们是而不是真正的时区,不是标准化的,甚至不是唯一的(!)。
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类?
-
Java SE 8,Java SE 9,后来
- 内置。
- 带捆绑实现的标准Java API的一部分。
- Java 9增加了一些次要功能和修复。在ThreeTen-Backport
-
Java SE 6和Java SE 7
- 大部分的java.time功能后移植到Java 6 & 7。
-
Android
- 的ThreeTenABP项目适应ThreeTen-反向移植(上述)为Android特异性。
- 请参阅How to use ThreeTenABP…。
不会这项工作'返回新的SimpleDateFormat(DATE_TIME_FORMAT“)格式(dateTimestamp);' –
什么的System.out.println(DateTimeZone.getDefault())的'输出;' – 2017-04-27 12:20:40
? @Hugo“Europe/London” –