Java DateFormat parse()不';我不尊重时区

Java DateFormat parse()不';我不尊重时区,java,parsing,date,format,Java,Parsing,Date,Format,结果如下: 2011-09-2414:10:51-0400 2011年9月24日星期六20:10:51 CEST 为什么当我解析从format()获得的日期时,它不尊重时区?您正在打印调用结果,调用结果总是使用默认时区。基本上,除了调试之外,您不应该使用Date.toString() 不要忘记,日期没有时区-它表示时间上的一个瞬间,从Unix纪元(1970年1月1日UTC午夜)开始以毫秒为单位测量 如果您再次使用格式化程序格式化日期,应该会得到与以前相同的答案 顺便说一句,如果您正在用Java进

结果如下:

2011-09-2414:10:51-0400

2011年9月24日星期六20:10:51 CEST


为什么当我解析从format()获得的日期时,它不尊重时区?

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

不要忘记,
日期
没有时区-它表示时间上的一个瞬间,从Unix纪元(1970年1月1日UTC午夜)开始以毫秒为单位测量

如果您再次使用格式化程序格式化日期,应该会得到与以前相同的答案

顺便说一句,如果您正在用Java进行大量的日期/时间工作,我建议使用而不是
Date
/
Calendar
;这是一个更好的API

DateFormat是日期/时间格式化子类的抽象类 哪种格式和解析日期或时间的语言独立 态度。日期/时间格式子类,例如SimpleDataFormat, 允许格式化(即日期->文本)、解析(文本->日期), 和正常化日期表示为日期对象或 自1970年1月1日00:00:00 GMT以来的毫秒数

从中,它返回EPOCH time

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

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();
}

这很混乱,但很有必要,因为
parse()
不返回保留时区的值,
format()
不接受定义时区的值(日期类)。

一个
java.util.Date
对象不像;相反,它表示自称为“历元”的标准基准时间(即1970年1月1日00:00:00 GMT)起的毫秒数。因为它不保存任何格式和时区信息,所以它应用格式,
EEE MMM dd HH:mm:ss z yyyy
和JVM的时区来返回从该毫秒值派生的
Date\toString
值。如果需要以不同的格式和时区打印日期时间,则需要使用带有所需格式和适用时区的
SimpleDateFormat
,例如:

    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());
请注意,
java.util
Date-Time API及其格式化API,
SimpleDataFormat
已经过时且容易出错。建议完全停止使用,并切换到*

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

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);
    }
}
在这里,您可以使用
yyyy
而不是
uuu
但是

了解更多关于
java.time
、来自的*


*无论出于何种原因,如果您必须坚持使用Java6或Java7,您可以使用哪个backport将大部分Java.time功能移植到Java6&7。如果您正在为Android项目工作,并且您的Android API级别仍然不符合Java-8,请检查并确认。

没错……试着打印
System.out.println(df.format(df.parse(df.format(cal.getTime())))并且它按预期工作:-)好的,那么问题显然来自toString()?谢谢您的建议。@MaximeLaval:From
Date.toString
特别是,是的-您需要注意的是,
Date
只是一个瞬间,而不是在任何特定的时区或日历中。除了显示/调试之外,千万不要依赖
.toString
方法。还要注意SimpleDataFormatter是区域设置敏感的。@ChristopherRoussy:当然,除非记录了该行为以满足您的需要。
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