如何将一天添加到 Calendar 对象并考虑夏令时?

How do I add one day to a Calendar object and also account for daylight savings time?

我正在尝试为列表中的对象创建时间限制。这可能意味着物品的保质期可能为 23、24 或 25 小时。有没有 Java 有用的库?这是我目前所拥有的。

我的问题是,例如,当我在 9:30 上午创建一条记录时,它必须在第二天的 9:30 上午删除。在 DST 生效的日子里,我会遇到差异。记录是在 8:30 或 10:30 处删除,具体取决于我是向前还是向后 spring。

//baseValue = object that I want to check

Date dt = new Date();
Calendar c = Calendar.getInstance(); 
c.setTime(dt); 
c.add(Calendar.DATE, -1);

if(baseValue.getTime() < c.getTime()){
    array.remove(baseValue);
}

旧的 classes(DateCalendarSimpleDateFormat)具有 lots of problems and design issues,包括难以处理 DST 更改,并且它们'正在被新的 API 取代。

如果您正在使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.

如果您使用 Java <= 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it ).

下面的代码适用于两者。 唯一的区别是 包名称 (在 Java 8 中是 java.time 而在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但是 classes 和方法 names 是相同的。


为了处理 DST 变化,理想的 class 是 ZonedDateTime,它代表特定时区的日期和时间。我还使用 ZoneId class,它代表时区本身。

我正在使用我的时区 (America/Sao_Paulo),因为这里我们也有夏令时,但您可以用您的时区替换(更多内容见下文):

// create a date 1 day before DST change in Sao Paulo, at 9 AM
ZoneId zone = ZoneId.of("America/Sao_Paulo");
ZonedDateTime z = ZonedDateTime.of(2017, 10, 14, 9, 0, 0, 0, zone);

// get the next day, at 9 AM
ZonedDateTime nextDay = z.plusDays(1);

System.out.println(z);
System.out.println(nextDay);

输出为:

2017-10-14T09:00-03:00[America/Sao_Paulo]
2017-10-15T09:00-02:00[America/Sao_Paulo]

请注意,偏移量从 -03:00 更改为 -02:00 - 这是由于 DST 从圣保罗时区开始(时钟向前移动 1 小时)。但还要注意时间(上午 9 点)已正确保存。

如果我们计算小时差,我们可以看到它是正确的:

System.out.println(ChronoUnit.HOURS.between(z, nextDay));

输出为:

23

正确的意思是这两个日期之间已经过去了 23 小时(因为时钟向前移动了 1 小时,所以 1 小时是 "lost")。


在你的情况下,你需要知道是否已经过了 1 天,所以你只需调用:

long days = ChronoUnit.DAYS.between(z, nextDay);

在这种情况下,days 将为 1(即使上面计算的小时差为 23,因为 API 足够聪明,可以考虑夏令时的影响)。

因此,在您的情况下,您只需要检查天数差异是否为 1(或大于 1,我不知道)并执行所有需要完成的操作。

如果需要获取当前date/time,可以调用ZonedDateTime.now(zone).


要使用您的时区而不是我的时区,首先请注意 API 使用 IANA timezones names(始终采用 Continent/City 格式,例如 America/Sao_PauloEurope/Berlin). 避免使用 3 个字母的缩写(如 CSTPST),因为它们是 ambiguous and not standard.

您可以使用 ZoneId.getAvailableZoneIds() 获取时区名称列表 - 然后选择最适合您的情况。

您也可以使用 ZoneId.systemDefault() - 它 returns 系统的默认时区。但这可以在不通知的情况下更改 - 即使在运行时 - 因此建议使用明确的时区。