为什么我的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应用程序可以正确解析这两个字符串。
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();
}
要转换我的传入字符串,我使用以下代码:
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 timestamp = LocalDateTime.now();
DateTimeFormatter gmtDateTimeFormatter = DateTimeFormatter.ofPattern("EE MMM dd HH:mm:ss 'GMT' yyyy", Locale.ENGLISH));
String time = gmtDateTimeFormatter.format(timestamp);
答案 0 :(得分:1)
java.util
日期时间 API 及其格式化 API SimpleDateFormat
已过时且容易出错。建议完全停止使用它们并切换到 modern Date-Time API*。
另外,下面引用的是来自 home page of Joda-Time 的通知:
<块引用>请注意,从 Java SE 8 开始,要求用户迁移到 java.time (JSR-310) - JDK 的核心部分,取代了该项目。
不要像您所做的那样为时区使用固定文本(例如 'GMT'
),因为该方法可能无法用于其他语言环境。
使用 java.time
(现代日期时间 API)的解决方案:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// Test
System.out.println(parse("Tue Jun 20 14:53:08 CEST 2017"));
System.out.println(parse("Tue Jun 20 13:40:37 GMT+02:00 2017"));
}
static ZonedDateTime parse(String strDateTime) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM d H:m:s z u", Locale.ENGLISH);
return ZonedDateTime.parse(strDateTime, dtf);
}
}
输出:
2017-06-20T14:53:08+02:00[Europe/Paris]
2017-06-20T13:40:37+02:00[GMT+02:00]
从 Trail: Date Time 了解有关现代 Date-Time API 的更多信息。
* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 & 7. 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。
答案 1 :(得分:0)
也许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&lt; = 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()
获取所有名称的列表。< / p>
由于新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 [欧洲/柏林]
请注意,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 [欧洲/柏林]
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"));