Java SimpleDateFormat无法解析“MMM dd,yyyy,h:mm a z”“Aug 15,2017,4:58 PM ET”
我无法解析此日期。任何人都注意到任何错他们似乎都失败了。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);
}
}
正如很多人已经说过的,ET
不是一个时区。它是一个常用的缩写,指的是both EST and EDT(东部标准时间和东部夏令时间),但有more than one timezone that uses it。
短名称(如EST
和EDT
)也不是时区,因为这样的缩写是ambiguous and not standard。有more than one timezone that can use the same abbreviations。
理想的是使用IANA timezones names(总是格式为Region/City
,如America/Sao_Paulo
或Europe/Berlin
)。 但是使用诸如EST
和ET
之类的短名称是普遍和常见的,所以我们必须忍受它(并且还要做一些解决方法)。
的第一件事是到定义要为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));
这将解析ET
为America/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
。
幸运的是,ZonedDateTime
有withLaterOffsetAtOverlap()
方法,它在DST结束后返回偏移量处的相应日期。所以你可以效仿SimpleDateFormat
的行为来调用这个方法。
如果我当DST开始,虽然日期:
String dateString = "Mar 09, 2008, 2:30 AM ET";
凌晨2时许,钟移着凌晨3点,所以不存在凌晨2点和凌晨2:59之间本地时间。在这种情况下,SimpleDateFormat
和ZonedDateTime
会将时间调整为凌晨3:30,并使用DST偏移量(-04:00
) - 日期将相当于2008-03-09T03:30-04:00
。
并记住,在一年中将有一个小时不能区分DST的标准时间,在时间倒退的秋季转换和0100到0200的小时重复。 –
@JimGarrison的确。每个API的行为方式都不相同:'SimpleDateFormat'偏好DST结束后的偏移量,'ZonedDateTime'偏好之前的偏移量。我已经更新了答案,非常感谢! – 2017-08-16 17:31:57
'8月'不一定是英语,它可能是其他几种语言,如e。 G。德语。而@JimGarrison,它不一定是重复的1到2之间的小时。这里是2到3之间的小时。 – Vampire
你的格式是好的,它只是你的日期是错误。 ET
不是有效的区域标识符。
With TimeZone.getAvailableIDs()
您可以查看有效的区域ID。
'ET'不是时区。 “EDT”是东部夏令时,“EST”是东部标准时间。 –
是你的策略,因为你不知道字符串是怎么样的?或者因为你试图找到哪一个工作? –
看来你应该用'EDT'来代替'ET'。 –