2017-08-16 109 views
2

我無法解析此日期。任何人都注意到任何錯他們似乎都失敗了。Java SimpleDateFormat無法解析「MMM dd,yyyy,h:mm a z」「Aug 15,2017,4:58 PM ET」

我已經嘗試了多個Locale類型的多個模式。

這裏是我的策略:

import java.text.DateFormat; 
import java.text.ParseException; 
import java.text.SimpleDateFormat; 
import java.util.Arrays; 
import java.util.Locale; 

public class Test { 

static void check(Locale locale){ 

    String dateString = "Aug 15, 2017, 4:58 PM ET"; 

    DateFormat format1 = new SimpleDateFormat("MMM dd, yyyy, h:mm aa zz", locale); 
    DateFormat format2 = new SimpleDateFormat("MMM dd, yyyy, h:mm a z", locale); 
    DateFormat format3 = new SimpleDateFormat("MMM dd, yyyy, hh:mm a z", locale); 
    DateFormat format4 = new SimpleDateFormat("MMM dd, yyyy, K:mm a z", locale); 
    DateFormat format5 = new SimpleDateFormat("MMM dd, yyyy, KK:mm a z", locale); 

    for (DateFormat format : Arrays.asList(format1, format2, format3, format4, format5)) { 

     try { 
      System.out.println(format.parse(dateString)); 
     } catch (ParseException ex){ 
      System.out.println("Failed"); 
     } 
    } 

} 

public static void main(String[] args) { 

    Arrays.asList(Locale.ENGLISH, Locale.UK, Locale.US, Locale.CANADA, Locale.ROOT, Locale.getDefault()).forEach(Test::check); 
    } 
} 
+3

'ET'不是時區。 「EDT」是東部夏令時,「EST」是東部標準時間。 –

+0

是你的策略,因爲你不知道字符串是怎麼樣的?或者因爲你試圖找到哪一個工作? –

+0

看來你應該用'EDT'來代替'ET'。 –

回答

2

正如很多人已經說過的,ET不是一個時區。它是一個常用的縮寫,指的是both EST and EDT東部標準時間東部夏令時間),但有more than one timezone that uses it

短名稱(如ESTEDT)也不是時區,因爲這樣的縮寫是ambiguous and not standard。有more than one timezone that can use the same abbreviations

理想的是使用IANA timezones names(總是格式爲Region/City,如America/Sao_PauloEurope/Berlin)。 但是使用諸如ESTET之類的短名稱是普遍和常見的,所以我們必須忍受它(並且還要做一些解決方法)。

的第一件事是到定義要爲ET使用哪個時區(這將是一個非常隨意的選擇,但也沒有別的辦法,因爲ET是模糊的)。在下面的示例中,我選擇了America/New_York。您可以使用java.util.TimeZone類(稱爲TimeZone.getAvailableIDs())查看所有可用時區的列表(並選擇最適合您需要的時區)。

使用java.text.DateFormatSymbols類可以覆蓋SimpleDateFormat使用的短名稱。所以,一個解決辦法是,當前的符號和覆蓋只是我們想要的時區:

SimpleDateFormat sdf = new SimpleDateFormat("MMM dd, yyyy, h:mm a z", Locale.ENGLISH); 

// get current date symbols 
String[][] zoneStrings = sdf.getDateFormatSymbols().getZoneStrings(); 
for (int i = 0; i < zoneStrings.length; i++) { 
    // overwrite just America/New_York (my arbitrary choice to be "ET") 
    if (zoneStrings[i][0].equals("America/New_York")) { 
     zoneStrings[i][2] = "ET"; // short name for standard time 
     zoneStrings[i][4] = "ET"; // short name for daylight time 
     break; 
    } 
} 
// create another date symbols and set in the formatter 
DateFormatSymbols symbols = new DateFormatSymbols(Locale.ENGLISH); 
symbols.setZoneStrings(zoneStrings); 
sdf.setDateFormatSymbols(symbols); 

String dateString = "Aug 15, 2017, 4:58 PM ET"; 
System.out.println(sdf.parse(dateString)); 

這將解析ETAmerica/New_York,和所有其他現有的內置區將不會受到影響。

Check the javadoc有關DateFormatSymbols的更多詳細信息。

另請注意,我使用了Locale.ENGLISH,因爲月份名稱(Aug)是英文的。如果我沒有指定區域設置,系統的默認值將被使用,並且不能保證永遠是英文的。即使它默認是正確的,即使在運行時也可以在沒有通知的情況下進行更改,所以最好使用明確的語言環境。


新的Java日期/時間API

如果您使用的是Java 8中,可以將這段代碼與new java.time API。這很容易,less bugged and less error-prone than the old SimpleDateFormat and Calendar APIs

所有相關的類都在java.time包中。您只需要定義首選區域的java.util.Set並將其設置爲java.time.format.DateTimeFormatter。然後你把它解析到java.time.ZonedDateTime - 如果你還需要有java.util.Date工作,你可以很容易地將其轉換:

// prefered zones 
Set<ZoneId> preferredZones = new HashSet<>(); 
preferredZones.add(ZoneId.of("America/New_York")); 

DateTimeFormatter fmt = new DateTimeFormatterBuilder() 
    // date and time 
    .appendPattern("MMM dd, yyyy, h:mm a ") 
    // zone (use set of prefered zones) 
    .appendZoneText(TextStyle.SHORT, preferredZones) 
    // create formatter (use English locale for month name) 
    .toFormatter(Locale.ENGLISH); 
String dateString = "Aug 15, 2017, 4:58 PM ET"; 
// parse string 
ZonedDateTime zdt = ZonedDateTime.parse(dateString, fmt); 
// convert to java.util.Date 
Date date = Date.from(zdt.toInstant()); 

夏令時發出

有一些極端情況。 America/New_York時區has Daylight Saving Time (DST),所以當它開始和結束時,您可能會有意想不到的結果。

如果我當DST結束日期:

String dateString = "Nov 02, 2008, 1:30 AM ET"; 

凌晨2時許,鍾移1小時回凌晨1點,所以存在兩次凌晨1點和凌晨1:59。之間的本地時間(DST中和非DST偏移)。 DST結束(-05:00),這樣的日期將等同於2008-11-02T01:30-05:00

SimpleDateFormat將得到補償,而ZonedDateTime將獲得前(-04:00)偏移和日期將相當於2008-11-02T01:30-04:00

幸運的是,ZonedDateTimewithLaterOffsetAtOverlap()方法,它在DST結束後返回偏移量處的相應日期。所以你可以效仿SimpleDateFormat的行爲來調用這個方法。


如果我當DST開始,雖然日期:

String dateString = "Mar 09, 2008, 2:30 AM ET"; 

凌晨2時許,鍾移着凌晨3點,所以不存在凌晨2點和凌晨2:59之間本地時間。在這種情況下,SimpleDateFormatZonedDateTime會將時間調整爲凌晨3:30,並使用DST偏移量(-04:00) - 日期將相當於2008-03-09T03:30-04:00

+1

並記住,在一年中將有一個小時不能區分DST的標準時間,在時間倒退的秋季轉換和0100到0200的小時重複。 –

+1

@JimGarrison的確。每個API的行爲方式都不相同:'SimpleDateFormat'偏好DST結束後的偏移量,'ZonedDateTime'偏好之前的偏移量。我已經更新了答案,非常感謝! – 2017-08-16 17:31:57

+0

'8月'不一定是英語,它可能是其他幾種語言,如e。 G。德語。而@JimGarrison,它不一定是重複的1到2之間的小時。這裏是2到3之間的小時。 – Vampire

0

你的格式是好的,它只是你的日期是錯誤。 ET不是有效的區域標識符。

With TimeZone.getAvailableIDs()您可以查看有效的區域ID。

相關問題