首先,如果您要开始一个新项目,我建议您使用新的日期时间 API 而不是 joda-time(更多内容见下文)。无论如何,这是两者的解决方案。
乔达时间
问题是模式Z
是偏移量(格式如+0000
or -0100
),但字符串EST
是时区短名称,由模式解析z
(有关更多详细信息,请查看jodatime javadoc)。
因此,您需要一个带有可选部分的模式,它可以同时接收一个或另一个。你可以在org.joda.time.format.DateTimeFormatterBuilder
课堂上做到这一点。
首先,您需要创建 2 个实例org.joda.time.format.DateTimeParser
(一个用于Z
,另一个用于z
),并将它们添加为可选解析器。然后org.joda.time.format.DateTimeFormatter
使用下面的代码创建。请注意,我还使用java.util.Locale
了 ,只是为了确保它正确解析工作日和月份名称(因此您不依赖于默认语言环境,该语言环境可能因每个系统/机器而异):
// offset parser (for "+0000")
DateTimeParser offsetParser = new DateTimeFormatterBuilder().appendPattern("Z").toParser();
// timezone name parser (for "EST")
DateTimeParser zoneNameParser = new DateTimeFormatterBuilder().appendPattern("z").toParser();
// formatter for both patterns
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// append common pattern
.appendPattern("EEE, d MMM yyyy HH:mm:ss ")
// optional offset
.appendOptional(offsetParser)
// optional timezone name
.appendOptional(zoneNameParser)
// create formatter (use English Locale to make sure it parses weekdays and month names independent of JVM config)
.toFormatter().withLocale(Locale.ENGLISH)
// make sure the offset "+0000" is parsed
.withOffsetParsed();
// parse the strings
DateTime est = fmt.parseDateTime("Sat, 10 Jun 2017 12:49:45 EST");
DateTime utc = fmt.parseDateTime("Sun, 11 Jun 2017 18:18:23 +0000");
System.out.println(est);
System.out.println(utc);
输出将是:
2017-06-10T12:49:45.000-04:00
2017-06-11T18:18:23.000Z
如果它们与您的预期不完全一样(或者您仍然遇到错误),请参阅下面的注释。
备注:
请注意,它EST
被打印为带有 offset 的日期/时间-0400
。那是因为EST
内部变成America/New_York
了时区,现在处于夏令时并且它的偏移量是-0400
(我可以通过这样做来DateTimeZone.forTimeZone(TimeZone.getTimeZone("EST"))
解决这个问题。问题是:这些 3 字母名称是模棱两可的而不是标准的,而 joda-time 只是假设“默认" 对他们来说。因此,如果您不期待这个时区,并且您不想依赖默认值,您可以使用具有自定义值的地图,如下所示:
// mapping EST to some other timezone (I know it's wrong and Chicago is not EST, it's just an example)
Map<String, DateTimeZone> map = new LinkedHashMap<>();
map.put("EST", DateTimeZone.forID("America/Chicago"));
// parser for my custom map
DateTimeParser customTimeZoneParser = new DateTimeFormatterBuilder().appendTimeZoneShortName(map).toParser();
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// append common pattern
.appendPattern("EEE, d MMM yyyy HH:mm:ss ")
// optional offset
.appendOptional(offsetParser)
// optional custom timezone name
.appendOptional(customTimeZoneParser)
// optional timezone name (accepts all others that are not in the map)
.appendOptional(zoneNameParser)
// create formatter (use English Locale to make sure it parses weekdays and month names independent of JVM config)
.toFormatter().withLocale(Locale.ENGLISH)
// make sure the offset "+0000" is parsed
.withOffsetParsed();
System.out.println(fmt.parseDateTime("Sat, 10 Jun 2017 12:49:45 EST"));
这将解析EST
为America/Chicago
(我知道这是错误的,Chicago 不是EST
,这只是一个示例,说明如何使用地图更改默认值),输出将是:
2017-06-10T12:49:45.000-05:00
如果您在上面的第一个代码中遇到错误,您也可以使用它,映射EST
到所需的时区(取决于您使用的 jodatime 和 Java 的版本,EST
可能不会映射到默认值并引发异常,所以使用自定义地图可以避免这种情况)。
新的日期时间 API
正如@Ole VV 的评论中所说(我昨天没有时间写),joda-time 正在被新的 Java 的 Date and Time API取代,这与旧的和classes相比要Date
SimpleDateFormat
优越得多。
如果您使用的 Java >= 8,则该java.time
包已经是 JDK 的一部分。对于 Java <= 7,有ThreeTen Backport。对于Android,还有ThreeTenABP(更多关于如何使用它的信息)。
如果您正在开始一个新项目,请考虑使用新的 API 而不是 joda-time,因为在joda 的网站上它说:请注意,Joda-Time 被认为是一个基本上“完成”的项目。没有计划进行重大改进。如果使用 Java SE 8,请迁移到 java.time (JSR-310)。
下面的代码适用于两者。唯一的区别是包名(在 Java 8 中是java.time
,在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是org.threeten.bp
),但类和方法名是相同的。
这个想法与 jodatime 非常相似,只是略有不同:
- 您可以使用可选的部分分隔符
[]
- 需要一个带有自定义时区名称的集合(映射
EST
到一些有效的非模棱两可的时区)(因为EST
未映射到任何默认值)
- 使用了一个新类:
ZonedDateTime
,它表示带有时区的日期和时间(因此它涵盖了您的两种情况)
只是提醒这些类在java.time
包中(或org.threeten.bp
取决于您使用的 Java 版本,如上所述):
// set with custom timezone names
Set<ZoneId> set = new HashSet<>();
// when parsing, ambiguous EST uses to New York
set.add(ZoneId.of("America/New_York"));
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// append pattern, with optional offset (delimited by [])
.appendPattern("EEE, d MMM yyyy HH:mm:ss[ Z]")
// append optional timezone name with custom set for EST
.optionalStart().appendLiteral(" ").appendZoneText(TextStyle.SHORT, set).optionalEnd()
// create formatter using English locale to make sure it parses weekdays and month names correctly
.toFormatter(Locale.ENGLISH);
ZonedDateTime est = ZonedDateTime.parse("Sat, 10 Jun 2017 12:49:45 EST", fmt);
ZonedDateTime utc = ZonedDateTime.parse("Sun, 11 Jun 2017 18:18:23 +0000", fmt);
System.out.println(est); // 2017-06-10T12:49:45-04:00[America/New_York]
System.out.println(utc); // 2017-06-11T18:18:23Z
输出将是:
2017-06-10T12:49:45-04:00[美国/纽约]
2017-06-11T18:18:23Z
请注意,在第一种情况下,EST
设置为America/New_York
(由自定义集配置)。使用appendZoneText
自定义集中的值来解决模棱两可的情况。
第二种情况设置为 UTC,因为偏移量是+0000
.
如果要将第一个对象转换为 UTC,则很简单:
System.out.println(est.withZoneSameInstant(ZoneOffset.UTC)); // 2017-06-10T16:49:45Z
输出将是纽约的日期/时间转换为 UTC:
2017-06-10T16:49:45Z
ZoneOffset.UTC
当然,您可以使用任何您想要的时区或偏移量来代替(使用ZoneId
和ZoneOffset
类,请查看javadoc以获取更多详细信息)。