Android转换日期时间解析错误(甚至尝试了joda时间) [英] Android converting date time parse error (even tried joda time)

查看:169
本文介绍了Android转换日期时间解析错误(甚至尝试了joda时间)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在解析许多新闻源,每个项目的pubDate遵循相同的格式:

I'm parsing a number of news feeds and each item's pubDate follows the same format:


Sun,2017年6月11日18: 18:23 +0000

Sun, 11 Jun 2017 18:18:23 +0000

不幸的是,一个Feed没有:

Unfortunately one feed does not:


周六,2017年6月10日12:49:45 EST

Sat, 10 Jun 2017 12:49:45 EST

我试图解析日期没有运气使用androids java date和 SimpleDateFormat

I have tried to parse the date with no luck using androids java date and 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)

java.text.ParseException: Unparseable date: "Sat, 3 Jun 2017 19:53:09 EST" (at offset 26)

我也尝试使用joda时间:

I've also tried to do this using 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

Caused by: java.lang.IllegalArgumentException: Invalid format: "Sat, 10 Jun 2017 12:49:45 EST" is malformed at " EST"

有没有人遇到过这个?

推荐答案

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

First of all, if you're starting a new project, I suggest you to use the new date-time API instead of joda-time (more on that below). Anyway, here's a solution for both.

问题是模式 Z 是偏移量(格式如 +0000 -0100 ),但字符串 EST 是时区短名称,由模式 z (请参阅 jodatime javadoc 了解更多详情。)

The problem is that the pattern Z is the offset (in formats like +0000 or -0100), but the string EST is the timezone short name, which is parsed by the pattern z (take a look at jodatime javadoc for more details).

因此,您需要一个带有可选部分的模式,可以同时接收一个或另一个。你可以用 org.joda.time.format.DateTimeFormatterBuilder 类来做到这一点。

So, you need a pattern with optional sections, that can receive one or another at the same time. You can do that with the org.joda.time.format.DateTimeFormatterBuilder class.

首先你需要创建2个 org.joda.time.format.DateTimeParser 的实例(一个用于 Z ,另一个用于 z ),并将它们添加为可选的解析器。然后使用下面的代码创建 org.joda.time.format.DateTimeFormatter 。请注意,我还使用了 java.util.Locale ,只是为了确保它正确解析工作日和月份名称(因此您不依赖于默认语言环境,这可能会有所不同)在每个系统/机器上):

First you need to create 2 instances of org.joda.time.format.DateTimeParser (one for Z, and other for z), and add them as optional parsers. Then you create the org.joda.time.format.DateTimeFormatter using the code below. Note that I also used java.util.Locale, just to make sure it parses the weekday and month names correctly (so you don't depend upon default locale, which can vary on each system/machine):

// 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

2017-06-10T12:49:45.000-04:00
2017-06-11T18:18:23.000Z

如果它们不像您期望的那样(或者您仍然遇到错误),请参阅下面的注释。

If they're not exactly like you were expecting (or you're still getting errors), please see the notes below.

注释


  • 请注意 EST 打印为日期/时间,偏移量 -0400 。这是因为 EST 内部成为 America / New_York 时区,现在是夏令时,其偏移量 -0400 (我可以通过 DateTimeZone.forTimeZone(TimeZone.getTimeZone(EST))来解决这个问题。问题是:这些3个字母的名称是含糊不清且不标准,joda-time只是假定它们是默认。所以,如果你不期望这个时区,并且你不想依赖默认值,你可以使用带有自定义值的地图,如下所示:

  • Note that EST was printed as a date/time with offset -0400. That's because EST internally became America/New_York timezone, which is now in Daylight Saving Time and its offset is -0400 (I could figure this out by doing DateTimeZone.forTimeZone(TimeZone.getTimeZone("EST")). The problem is: these 3-letter names are ambiguous and not standard, and joda-time just assumes a "default" for them. So, if you were not expecting this timezone, and you don't want to rely on defaults, you can use a map with custom values, like this:

// 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 as America / Chicago (我知道这是错的,芝加哥不是 EST ,它是只是一个如何使用地图更改默认值的示例),输出将是:

This will parse EST as America/Chicago (I know it's wrong and Chicago is not EST, it's just an example of how you can change the defaults using a map), and the output will be:


2017-06-10T12:49 :45.000-05:00

2017-06-10T12:49:45.000-05:00

如果上面第一个代码出错,你也可以使用它,映射 EST 到期望的时区(取决于您使用的jodatime和Java的版本, EST 可能未映射到默认值值并抛出异常,因此使用自定义映射可以避免这种情况。)

If you got an error with the first code above, you can also use this, mapping EST to the desired timezone (depending on the version of jodatime and Java you're using, EST might not be mapped to a default value and throws an exception, so using a custom map avoids this).

@ Ole VV的评论(我昨天没有时间写),joda-time正在进行中由新Java的日期和时间API 替代,这是非常优越的与旧的日期和<$相比c $ c> SimpleDateFormat

As told in @Ole V.V.'s comment (and I didn't have time to write yesterday), joda-time is being replaced by the new Java's Date and Time API, which is far superior compared to the old Date and SimpleDateFormat classes.

如果您使用Java> = 8, java.time 包已经是JDK的一部分。对于Java< = 7,有 ThreeTen Backport 。对于 Android ,有 ThreeTenABP (更多关于如何使用它< a href =https://stackoverflow.com/a/38922755/7605325>这里)。

If you're using Java >= 8, the java.time package is already part of the JDK. For Java <= 7 there's the ThreeTen Backport. And for Android, there's the ThreeTenABP (more on how to use it here).

如果你要开始一个新项目,请考虑新的API而不是joda-time,因为在 joda的网站中它说:请注意,Joda-Time被认为是一个很大程度上完成的项目。没有计划重大改进。如果使用Java SE 8,请迁移到java.time(JSR-310)

If you're starting a new project, please consider the new API instead of joda-time, because in joda's website it says: Note that Joda-Time is considered to be a largely "finished" project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).

以下代码适用于两者。唯一的区别是包名称(在Java 8中是 java.time ,在ThreeTen Backport(或Android的ThreeTenABP)中是 org.threeten.bp ),但类和方法名称是相同的。

The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.

这个想法与jodatime非常相似差异:

The idea is very similar to jodatime, with minor differences:


  • 您可以使用可选部分分隔符 []

  • 需要一个带有自定义时区名称的集合(将 EST 映射到某个有效的非模糊时区)(如 EST 未映射到任何默认值)

  • 使用新类: ZonedDateTime ,表示日期和带时区的时间(所以它涵盖了你的两种情况)

  • you can use the optional section delimiters []
  • a set with custom timezone names (to map EST to some valid non-ambiguous timezone) is required (as EST is not mapped to any default)
  • a new class is used: ZonedDateTime, which represents a date and time with a timezone (so it covers both of your cases)

只需提醒这些类在 java中.time 包(或在 org.threeten.bp ,具体取决于您使用的Java版本,如上所述):

Just reminding that these classes are in java.time package (or in org.threeten.bp depending on what Java version you're using, as explained above):

// 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 [America / New_York]

2017-06-11T18:18: 23Z

2017-06-10T12:49:45-04:00[America/New_York]
2017-06-11T18:18:23Z

请注意,在第一种情况下, EST 设置为 America / New_York (由自定义集配置)。 appendZoneText 可以解决问题,使用自定义集中的值来解决不明确的情况。

Note that in the first case, EST was set to America/New_York (as configured by the custom set). The appendZoneText does the trick, using the values in the custom set to resolve ambiguous cases.

第二种情况设置为UTC,因为偏移量为 +0000

And the second case was set to UTC, as the offset is +0000.

如果要将第一个对象转换为UTC ,它是直截了当的:

If you want to convert the first object to UTC, it's straighforward:

System.out.println(est.withZoneSameInstant(ZoneOffset.UTC)); // 2017-06-10T16:49:45Z

输出将是纽约的日期/时间兑换UTC:

The output will be the New York's date/time converted to UTC:


2017-06-10T16:49:45Z

2017-06-10T16:49:45Z

当然你可以使用你想要的任何时区或偏移量来代替 ZoneOffset.UTC (使用 ZoneId ZoneOffset 类,检查 javadoc 了解更多详情。)

Instead of ZoneOffset.UTC, of course you can use any timezone or offset you want (using ZoneId and ZoneOffset classes, check the javadoc for more details).

这篇关于Android转换日期时间解析错误(甚至尝试了joda时间)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆