Java SimpleDateFormat解析后时区错误。

6
为什么:当我输入带有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

2个回答

10
您正在打印Date.toString()的结果。一个Date没有时区的概念,它只是自UTC Unix纪元以来的毫秒数。Date.toString()始终使用系统默认时区。
请注意,您不应该期望得到"Mon Oct 31 13:19:56 GMT 2011",因为您给出了一个指定GMT小时为11而不是13的时间。
如果要使用特定的时区进行打印,则应使用另一个DateFormat进行打印,而不是使用Date.toString()。(Date.toString()会导致这样的混淆,这真的很不幸。)

0

java.util.Date不包含时区信息。

java.util.Date对象只是表示自“纪元”即1970年1月1日00:00:00 GMT(或UTC)以来的毫秒数。因为它没有保存任何时区信息,所以其toString函数应用JVM的时区返回一个格式为EEE MMM dd HH:mm:ss zzz yyyyString,该格式派生自这个毫秒数值。如果需要在不同的时区中以不同的格式获取java.util.Date对象的String表示形式,则需要使用带有所需格式和适用时区SimpleDateFormat

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

  1. 使用24小时制时,应该使用大写字母H而不是小写字母h。小写字母h用于12小时制(即带有AM/PM标记)。
  2. 尽管SimpleDateFormat可以使用MMM解析月份的长名称(例如January),但它实际上是用于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

在线演示

切换到 java.time API。

java.util日期时间API及其格式化API SimpleDateFormat已过时且容易出错。建议完全停止使用它们并切换到现代日期时间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

在线演示

输出中的Z是零时区偏移的时区标识符。它代表Zulu并指定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项目中使用ThreeTenABP


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接