如何使用java.util.Date解析在夏令时切换期间发生的其他时区的日期

时间:2015-03-19 16:38:16

标签: java date datetime dst

我在解析以下日期时遇到了一个问题。

08 März 2015 02:15:20

代码

SimpleDateFormat fmt = new SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.GERMAN);
fmt.setLenient(false);
try {
    System.out.println(fmt.parse("08 März 2015 02:15:20"));
} catch (ParseException e) {
    e.printStackTrace();
}

经过一番调查后,我发现由于DST如何工作,我的时区早上2点不存在。一旦时钟到达凌晨1:59,下次更新时间将是凌晨3点。我已经检查过以下日期的情况:

"08 März 2015 01:59:59"
"08 März 2015 03:00:00"

两者都可以正确解析。

我想要的是一个日期对象,它正确地解释了我看到的输入日期:

Mar 8, 2015 at 2:15:20 AM

我该如何做到这一点?

2 个答案:

答案 0 :(得分:2)

理想情况下,您使用的解析器允许您解析日期/时间值而不尝试完全应用时区 - java.time.*Joda Time允许这样做,IIRC,两者都比java.util.*更清洁。

但是,如果 使用Date,并且您不知道原始时区,最安全的方法是使用TimeZone

SimpleDateFormat parser = new SimpleDateFormat(
   "dd MMM yyyy HH:mm:ss",
   Locale.GERMAN);
parser.setLenient(false);
parser.setTimeZone(TimeZone.getTimeZone("UTC"));

因此,它将解析它,就好像它最初是在UTC中一样 - 所以当你想格式化它回到文本时,你需要做同样的事情:

SimpleDateFormat formatter = new SimpleDateFormat(
   "EEE d, yyyy at h:mm:ss tt",
   Locale.US);
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
String text = formatter.format(date);

答案 1 :(得分:0)

TL;DR

TimeZone.getTimeZone("Europe/Berlin") 设置为 fmt 的时区。

演示:

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) {
        SimpleDateFormat fmt = new SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.GERMAN);
        fmt.setLenient(false);
        fmt.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        try {
            Date date = fmt.parse("08 März 2015 02:15:20");
            System.out.println(fmt.format(date));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

输出:

08 März 2015 02:15:20

ONLINE DEMO

如果您想要带有 AM/PM 标记的输出,该技术将保持不变,但额外的步骤是为输出创建另一个 SimpleDateFormat 实例(因为输出的格式将与输入不同) .

演示:

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) {
        SimpleDateFormat parser = new SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.GERMAN);
        parser.setLenient(false);
        parser.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

        SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy hh:mm:ss a", Locale.GERMAN);
        formatter.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        try {
            Date date = parser.parse("08 März 2015 02:15:20");
            System.out.println(formatter.format(date));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

输出:

08 März 2015 02:15:20 AM

ONLINE DEMO

java.time

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

另外,下面引用的是来自 home page of Joda-Time 的通知:

<块引用>

请注意,从 Java SE 8 开始,要求用户迁移到 java.time (JSR-310) - JDK 的核心部分,取代了该项目。

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

java.util.Date 不同,import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; public class Main { public static void main(String[] args) { DateTimeFormatter parser = DateTimeFormatter.ofPattern("dd MMM uuuu HH:mm:ss", Locale.GERMAN); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMM uuuu hh:mm:ss a", Locale.GERMAN); LocalDateTime ldt = LocalDateTime.parse("08 März 2015 02:15:20", parser); System.out.println(parser.format(ldt)); System.out.println(formatter.format(ldt)); } } 只是对自 UNIX 时代(1970 年 1 月 1 日,格林威治标准时间 00:00:00)以来毫秒数的包装,java.time types 真正代表日期、时间,日期时间,带和不带时区信息。

08 März 2015 02:15:20
08 März 2015 02:15:20 AM

输出:

between

ONLINE DEMO

Trail: Date Time 了解有关现代 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