切勿在没有Locale 的情况下使用SimpleDateFormat 或DateTimeFormatter
由于给定的日期时间是英文的,你应该在你的日期时间解析器中使用Locale.ENGLISH;否则解析将在使用非英语类型区域设置的系统(计算机、电话等)中失败。
另外,请注意 java.util 的日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用,转用modern date-time API。
演示:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
final String strDateTime = "Mon Sep 18 10:30:06 MST 2017";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu", Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, dtf);
System.out.println(zdt);
}
}
输出:
2017-09-18T10:30:06-06:00[America/Denver]
在我们继续之前关于时区的重要说明:
避免使用 3 个字母的缩写指定时区。时区应以 Region/City 格式指定名称,例如ZoneId.of("Europe/London")。使用此约定,UTC 的ZoneId 可以用ZoneId.of("Etc/UTC") 指定。以UTC[+/-]Offset 指定的时区可以指定为Etc/GMT[+/-]Offset,例如ZoneId.of("Etc/GMT+1")、ZoneId.of("Etc/GMT+1")等
也有一些例外情况,例如要指定Turkey 的时区,我们使用
ZoneId.of("Turkey")
以下代码将为您提供所有可用的ZoneIds:
// Get the set of all time zone IDs.
Set<String> allZones = ZoneId.getAvailableZoneIds();
您应该要求您的服务器应用程序使用此约定为您提供日期时间,例如
Mon Sep 18 10:30:06 America/Denver 2017
上面的代码,没有任何改变,将适用于这个日期时间字符串。
回到原来的话题:
默认情况下,DateTimeFormatter#ofPattern 使用 JVM 在启动时根据宿主环境设置的 default FORMAT locale。 SimpleDateFormat 也是如此。我试图通过下面的演示来说明这个问题:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
final String strDateTime = "Mon Sep 18 10:30:06 America/Denver 2017";
DateTimeFormatter dtfWithDefaultLocale = null;
System.out.println("JVM's Locale: " + Locale.getDefault());
// Using DateTimeFormatter with the default Locale
dtfWithDefaultLocale = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu");
System.out.println("DateTimeFormatter's Locale: " + dtfWithDefaultLocale.getLocale());
System.out
.println("Parsed with JVM's default locale: " + ZonedDateTime.parse(strDateTime, dtfWithDefaultLocale));
// Setting the JVM's default locale to Locale.FRANCE
Locale.setDefault(Locale.FRANCE);
// Using DateTimeFormatter with Locale.ENGLISH explicitly (recommended)
DateTimeFormatter dtfWithEnglishLocale = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu", Locale.ENGLISH);
System.out.println("JVM's Locale: " + Locale.getDefault());
System.out.println("DateTimeFormatter's Locale: " + dtfWithEnglishLocale.getLocale());
ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, dtfWithEnglishLocale);
System.out.println("Parsed with Locale.ENGLISH: " + zdt);
System.out.println("JVM's Locale: " + Locale.getDefault());
// Using DateTimeFormatter with the default Locale
dtfWithDefaultLocale = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu");
System.out.println("DateTimeFormatter's Locale: " + dtfWithDefaultLocale.getLocale());
System.out
.println("Parsed with JVM's default locale: " + ZonedDateTime.parse(strDateTime, dtfWithDefaultLocale));
}
}
输出:
JVM's Locale: en_GB
DateTimeFormatter's Locale: en_GB
Parsed with JVM's default locale: 2017-09-18T10:30:06-06:00[America/Denver]
JVM's Locale: fr_FR
DateTimeFormatter's Locale: en
Parsed with Locale.ENGLISH: 2017-09-18T10:30:06-06:00[America/Denver]
JVM's Locale: fr_FR
DateTimeFormatter's Locale: fr_FR
Exception in thread "main" java.time.format.DateTimeParseException: Text 'Mon Sep 18 10:30:06 America/Denver 2017' could not be parsed at index 0
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
at java.base/java.time.ZonedDateTime.parse(ZonedDateTime.java:598)
at Main.main(Main.java:32)
以下演示,使用SimpleDateFormat,只是为了完整起见:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class Main {
public static void main(String[] args) throws ParseException {
final String strDateTime = "Mon Sep 18 10:30:06 MST 2017";
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM d H:m:s z yyyy", Locale.ENGLISH);
Date date = sdf.parse(strDateTime);
System.out.println(date);
}
}
输出:
Mon Sep 18 18:30:06 BST 2017
注意:java.util.Date 对象不像modern date-time types 那样是真正的日期时间对象;相反,它表示距离Epoch of January 1, 1970 的毫秒数。当你打印一个java.util.Date 的对象时,它的toString 方法返回从这个毫秒值计算的日期时间。由于java.util.Date 没有时区信息,它会应用您的JVM 的时区并显示相同的信息。如果您需要在不同的时区打印日期时间,您需要将时区设置为SimpleDateFomrat 并从中获取格式化字符串。