2017-09-13 77 views
4

我使用喬達時間解析幾年是這樣的:如何解析只有4位數字的年份

private DateTime attemptParse(String pattern, String date) { 
     DateTimeFormatter parser = DateTimeFormat.forPattern(pattern).withLocale(Locale.ENGLISH); 
     DateTime parsedDateTime = parser.parseLocalDateTime(date).toDateTime(WET); 
     return parsedDateTime; 
    } 

我試圖解析多種格式:"yyyy-MM-dd", "yyyy-MMM-dd","yyyy MMM dd-dd","yyyy MMM", (etc), "yyyy"。當一個人不工作時,我會嘗試下一個。

當字符串確實只有4位數時(例如:"2016"),它就像一個魅力。問題是我有時收到這樣的東西:"201400"。 Joda-Time與"yyyy"模式匹配,並返回年份爲201400的日期。

我想避免醜陋的檢查year > 9999。有沒有辦法使用Joda-Time來做到這一點?

+0

爲什麼不只是解析'substr(date,0,4)'? – GriffeyDog

+0

@GriffeyDog因爲我不認爲201400是有效的日期,所以我必須拋出異常。 – micdcar

+0

字符串的長度沒有告訴你嗎? –

回答

3

要解析多種格式,您可以創建大量DateTimeParser實例,並將所有實例加入到一個格式化程序中(而不是一個接一個地嘗試)。

這將需要一個DateTimeFormatterBuilder,這也將被用來強制執行一個特定數量的數字輸入(不幸的是,沒有辦法強制執行一個特定數量的數字,就像你只想使用DateTimeFormat.forPattern())。

首先創建大量org.joda.time.format.DateTimeParser實例(每個可能的圖案):

// only yyyy 
DateTimeParser p1 = new DateTimeFormatterBuilder() 
    // year with exactly 4 digits 
    .appendYear(4, 4).toParser(); 
// yyyy-MM-dd 
DateTimeParser p2 = new DateTimeFormatterBuilder() 
    // year with exactly 4 digits 
    .appendYear(4, 4) 
    // rest of the pattern 
    .appendPattern("-MM-dd").toParser(); 
// yyyy MMM 
DateTimeParser p3 = new DateTimeFormatterBuilder() 
    // year with exactly 4 digits 
    .appendYear(4, 4) 
    // rest of the pattern 
    .appendPattern(" MMM").toParser(); 

然後創建所有這些模式陣列,並創建一個DateTimeFormatter它:

// create array with all the possible patterns 
DateTimeParser[] possiblePatterns = new DateTimeParser[] { p1, p2, p3 }; 

DateTimeFormatter parser = new DateTimeFormatterBuilder() 
    // append all the possible patterns 
    .append(null, possiblePatterns) 
    // use the locale you want (in case of month names and other locale sensitive data) 
    .toFormatter().withLocale(Locale.ENGLISH); 

我也使用Locale.ENGLISH(因爲你也在你的問題的代碼中使用它)。此區域表示月份名稱將以英文顯示(因此MMM可以分析像JanSep這樣的值)。有了這個,你可以解析輸入:

System.out.println(parser.parseLocalDateTime("2014")); // OK 
System.out.println(parser.parseLocalDateTime("201400")); // exception 
System.out.println(parser.parseLocalDateTime("2014-10-10")); // OK 
System.out.println(parser.parseLocalDateTime("201400-10-10")); // exception 
System.out.println(parser.parseLocalDateTime("2014 Jul")); // OK 
System.out.println(parser.parseLocalDateTime("201400 Jul")); // exception 

當今年是2014,代碼工作正常。當它的201400,它拋出一個java.lang.IllegalArgumentException,如:

java.lang.IllegalArgumentException異常:無效的格式爲: 「201400」 在 「00」

DateTimeFormatter畸形是不可變的和線程安全的,所以每次調用驗證方法時都不需要創建它。您可以在方法外創建它(例如在static final字段中)。

這比每次執行驗證時創建一個格式化程序並在出現異常時轉到下一個更好。創建的格式化程序已經在內部完成了,直到找到一個可用的模式(或者在所有模式失敗時拋出異常)。


新的Java日期/時間API

喬達時間是在修的模式,正在被新的API所取代,所以我不建議開始使用它的新項目。即使在joda's website它說:「請注意,喬達時間被認爲是一個很大程度上」完成「的項目。計劃沒有重大改進。如果使用Java SE 8,請遷移到java.time(JSR-310)。「

如果您不能(或不想)從Joda-Time遷移到新的API,那麼您如果您使用的Java 可以忽略

這一部分,可以考慮使用new java.time API。它很容易,less bugged and less error-prone than the old APIs

如果您使用的Java 6或7,則可以使用ThreeTen Backport,爲Java 8的新日期/時間類提供了一個很好的支持。而對於Android,你還需要ThreeTenABP(更多關於如何使用它here)。

下面的代碼適用於兩者。 唯一的區別是包名(在Java中8 java.time和ThreeTen反向移植(或Android的ThreeTenABP)爲org.threeten.bp),但類和方法是相同的。

這個新的API是比以往更嚴格,所以格式化只與數字的確切人數(注意,有些課程是非常相似的喬達時間)工作原理:

// 4 digits in year 
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy", Locale.ENGLISH); 
fmt.parse("2014"); // OK 
fmt.parse("201400"); // exception 
fmt.parse("201"); // exception 

此代碼與2014工作,但與201400201(或不正是4位其他值),它拋出一個異常:

java.time.format.DateTimeParseException:文本「201400」不能在索引0

解析

有了這個,您的驗證代碼可以使用字符串數組。


這裏只有一個細節:解析爲日期時,喬達時設置的默認值時,輸入所沒有的一些領域(如一個月變得月份,天變爲1,時/分/秒的設置爲零等)。

如果你只是驗證輸入,那麼你不需要返回任何東西。只要檢查是否拋出異常,你就會知道輸入是否有效。

如果你只需要一年的值,不過,你可以使用Year class

DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy", Locale.ENGLISH); 
System.out.println(Year.parse("2014", parser)); // ok 
System.out.println(Year.parse("201400", parser)); // exception 

如果你想在一年價值爲int

Year year = Year.parse("2014", parser); 
int yearValue = year.getValue(); // 2014 

但是,如果你想要獲取日期對象,您需要手動設置默認值 - 新API非常嚴格,並且不要自動設置這些值。在這種情況下,您必須使用DateTimeFormatterBuilder來設置默認值。

我還它解析爲LocalDateTime,就像例如:

DateTimeFormatter fmt = new DateTimeFormatterBuilder() 
    // string pattern 
    .appendPattern("yyyy") 
    // default month is January 
    .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1) 
    // default day is 1 
    .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) 
    // default hour is zero 
    .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) 
    // default minute is zero 
    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) 
    // set locale 
    .toFormatter(Locale.ENGLISH); 
// create LocalDateTime 
System.out.println(LocalDateTime.parse("2014", fmt)); // 2014-01-01T00:00 
System.out.println(LocalDateTime.parse("201400", fmt)); // exception 

你可以選擇你想要作爲默認的字段的任何值,並且使用任何new available date types的。

+1

這可能是比我的預處理答案更好的主意;-) –

+0

其實我不想更改爲DateTimeFormatterBuilder,因爲我簡化了問題。我試圖解析多種格式:「yyyy-MM-dd」,「yyyy-MMM-dd」,「yyyy MMM dd-dd」,「yyyy MMM」,(等),「yyyy」。 而當無效日期「201400」顯示它被轉換,我想讓Jodatime拋出一個異常。如果我要切換到DateTimeFormatterBuilder,我需要使用它來創建每個日期模式:/ – micdcar

+0

@micdcar在這種情況下,是否可以編輯您的問題,以便我可以相應更新我的答案? – 2017-09-13 14:16:24

0

你在說的是,Jodatime應該以某種方式猜測它應該將201400解析爲2014.我認爲這不在該庫的合理範圍內。你應該自己預處理數據,例如使用:

String normalizedDate = String.format("%4s", date).trim(); 
+0

感謝您的回答。其實我不想轉換這個日期,我想把201400視爲錯誤。 – micdcar

相關問題