问题
为什么我的 Android 应用无法解析 String str1= "Tue Jun 20 15:56:29 CEST 2017"
?
我发现了一些类似的问题,但没有一个对我有帮助。
旁注
在我的项目中,我有一些在计算机上运行的 Java 应用程序和一些 Android 应用程序。他们能够相互交流。
消息中有时间戳。但是,我的 Java 应用程序正在发送格式为 String str1= "Tue Jun 20 15:56:29 CEST 2017"
的时间戳,而我的 Android 应用程序则为 String str2 = "Tue Jun 20 13: 40:37 GMT+02:00 2017”
。要保存包含时间的消息,我必须将传入时间解析为日期。
我的安卓应用
在我的 Android 应用程序中,我无法正确解析 String str1= "Tue Jun 20 15:56:29 CEST 2017"
:
java.text.ParseException: Unparseable date: "Tue Jun 20 15:56:29 CEST 2017"
String str2 = "Tue Jun 20 13:40:37 GMT+02:00 2017"
工作正常。
代码:
String str1 = "Tue Jun 20 14:53:08 CEST 2017";
SimpleDateFormat formatter = new SimpleDateFormat("EE MMM dd HH:mm:ss zzzz yyyy", Locale.US);
try {
Date date = formatter.parse(str1);
} catch (ParseException e) {
e.printStackTrace();
}
// test
String str2 = "Tue Jun 20 13:40:37 GMT+02:00 2017";
formatter = new SimpleDateFormat("EE MMM dd HH:mm:ss zzzz yyyy", Locale.US);
try {
Date date = formatter.parse(str2);
} catch (ParseException e) {
e.printStackTrace();
}
我的 Java 应用
但是,我的 Java 应用程序可以正确解析这两个字符串。
代码:
String str = "Tue Jun 20 14:53:08 CEST 2017";
SimpleDateFormat formatter = new SimpleDateFormat("EE MMM dd HH:mm:ss zzzz yyyy", Locale.US);
try {
Date date = formatter.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
// test
str = "Tue Jun 20 13:40:37 GMT+02:00 2017";
formatter = new SimpleDateFormat("EE MMM dd HH:mm:ss zzzz yyyy", Locale.US);
try {
Date date = formatter.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
ThreeTen 解决方案
字符串到 LocalDateTime
要转换传入的字符串,我使用以下代码:
String time = "Mon Jun 26 15:42:51 GMT 2017";
DateTimeFormatter gmtDateTimeFormatter = DateTimeFormatter.ofPattern("EE MMM dd HH:mm:ss 'GMT' yyyy", Locale.ENGLISH));
LocalDateTime timestamp = LocalDateTime.parse(time, gmtDateTimeFormatter);
LocalDateTime 到字符串
为了将我的 LocalDateTime 转换为字符串,我使用了这个:
LocalDateTime timestamp = LocalDateTime.now();
DateTimeFormatter gmtDateTimeFormatter = DateTimeFormatter.ofPattern("EE MMM dd HH:mm:ss 'GMT' yyyy", Locale.ENGLISH));
String time = gmtDateTimeFormatter.format(timestamp);
最佳答案
也许 Android 处理 zzzz
模式的方式有所不同(可能 Java 的实现比 Android 更好地处理它,因此它以 Android 不会的方式“猜测”正确的时区)。我不知道。
无论如何,我可以建议您避免使用那些旧类吗?这些旧类(Date
、Calendar
和 SimpleDateFormat
)具有 lots of problems和 design issues ,并且它们正在被新的 API 取代。
如果您使用的是 Java 8,请考虑使用 new java.time API .这更容易,less bugged and less error-prone than the old APIs .
如果您使用的是 Java <= 7,您可以使用 ThreeTen Backport ,Java 8 的新日期/时间类的一个很好的反向移植。对于 Android,有 ThreeTenABP (更多关于如何使用它的信息 here)。
下面的代码适用于两者。
唯一的区别是包名称(在 Java 8 中是 java.time
,在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp
),但是类和方法名称相同。
要解析这两种格式,您可以使用带有可选部分的 DateTimeFormatter
。这是因为 CEST
是一个时区短名称而 GMT+02:00
是一个 UTC 偏移量,所以如果你想用相同的格式化程序解析两者,你需要为每种格式使用一个可选部分。
另一个细节是像 CET
或 CEST
这样的短名称是 ambiguous and not standard .新 API 使用 IANA timezones names (始终采用 Continent/City
格式,例如 America/Sao_Paulo
或 Europe/Berlin
)。
因此,您需要选择一个适合您需要的时区。在下面的示例中,我刚刚选择了 CEST 中的时区(Europe/Berlin
),但您可以根据需要更改它 - 您可以使用 获取所有名称的列表>ZoneId.getAvailableZoneIds()
.
由于新 API 不解析 CEST
(因为它的歧义),我需要创建一个带有首选时区的集合,以便正确解析输入:
// when parsing, if finds ambiguous CET or CEST, it uses Berlin as prefered timezone
Set<ZoneId> set = new HashSet<>();
set.add(ZoneId.of("Europe/Berlin"));
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// your pattern (weekday, month, day, hour/minute/second)
.appendPattern("EE MMM dd HH:mm:ss ")
// optional timezone short name (like "CST" or "CEST")
.optionalStart().appendZoneText(TextStyle.SHORT, set).optionalEnd()
// optional GMT offset (like "GMT+02:00")
.optionalStart().appendPattern("OOOO").optionalEnd()
// year
.appendPattern(" yyyy")
// create formatter (using English locale to make sure it parses weekday and month names correctly)
.toFormatter(Locale.US);
要解析 Tue Jun 20 14:53:08 CEST 2017
,只需使用格式化程序:
ZonedDateTime z1 = ZonedDateTime.parse("Tue Jun 20 14:53:08 CEST 2017", fmt);
System.out.println(z1);
输出是:
2017-06-20T14:53:08+02:00[Europe/Berlin]
请注意,根据我们创建的集合,CEST
已映射到 Europe/Berlin
。
要解析 Tue Jun 20 13:40:37 GMT+02:00 2017
,我们可以使用相同的格式化程序。但是 GMT+02:00
可以在很多不同的地区,因此 API 无法将其映射到单个时区。要将其转换为正确的时区,我需要使用 withZoneSameInstant()
方法:
// parse with UTC offset
ZonedDateTime z2 = ZonedDateTime.parse("Tue Jun 20 13:40:37 GMT+02:00 2017", fmt)
// convert to Berlin timezone
.withZoneSameInstant(ZoneId.of("Europe/Berlin"));
System.out.println(z2);
输出是:
2017-06-20T13:40:37+02:00[Europe/Berlin]
PS:第一种情况 (z1
) 在 Java 8 中有效,但在 ThreeTen Backport 中它没有将时区设置为柏林。要修复它,只需调用 .withZoneSameInstant(ZoneId.of("Europe/Berlin"))
就像我们对 z2
所做的那样。
如果您仍然需要使用 java.util.Date
,您可以在新 API 之间进行转换。
在 java.time
中,Date
类添加了新方法:
// convert ZonedDateTime to Date
Date date = Date.from(z1.toInstant());
// convert back to ZonedDateTime (using Berlin timezone)
ZonedDateTime z = date.toInstant().atZone(ZoneId.of("Europe/Berlin"));
在 ThreeTen backport(和 Android)中,您可以使用 org.threeten.bp.DateTimeUtils
类:
// convert ZonedDateTime to Date
Date date = DateTimeUtils.toDate(z1.toInstant());
// convert back to ZonedDateTime (using Berlin timezone)
ZonedDateTime z = DateTimeUtils.toInstant(date).atZone(ZoneId.of("Europe/Berlin"));
关于java - 将字符串转换为日期(CEST 工作正常,GMT+02 :00 doesn't work),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44656519/