Java DateFormat parse()不尊重时区

时间:2011-09-23 18:25:52

标签: java parsing date format

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("America/New_York"));
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
df.setTimeZone(TimeZone.getTimeZone("America/New_York"));

try {
    System.out.println(df.format(cal.getTime()));
    System.out.println(df.parse(df.format(cal.getTime())));
} catch (ParseException e) {
    e.printStackTrace();
}

结果如下:

  

2011-09-24 14:10:51 -0400

     

周六9月24日20:10:51 CEST 2011

为什么当我解析格式()得到的日期时,它不尊重时区?

4 个答案:

答案 0 :(得分:23)

您正在打印调用Date.toString()的结果,始终使用默认时区。基本上,除了调试之外,不应该使用Date.toString()

不要忘记Date 时区 - 它代表一个时刻,以Unix时代(1970年1月1日午夜UTC)为单位测量)。

如果您再次使用格式化程序格式化日期,那么 应该提供与以前相同的答案。

顺便说一句,如果您在Java中进行大量的日期/时间工作,我建议您使用Joda Time代替Date / Calendar;它是很多更好的API。

答案 1 :(得分:7)

DateFormat.parse()不是查询(返回值并且不会更改系统状态的内容)。这是一个具有更新内部Calendar对象的副作用的命令。致电parse()后,您必须访问DateFormat的{​​{1}}或致电Calendar来访问时区。除非您想丢弃原始时区并使用当地时间,否则请勿使用DateFormat.getTimeZone()中返回的Date值。而是在解析后使用日历对象。格式方法也是如此。如果要格式化日期,请在调用parse()之前将带有时区信息的日历传递到DateFormat对象。以下是如何将一种格式转换为另一种格式,保留原始时区:

format()

由于 DateFormat originalDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy"); DateFormat targetDateFormat = new SimpleDateFormat("EEE., MMM. dd, yyyy"); originalDateFormat.parse(origDateString); targetDateFormat.setCalendar(originalDateFormat.getCalendar()); return targetDateFormat.format(targetDateFormat.getCalendar().getTime()); 没有返回保留时区的值而parse()不接受定义时区的值(format()类),因此它很麻烦但是必要。

答案 2 :(得分:2)

  

DateFormat是日期/时间格式化子类的抽象类   它以与语言无关的格式和分析日期或时间   方式。日期/时间格式化子类,例如SimpleDateFormat,   允许格式化(即日期 - >文本),解析(文本 - >日期),   和规范化。 日期表示为Date对象或表示为   自1970年1月1日00:00:00 GMT后的毫秒数

spec开始,它返回EPOCH时间

答案 3 :(得分:1)

java.util.Date 对象不是像 modern Date-Time types 那样真正的 Date-Time 对象;相反,它表示自称为“纪元”的标准基准时间以来的毫秒数,即 January 1, 1970, 00:00:00 GMT(或 UTC)。由于它不包含任何格式和时区信息,因此它应用格式 EEE MMM dd HH:mm:ss z yyyy 和 JVM 的时区来返回从该毫秒值派生的 Date#toString 值。如果您需要以不同的格式和时区打印日期时间,则需要使用具有所需格式和适用时区的 SimpleDateFormat,例如

Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
String strDate = sdf.format(date);
System.out.println(strDate);

请注意,java.util 日期时间 API 及其格式 API SimpleDateFormat 已过时且容易出错。建议完全停止使用它们并切换到 modern Date-Time API*

使用现代 API java.time 的解决方案:

import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("America/New_York"));
        System.out.println(zdt);

        // With timezone offset
        OffsetDateTime odt = zdt.toOffsetDateTime();
        System.out.println(odt);

        // In a custom format
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss Z", Locale.ENGLISH);
        String formatted = dtf.format(odt);
        System.out.println(formatted);
    }
}

输出:

2021-06-04T14:25:08.266940-04:00[America/New_York]
2021-06-04T14:25:08.266940-04:00
2021-06-04 14:25:08 -0400

在这里,您可以使用 yyyy 代替 uuuu,而是使用 I prefer u to y

modern Date-Time API 中详细了解 java.timeTrail: Date Time*


* 出于任何原因,如果您必须坚持使用 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