2017-08-16 107 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。

相关问题