3

我正在解析许多新闻提要,并且每个项目的 pubDate 都遵循相同的格式:

2017 年 6 月 11 日星期日 18:18:23 +0000

不幸的是,一个提要没有:

2017 年 6 月 10 日星期六 12:49:45 EST

我尝试使用 androids java date 解析日期,但没有成功SimpleDateFormat

try {
    Calendar cal = Calendar.getInstance();
    TimeZone tz = cal.getTimeZone();
    SimpleDateFormat readDate = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
    readDate.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date date = readDate.parse(rssDateTime);
    SimpleDateFormat writeDate = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
    writeDate.setTimeZone(tz);
    parsedDate = writeDate.format(date);
} catch (ParseException e) {
    e.printStackTrace();
}

哪个抛出和错误:

java.text.ParseException:无法解析的日期:“星期六,2017 年 6 月 3 日 19:53:09 EST”(偏移量 26)

我也尝试过使用 joda time 来做到这一点:

DateTime dtUTC = null;
DateTimeZone timezone = DateTimeZone.getDefault();
DateTimeFormatter formatDT = DateTimeFormat.forPattern("EEE, d MMM yyyy HH:mm:ss Z");
DateTime dtRssDateTime = formatDT.parseDateTime(rssDateTime);
DateTime now = new DateTime();
DateTime nowUTC = new LocalDateTime(now).toDateTime(DateTimeZone.UTC);

long instant = now.getMillis();
long instantUTC = nowUTC.getMillis();
long offset = instantUTC - instant;
dtUTC = dtRssDateTime.withZoneRetainFields(timezone);
dtUTC = dtUTC.minusMillis((int) offset);
String returnTimeDate = "";
returnTimeDate = dtUTC.toString(formatDT);

这会引发错误:

原因:java.lang.IllegalArgumentException:无效格式:“星期六,2017 年 6 月 10 日 12:49:45 EST”在“EST”格式错误

有没有人遇到过这个?

4

1 回答 1

2

首先,如果您要开始一个新项目,我建议您使用新的日期时间 API 而不是 joda-time(更多内容见下文)。无论如何,这是两者的解决方案。


乔达时间

问题是模式Z是偏移量(格式如+0000or -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"));
    

这将解析ESTAmerica/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相比要DateSimpleDateFormat优越得多。

如果您使用的 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当然,您可以使用任何您想要的时区或偏移量来代替(使用ZoneIdZoneOffset类,请查看javadoc以获取更多详细信息)。

于 2017-06-12T01:06:22.550 回答