【问题标题】:Java SimpleDateFormat Wrong Timezone after parsingJava SimpleDateFormat 解析后时区错误
【发布时间】:2011-12-18 15:45:06
【问题描述】:

为什么:当我输入带有 GMT 时区的日期字符串时,SimpleDateFormat 会解析它并输出 EET 时区?

public static String DATE_FORMAT="dd MMM yyyy hh:mm:ss z";
public static String CURRENT_DATE_STRING ="31 October 2011 11:19:56 GMT";
...
SimpleDateFormat simpleDateFormat =  new SimpleDateFormat(DATE_FORMAT, Locale.US);
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(simpleDateFormat.parseObject(CURRENT_DATE_STRING));

输出是:

2011 年 10 月 31 日星期一 13:19:56 EET

而不是

2011 年 10 月 31 日星期一 13:19:56 GMT

【问题讨论】:

    标签: java date timezone


    【解决方案1】:

    java.util.Date 不保存时区信息。

    java.util.Date 对象仅表示自称为“纪元”的标准基准时间(即 1970 年 1 月 1 日 00:00)以来的毫秒数: 00 GMT(或 UTC)。由于它不保存任何时区信息,它的 toString 函数应用 JVM 的时区以返回格式为 EEE MMM dd HH:mm:ss zzz yyyyString,从这个 毫秒 值。要以不同格式和 timezone 获得 String 对象的 String 表示,您需要使用具有所需格式和适用 timezoneSimpleDateFormat

    除此之外,您的代码还有几个问题:

    1. 使用 H 而不是 h 来表示 24 小时格式。字母 h 用于 12 小时格式(即带有 AM/PM 标记)。
    2. 尽管MMM 可以用SimpleDateFormat 解析月份的长名称(例如一月),但它也适用于三个字母的月份名称(例如一月)。如果您尝试使用现代日期时间 API 进行操作,您将收到 DateTimeParseException。您应该使用 MMMM 作为月份的长名称。

    包含以下几点的演示:

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Locale;
    import java.util.TimeZone;
    
    public class Main {
        public static void main(String[] args) throws ParseException {
            String strDateTime = "31 October 2011 11:19:56 GMT";
    
            SimpleDateFormat sdf = new SimpleDateFormat("dd MMMM yyyy HH:mm:ss z", Locale.ENGLISH);
            sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
    
            Date date = sdf.parse(strDateTime);
    
            String strDate = sdf.format(date);
            System.out.println(strDate);
    
            // Some other format
            sdf = new SimpleDateFormat("MMMM dd HH:mm:ss z yyyy", Locale.ENGLISH);
            sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
            strDate = sdf.format(date);
            System.out.println(strDate);
    
            // The last format with some other timezone
            sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
            strDate = sdf.format(date);
            System.out.println(strDate);
        }
    }
    

    输出:

    31 October 2011 11:19:56 GMT
    October 31 11:19:56 GMT 2011
    October 31 07:19:56 EDT 2011
    

    ONLINE DEMO

    切换到java.time API。

    java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用,改用modern Date-Time API*

    使用现代日期时间 API java.time 的解决方案:

    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.Locale;
    
    public class Main {
        public static void main(String[] args) {
            String strDateTime = "31 October 2011 11:19:56 GMT";
    
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd MMMM uuuu HH:mm:ss z", Locale.ENGLISH);
    
            ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, dtf);
            System.out.println(zdt);
    
            // Some other format
            DateTimeFormatter dtfAnother = DateTimeFormatter.ofPattern("MMMM dd HH:mm:ss z uuuu", Locale.ENGLISH);
            String strDate = dtfAnother.format(zdt);
            System.out.println(strDate);
        }
    }
    

    输出:

    2011-10-31T11:19:56Z[GMT]
    October 31 11:19:56 GMT 2011
    

    ONLINE DEMO

    输出中的Z 是零时区偏移的timezone designator。它代表 Zulu 并指定 Etc/UTC 时区(时区偏移量为 +00:00 小时)。

    Trail: 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

    【讨论】:

      【解决方案2】:

      您正在打印出Date.toString() 的结果。 Date 没有任何时区概念 - 它只是自 UTC Unix 纪元以来的毫秒数。 Date.toString()总是使用系统默认时区。

      请注意,您不应期待“2011 年 10 月 31 日星期一 13:19:56 GMT”,因为您给出的时间指定 GMT 小时为 11,而不是 13。

      如果要使用特定时区进行打印,则应使用另一个DateFormat 进行打印,而不是使用Date.toString()。 (Date.toString() 不断造成这样的混乱;这真的很不幸。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多