简体   繁体   English

Java日历中的未来日期给出了一种奇怪的行为

[英]Future dates in java calendar are giving a strange behaviour

I have an application which I create dates that a user can select to an appointment. 我有一个应用程序,可以创建用户可以选择约会的日期。 If a user start to work at 9, and an appointment takes 2 hours, I create dates at 9, 11, 13... until a limit, of course. 如果用户从9点开始工作,并且约会需要2个小时,那么我将在9点,11点,13点创建日期,直到达到限制为止。 And then I change the day and start again. 然后我改变了一天,然后重新开始。 This is the code for doing this: 这是执行此操作的代码:

    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;
    }

I am working with the "America/Sao_Paulo" timezone to create these dates. 我正在使用“ America / Sao_Paulo”时区来创建这些日期。 I set the variables "initial" and "end" as "America/Sao_Paulo". 我将变量“ initial”和“ end”设置为“ America / Sao_Paulo”。 My system timezone is "GMT", and that is ok, because I want to save these dates in GMT in the database. 我的系统时区是“ GMT”,没关系,因为我想将这些日期保存在数据库的GMT中。 When I print the dates in last "for", magically it is already converted from "America/Sao_Paulo" to "GMT" and it is printing right. 当我在最后一个“ for”中打印日期时,已经神奇地将其从“ America / Sao_Paulo”转换为“ GMT”,并且打印正确。 The strange thing is that from a certain date, it changes the time zone. 奇怪的是,从某个日期开始,它会更改时区。 Example of prints: 打印示例:

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

While is in GMT, it is right, but I can't understand this BST. 虽然在格林尼治标准时间(GMT),这是正确的,但我无法理解此BST。 Can it be because it's too much in the future? 可能是因为将来太多了吗? It always starts on April. 它总是从四月开始。

Your system time isn't GMT, it's Europe/London (or something similar). 您的系统时间不是格林尼治标准时间,而是欧洲/伦敦(或类似时间)。 In March London time coincides with GMT. 3月,伦敦时间恰逢格林尼治标准时间。 Not in April. 不在四月。 That's why. 这就是为什么。

getWorkingHour() returns an instance of Date (another poorly designed and long outdated class, but let that be a different story for now). getWorkingHour()返回Date一个实例(另一个设计欠佳且过时的类,但现在让它变成一个不同的故事)。 When you append it to the empty string, Date.toString is implicitly called and builds the string using your system time zone. 当您将其附加到空字符串时, Date.toString被隐式调用,并使用您的系统时区构建该字符串。 During standard time it prints GMT as time zone abbreviation. 在标准时间,它将GMT打印为时区缩写。 Summer time (DST) begins in London on the last Sunday of March, in this case March 31. So in April Date.toString on your JVM uses British Summer Time and its abbreviation, BST for printing the time. 夏令时(DST)在3月的最后一个星期日,即3月31日,在伦敦开始。因此,在4月,JVM上的Date.toString使用英国夏令时及其缩写BST来打印时间。

The good solution involves two changes: 好的解决方案涉及两个更改:

  1. Don't rely on the JVM's default time zone. 不要依赖JVM的默认时区。 It can be changed at any time from another part of your program or another program running in the same JVM, so is too fragile. 可以随时从程序的另一部分或在同一JVM中运行的另一个程序更改它,这太脆弱了。 Instead give explicit time zone to your date-time operations. 而是为您的日期时间操作指定明确的时区。
  2. Skip the old date-time classes Calendar and Date and instead use java.time, the modern Java date and time API. 跳过旧的日期时间类CalendarDate ,而使用现代Java日期和时间API java.time。 It is so much nicer to work with and gives much clearer code, not least when it comes to conversions between time zones. 使用它会更好,代码也更清晰,尤其是在时区之间进行转换时。

Instead of Calendar use ZonedDateTime . 代替Calendar使用ZonedDateTime Depending on the capabilities of your JDBC driver, convert it to either Instant or OffsetDateTime in UTC for saving to the database. 根据JDBC驱动程序的功能,将其转换为UTC中的InstantOffsetDateTime以保存到数据库。

To create a ZonedDateTime , one option is to use one of its of methods (there are several): 要创建一个ZonedDateTime ,一个选择是使用它的一个of方法(有几种):

    ZonedDateTime initial = ZonedDateTime.of(2019, 3, 10, 9, 0, 0, 0, ZoneId.of("America/Sao_Paulo"));

This creates a date-time of March 10, 2019 at 09:00 in São Paolo. 这将在圣保罗创建一个2019年3月10日09:00的日期时间。 To add 2 hours to it: 要添加2小时:

    int appointmentDuration = 2;
    ZonedDateTime current = initial.plusHours(appointmentDuration);
    System.out.println(current);

Output: 输出:

2019-03-10T11:00-03:00[America/Sao_Paulo] 2019-03-10T11:00-03:00 [美国/圣保罗]

To convert to an Instant for your database: 要将数据库转换为Instant ,请执行以下操作:

    Instant inst = current.toInstant();
    System.out.println(inst);

Output: 输出:

2019-03-10T14:00:00Z 2019-03-10T14:00:00Z

Instants are time zone neutral, just a point in time, but print in UTC. 即时消息是时区中立的,只是一个时间点,但以UTC打印。 Some JDBC drivers accept them for UTC times. 一些JDBC驱动程序在UTC时间接受它们。 If yours doesn't happen to, you will need to give it an OffsetDateTime instead. 如果您没有碰巧,则需要给它一个OffsetDateTime Convert like this: 像这样转换:

    OffsetDateTime odt = current.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
    System.out.println(odt);

Output: 输出:

2019-03-10T14:00Z 2019-03-10T14:00Z

Note that I give UTC explicitly rather than relying on the JVM default. 请注意,我明确给出了UTC,而不是依赖JVM默​​认值。 So this is explicitly in UTC. 因此,这在UTC中是明确的。 You notice that the date and time agree with what was printed from the Instant . 您会注意到日期和时间与Instant所打印的内容一致。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM