将字符串转换为日期(CEST工作正常,GMT + 02:00不起作用)

时间:2017-06-20 14:55:34

标签: java android datetime datetime-parsing

问题

为什么我的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-App

在我的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-App

但是,我的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 to String

要将我的LocalDateTime转换为我使用过的字符串:

    LocalDateTime timestamp = LocalDateTime.now();
    DateTimeFormatter gmtDateTimeFormatter = DateTimeFormatter.ofPattern("EE MMM dd HH:mm:ss 'GMT' yyyy", Locale.ENGLISH));
    String time = gmtDateTimeFormatter.format(timestamp); 

2 个答案:

答案 0 :(得分:1)

java.time

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]

ONLINE DEMO

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 desugaringHow to use ThreeTenABP in Android Project

答案 1 :(得分:0)

也许Android处理zzzz模式的方式有所不同(可能Java的实现比Android更好地处理它,因此它以Android没有的方式“猜测”正确的时区)。我不知道。

无论如何,我可以建议你避免使用那些旧班吗?这些旧类(DateCalendarSimpleDateFormat)包含lots of problemsdesign 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偏移量,因此如果要使用相同的格式化程序解析两者,则需要为每种格式使用一个可选节。

另一个细节是CETCEST等短名称是ambiguous and not standard。新API使用IANA timezones names(始终采用Continent/City格式,例如America/Sao_PauloEurope/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"));