簡體   English   中英

Java日歷中的未來日期給出了一種奇怪的行為

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

我有一個應用程序,可以創建用戶可以選擇約會的日期。 如果用戶從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。 可能是因為將來太多了嗎? 它總是從四月開始。

您的系統時間不是格林尼治標准時間,而是歐洲/倫敦(或類似時間)。 3月,倫敦時間恰逢格林尼治標准時間。 不在四月。 這就是為什么。

getWorkingHour()返回Date一個實例(另一個設計欠佳且過時的類,但現在讓它變成一個不同的故事)。 當您將其附加到空字符串時, Date.toString被隱式調用,並使用您的系統時區構建該字符串。 在標准時間,它將GMT打印為時區縮寫。 夏令時(DST)在3月的最后一個星期日,即3月31日,在倫敦開始。因此,在4月,JVM上的Date.toString使用英國夏令時及其縮寫BST來打印時間。

好的解決方案涉及兩個更改:

  1. 不要依賴JVM的默認時區。 可以隨時從程序的另一部分或在同一JVM中運行的另一個程序更改它,這太脆弱了。 而是為您的日期時間操作指定明確的時區。
  2. 跳過舊的日期時間類CalendarDate ,而使用現代Java日期和時間API java.time。 使用它會更好,代碼也更清晰,尤其是在時區之間進行轉換時。

代替Calendar使用ZonedDateTime 根據JDBC驅動程序的功能,將其轉換為UTC中的InstantOffsetDateTime以保存到數據庫。

要創建一個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所打印的內容一致。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM