关于日期:如何使用Java中的TimeZone解决夏令时问题

How to tackle daylight savings using TimeZone in Java

我必须在我的Java应用程序中打印EST时间。 我使用以下方法将时区设置为EST:

1
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST"));

但是当在这个时区中遵循夏令时时,我的代码不能打印正确的时间(它打印的时间减少1小时)。

无论是否观察到夏令时,如何使代码始终能够正确读取正确的时间?

PS:我尝试将时区设置为EDT,但它没有解决问题。


这是问题的开始:

1
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST"));

应全心全意地避免使用3个字母的缩写,以支持TZDB区域ID。 EST是东部标准时间 - 标准时间从未观察到DST;它不是真正的全时区名称。它是用于部分时区的名称。 (不幸的是,我没有遇到这个"半时区"概念的好词。)

您需要一个完整的时区名称。例如,America/New_York位于东部时区:

1
2
3
4
5
TimeZone zone = TimeZone.getTimeZone("America/New_York");
DateFormat format = DateFormat.getDateTimeInstance();
format.setTimeZone(zone);

System.out.println(format.format(new Date()));


其他答案是正确的,尤其是Jon Skeet的答案,但过时了。

java.time

这些旧的日期时间类已被Java 8及更高版本中内置的java.time框架所取代。

如果您只想要UTC中的当前时间,请使用Instant类。

1
Instant now = Instant.now();

EST不是时区,正如Jon Skeet的正确答案中所述。这样的3-4个字母代码既不是标准化也不是唯一的,并且进一步混淆了夏令时(DST)。使用"continent / region"格式的正确时区名称。

也许你的意思是北美东海岸的东部标准时间?还是埃及标准时间?还是欧洲标准时间?

1
2
3
ZoneId zoneId = ZoneId.of("America/New_York" );
ZoneId zoneId = ZoneId.of("Africa/Cairo" );
ZoneId zoneId = ZoneId.of("Europe/Lisbon" );

使用任何此类ZoneId对象将当前时刻调整到特定时区以生成ZonedDateTime对象。

1
ZonedDateTime zdt = ZonedDateTime.now( zoneId ) ;

通过从第一个生成另一个ZonedDateTime对象,将ZonedDateTime调整到不同的时区。 java.time框架使用不可变对象而不是更改(变异)现有对象。

1
ZonedDateTime zdtGuam = zdt.withZoneSameInstant( ZoneId.of("Pacific/Guam" ) ) ;


您可以输入"EST5EDT",而不是输入"EST"作为时区。如你所知,只是"EDT"不起作用。这将解决夏令时问题。代码行如下所示:

1
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST5EDT"));

1
2
3
4
5
6
7
8
private static Long DateTimeNowTicks(){
    long TICKS_AT_EPOCH = 621355968000000000L;
    TimeZone timeZone = Calendar.getInstance().getTimeZone();
    int offs = timeZone.getRawOffset();
    if (timeZone.inDaylightTime(new Date()))
        offs += 60 * 60 * 1000;
    return (System.currentTimeMillis() + offs) * 10000 + TICKS_AT_EPOCH;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static float calculateTimeZone(String deviceTimeZone) {
    float ONE_HOUR_MILLIS = 60 * 60 * 1000;

    // Current timezone and date
    TimeZone timeZone = TimeZone.getTimeZone(deviceTimeZone);
    Date nowDate = new Date();
    float offsetFromUtc = timeZone.getOffset(nowDate.getTime()) / ONE_HOUR_MILLIS;

    // Daylight Saving time
    if (timeZone.useDaylightTime()) {
        // DST is used
        // I'm saving this is preferences for later use

        // save the offset value to use it later
        float dstOffset = timeZone.getDSTSavings() / ONE_HOUR_MILLIS;
        // DstOffsetValue = dstOffset
        // I'm saving this is preferences for later use
        // save that now we are in DST mode
        if (timeZone.inDaylightTime(nowDate)) {
            Log.e(Utility.class.getName(),"in Daylight Time");
            return -(ONE_HOUR_MILLIS * dstOffset);
        } else {
            Log.e(Utility.class.getName(),"not in Daylight Time");
            return 0;
        }
    } else
        return 0;
}


根据这个答案:

1
2
TimeZone tz = TimeZone.getTimeZone("EST");
boolean inDs = tz.inDaylightTime(new Date());


在java中,默认情况下DateFormatter使用DST,为避免日光节约(DST),您需要手动执行操作,
首先你必须得到DST偏移,即应用了多少毫秒DST,对于某些地方,DST也是45分钟,有些地方是30分钟左右
但在大多数情况下,DST为1小时
您必须使用Timezone对象并检查日期是否属于DST,然后您必须手动将DST的偏移量添加到其中。例如:

1
2
3
4
5
6
7
8
9
 TimeZone tz = TimeZone.getTimeZone("EST");
 boolean isDST = tz.inDaylightTime(yourDateObj);
 if(isDST){
 int sec= tz.getDSTSavings()/1000;// for no. of seconds
 Calendar cal= Calendar.getInstance();
 cal.setTime(yourDateObj);
 cal.add(Calendar.Seconds,sec);
 System.out.println(cal.getTime());// your Date with DST neglected
  }

实现TimeZone类以将时区设置为日历可以节省夏令时。

java.util.TimeZone表示时区偏移量,并且还计算出夏令时。

示例代码:

1
2
3
TimeZone est_timeZone = TimeZoneIDProvider.getTimeZoneID(TimeZoneID.US_EASTERN).getTimeZone();
Calendar enteredCalendar = Calendar.getInstance();
enteredCalendar.setTimeZone(est_timeZone);