我有一个应用程序,可以创建用户可以选择约会的日期。如果用户从9点开始工作,并且约会需要2个小时,那么我将在9点,11点,13点创建日期,直到达到限制为止。然后我改变了一天,然后重新开始。 这是执行此操作的代码:
public List<Agenda> createListOfDates(Calendar initial, Calendar end,
int appointmentDuration, int lunchTimeDuration, int lunchTimeStart) {
List<Agenda> agendaList = new ArrayList<Agenda>();
Agenda agenda = new Agenda();
agenda.setWorkingHour(initial.getTime());
agendaList.add(agenda);
while (true) {
initial.add(Calendar.HOUR_OF_DAY, appointmentDuration);
// Logger.error("" + initial.getTime());
if (initial.getTime().after(end.getTime())) {
break;
} else if (initial.get(Calendar.HOUR_OF_DAY) == lunchTimeStart
&& initial.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY
) {
initial.add(Calendar.HOUR_OF_DAY, lunchTimeDuration);
agenda = new Agenda();
agenda.setWorkingHour(initial.getTime());
agendaList.add(agenda);
} else {
agenda = new Agenda();
agenda.setWorkingHour(initial.getTime());
agendaList.add(agenda);
}
}
for(Agenda agendaX : agendaList){
Logger.info("" + agendaX.getWorkingHour());
}
return agendaList;
}
我正在使用“ America / Sao_Paulo”时区来创建这些日期。我将变量“ initial”和“ end”设置为“ America / Sao_Paulo”。我的系统时区是“ GMT”,没关系,因为我想将这些日期保存在数据库的GMT中。当我在最后一个“ for”中打印日期时,已经神奇地将其从“ America / Sao_Paulo”转换为“ GMT”,并且打印正确。奇怪的是,从某个日期开始,它会更改时区。打印示例:
Sat Mar 30 12:00:00 GMT 2019
Sat Mar 30 14:00:00 GMT 2019
Sat Mar 30 16:00:00 GMT 2019
Sat Mar 30 18:00:00 GMT 2019
Mon Apr 01 13:00:00 BST 2019
Mon Apr 01 15:00:00 BST 2019
Mon Apr 01 18:00:00 BST 2019
Mon Apr 01 20:00:00 BST 2019
Mon Apr 01 22:00:00 BST 2019
虽然在格林尼治标准时间(GMT),这是正确的,但是我不明白此BST。可能是因为将来太多了吗?它总是从四月开始。
答案 0 :(得分:3)
您的系统时间不是格林尼治标准时间,而是欧洲/伦敦(或类似时间)。 3月,伦敦时间恰逢格林尼治标准时间。不在四月。这就是为什么。
getWorkingHour()
返回Date
的实例(另一个设计欠佳且过时的类,但现在让它变成一个不同的故事)。当您将其附加到空字符串时,Date.toString
被隐式调用,并使用您的系统时区来构建字符串。在标准时间,它会打印GMT
作为时区缩写。夏令时(DST)在3月的最后一个星期日(在本例中为3月31日)在伦敦开始。因此,在4月,您的JVM上的Date.toString
使用英国夏令时及其缩写BST
来打印时间。
好的解决方案涉及两个更改:
Calendar
和Date
,而使用现代Java日期和时间API java.time。这样操作起来更好,代码也更清晰,尤其是在时区之间进行转换时。使用Calendar
代替ZonedDateTime
。根据JDBC驱动程序的功能,将其转换为UTC中的Instant
或OffsetDateTime
以保存到数据库。
要创建ZonedDateTime
,一种选择是使用其of
方法之一(有几种):
ZonedDateTime initial = ZonedDateTime.of(2019, 3, 10, 9, 0, 0, 0, ZoneId.of("America/Sao_Paulo"));
这将在圣保罗创建一个2019年3月10日09:00的日期时间。要添加2个小时:
int appointmentDuration = 2;
ZonedDateTime current = initial.plusHours(appointmentDuration);
System.out.println(current);
输出:
2019-03-10T11:00-03:00 [美国/圣保罗]
要为您的数据库转换为Instant
,请执行以下操作:
Instant inst = current.toInstant();
System.out.println(inst);
输出:
2019-03-10T14:00:00Z
即时消息是时区中立的,只是一个时间点,但以UTC打印。一些JDBC驱动程序在UTC时间接受它们。如果您的情况并非如此,则需要给它一个OffsetDateTime
。像这样转换:
OffsetDateTime odt = current.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
System.out.println(odt);
输出:
2019-03-10T14:00Z
请注意,我明确给出了UTC,而不是依赖JVM默认值。因此,这在UTC中是明确地。您会注意到日期和时间与Instant
上打印的内容一致。