Java SimpleDateFormat 解析后时区错误



>为什么:当我给出带有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));

输出为:

周一 10月 31 13:19:56 EET 2011

而不是

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

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

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

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

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

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

除此之外,您的代码还存在一些问题:

  1. 使用 H 而不是 h 表示 24 小时制格式。字母h用于 12 小时格式(即带有 AM/PM 标记(。
  2. 尽管MMM用于用SimpleDateFormat解析月份的长名称(例如一月(,但它适用于 3 个字母的月份名称(例如 Jan(。如果您尝试使用现代日期时间 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

在线演示

Switch to java.time API.

java.util日期时间 API 及其格式设置 API SimpleDateFormat已过时且容易出错。建议完全停止使用它们,并切换到现代日期-时间 API*

使用 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) {
        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

在线演示

输出中的Z是零时区偏移量的时区指示符。它代表祖鲁语,并指定Etc/UTC时区(时区偏移量为 +00:00 小时(。

要了解有关现代日期-时间 API 的更多信息,请参阅跟踪:日期时间

<小时 />

出于任何原因,如果你必须坚持使用Java 6或Java 7,你可以使用ThreeTen-Backport,它将大部分java.time功能向后移植到Java 6和7。如果您正在为 Android 项目工作,并且您的 Android API 级别仍然不符合 Java-8,请查看通过脱糖提供的 Java 8+ API 和如何在 Android Project 中使用 ThreeTenABP。

相关内容

  • 没有找到相关文章