关于java:考虑夏令时转换为UTC

Converting to UTC considering daylight saving

我有一个约会,例如2014年4月17日星期四09:03:01 GMT在时区:

sun.util.calendar.ZoneInfo[id="Europe/London",offset=0,dstSavings=3600000,useDaylight=true,transitions=242,lastRule=java.util.SimpleTimeZone[id=Europe/London,offset=0,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]

每次尝试转换为UTC时,它都会返回2014年4月17日格林尼治标准时间2014年10月17日10:03:01

这没有意义,因为相应的UTC时间实际上是2014年4月17日08:03:01 GMT,因为我的时区时间因夏令时增加了1小时。

我用来转换的代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//timeZone - id="Europe/London"
public static Date timeZoneConvertDate(Date date, TimeZone timeZone) {
        SimpleDateFormat sdf = new SimpleDateFormat();
        sdf.setTimeZone(timeZone);
        sdf.applyPattern("dd-MM-yyyy HH:mm:ss");
        String newDate = sdf.format(date);
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        try {
            Date nd = sdf.parse(newDate);
            return nd;
        } catch (ParseException e) {
            return null;
    }
}

有人可以解释我做错了吗?


java.util.Date中没有时区

正如Duncan的正确答案所说,java.util.Date没有时区组件。令人困惑的是它的toString方法应用了JVM的默认时区。要在另一个时区中显示,请使用SimpleDateFormat应用调整。

更好的是,避免使用臭名昭着的java.util.Date,.Calendar和SimpleDateFormat。在Java 8中使用Joda-Time或新的java.time包。

乔达时间

在Joda-Time中,DateTime对象确实包含指定的时区。如果未指定时区,则会分配JVM的默认时区。

1
2
3
4
DateTimeZone timeZone = DateTimeZone.forID("Europe/London" );
DateTime dateTime = new DateTime( 2014, 4, 17, 9, 3, 1, timeZone );
DateTime dateTimeUtc = dateTime.withZone( DateTimeZone.UTC );
DateTime dateTimeIndia = dateTime.withZone( DateTimeZone.forID("Asia/Kolkata" ) );

跑的时候......

1
2
3
dateTime: 2014-04-17T09:03:01.000+01:00
dateTimeUtc: 2014-04-17T08:03:01.000Z
dateTimeIndia: 2014-04-17T13:33:01.000+05:30 (note the half-hour difference, +05:30)

您可以轻松地来回转换为java.util.Date。

1
DateTime dateTime = new DateTime( myJUDate, timeZone );

…和…

1
Java.util.Date date = dateTime.toDate();

TL;博士

Date没有与之关联的时区,因此您无法创建调整日期对象时区的方法。如果要保留TZ信息,或者最好查看Joda-Time,则需要使用Calendar对象。

输出说明

Date值没有时区信息;它只是自纪元以来的毫秒数。考虑到这一点,让我们看看你在做什么:

1
2
3
4
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.setTimeZone(timeZone);
sdf.applyPattern("dd-MM-yyyy HH:mm:ss");
String newDate = sdf.format(date);

这部分代码创建了一个格式化程序,用于在伦敦时区打印日期。因此,您在撰写本文时得到的结果大约是:17-04-2014 11:38:15(假设您刚刚创建了日期对象)。

1
2
3
4
5
6
7
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
try {
  Date nd = sdf.parse(newDate);
  return nd;
} catch (ParseException e) {
   return null;
}

在这里,您告诉日期解析器读取日期,就像它是UTC日期一样。它使用该信息来了解自纪元已经过去多少毫秒。您获取的日期对象仍然没有与之关联的时区。

UTC比英国夏令时晚一个小时,因此它将创建一个日期对象,在BST时区打印时提前一小时出现。所以当我打印nd时,我得到:Thu Apr 17 12:38:15 BST 2014