2017-06-11 112 views
3

我解析了一些新聞提要和每個項目的pubdate的遵循相同的格式:Android的轉換日期時間解析錯誤(甚至試圖喬達時間)

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

不幸的是一個進料不會:

Sat, 10 Jun 2017 12:49:45 EST

我曾嘗試使用機器人java的日期和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: Unparseable date: "Sat, 3 Jun 2017 19:53:09 EST" (at offset 26)

我也試着做到這一點使用約達時間:

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

會拋出一個錯誤:

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

有沒有人遇到過嗎?

+1

如果你對外部庫很滿意,Joda-Time可能不是最糟糕的選擇,但你是否意識到Joda-Time已經進一步發展爲現代Java數據和時間API? Joda-Time人員正式推薦遷移,並且該API可在[ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP)庫中用於Android。 –

+1

@ OleV.V。你是對的,昨天我沒有時間寫新的日期時間API。無論如何,我已經在下面更新了[我的回答](https://stackoverflow.com/a/44490104/7605325),包括新API的示例。謝謝! –

回答

2

首先,如果你正在開始一個新項目,我建議你使用新的日期時間API而不是joda-time(更多關於下面的內容)。無論如何,這是兩個解決方案。


約達時間

的問題是,該圖案Z是偏移量(以像+0000-0100格式),但是字符串EST是時區短名稱,這是由圖案z(解析請參閱jodatime javadoc瞭解更多詳情)。

所以,你需要一個可選部分的模式,可以同時接收一個或另一個。你可以用org.joda.time.format.DateTimeFormatterBuilder這個類來完成。

首先,你需要創建的org.joda.time.format.DateTimeParser(一個用於Z,以及其他爲z)2個實例,並將其添加爲可選的解析器。然後使用下面的代碼創建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印刷作爲日期/時間與偏移-0400。這是因爲EST內部成爲America/New_York時區,現在在夏令時,它的偏移量是-0400(我可以通過做DateTimeZone.forTimeZone(TimeZone.getTimeZone("EST"))來解決這個問題:問題是:這些3個字母的名稱是ambiguous and not standard,並且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(我知道這是錯的,芝加哥不是EST,這只是一個例子,你如何改變d efaults使用地圖),以及輸出將是:

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

如果得到了與上面的第一個代碼的錯誤,則還可以使用此,映射EST到所需的時區(取決於jodatime的版本和您正在使用的Java,EST可能未映射到默認值並引發異常,因此使用自定義映射可以避免這種情況)。


新的日期時API

至於說在@Ole V.V.'s comment(我沒有時間昨天寫的),喬達時間正在被new Java's Date and Time API,這是遠遠優於compared to the old Date and SimpleDateFormat classes取代。

如果您使用Java> = 8,則java.time軟件包已經是JDK的一部分。對於Java < = 7,有ThreeTen Backport。而對於Android,還有ThreeTenABP(更多關於如何使用它here)。

如果您正在開始一個新項目,請考慮使用新的API而不是joda-time,因爲在joda's website中寫着:請注意,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[America/New_York]
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獲取更多詳細信息)。

+1

非常好的答覆非常感謝你,改變DateTimeZone.forID運作良好。非常詳細的步驟,我將來也會使用它:D:D –