如何将一天添加到 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(Date
、Calendar
和 SimpleDateFormat
)具有 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_Paulo
或 Europe/Berlin
).
避免使用 3 个字母的缩写(如 CST
或 PST
),因为它们是 ambiguous and not standard.
您可以使用 ZoneId.getAvailableZoneIds()
获取时区名称列表 - 然后选择最适合您的情况。
您也可以使用 ZoneId.systemDefault()
- 它 returns 系统的默认时区。但这可以在不通知的情况下更改 - 即使在运行时 - 因此建议使用明确的时区。
我正在尝试为列表中的对象创建时间限制。这可能意味着物品的保质期可能为 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(Date
、Calendar
和 SimpleDateFormat
)具有 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_Paulo
或 Europe/Berlin
).
避免使用 3 个字母的缩写(如 CST
或 PST
),因为它们是 ambiguous and not standard.
您可以使用 ZoneId.getAvailableZoneIds()
获取时区名称列表 - 然后选择最适合您的情况。
您也可以使用 ZoneId.systemDefault()
- 它 returns 系统的默认时区。但这可以在不通知的情况下更改 - 即使在运行时 - 因此建议使用明确的时区。