2017-02-22 4 views
1

ich mit "2016-03-28T02:00:00+0200" mich entschiedenErsten falsches Ergebnis nach einem Tag von einem Datum Mit Local und ZonedDateTime Subtrahierend

1 Tag Nach Abzug wird DST angewandt und ausgegeben werden soll:

"2016-03-27T03: 00: 00 + 0200"

Aber ich bin immer dies:

2016-03-26T01: 00 + 01: 00 [Europa/Stockholm]

Code:

public class DateFormatSampleCode { 
    public static void main(String[] args) 
    { 
     LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(1459123200, 0, ZoneOffset.UTC); 

     System.out.println(localDateTime); 
     localDateTime = localDateTime.minusDays(1); 
     System.out.println(localDateTime); 

     ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of("Europe/Stockholm")); 

     System.out.println(zonedDateTime); 
    } 
} 

Bitte überprüfen und darauf hinweisen, wo ich falsch werde.

+1

Sie können einen Tag nicht von einem * lokalen * Datum/Uhrzeit, das keine Sommerzeiteinsparungen erwartet, subtrahieren und erwarten, dass Sommerzeit-Regeln angewendet werden. – RealSkeptic

+0

@RealSkeptic Können Sie vorschlagen, wie Sie es schaffen? –

+0

Wenn Sie Datum/Uhrzeit in Zonen einteilen möchten, verwenden Sie "ZonedDateTime". Verwenden Sie nicht 'LocalDateTime'. – RealSkeptic

Antwort

1

Ich denke, ich kann meine obige Frage beantworten.

Hier ist der Code.

public ZonedDateTime addDays(long myUTCTimeInSeconds, int days) { 
    Instant instant = Instant.ofEpochSecond(myUTCTimeInSeconds); 
    ZonedDateTime dateTimeWithOffSet = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault()); 
    if (localDays >= 0) { 
     dateTimeWithOffSet = dateTimeWithOffSet.plusDays(localDays); 
    } else { 
     dateTimeWithOffSet = dateTimeWithOffSet.minusDays(abs(localDays)); 
    } 
    return dateTimeWithOffSet; 
} 

Wenn die Zeitzone von dem System unterscheidet, können wir die Standardzeitzone und nach oben Aufruf der Methode der Zeitzone zurückgesetzt, wie:

TimeZone systemDefaultTimeZone = TimeZone.getDefault(); 
TimeZone.setDefault(TimeZone.getTimeZone(timezone)); 

addDays(1459123200, 1); 
TimeZone.setDefault(systemDefaultTimeZone); 
0

Gut Sie eine Lösung gefunden, würde ich nur Ich möchte einige Einsichten hinzufügen und eine kleine Verbesserung für your answer vorschlagen.

Das Festlegen der JVM-Standardzeitzone mit TimeZone.setDefault ist nicht der beste Weg, dies zu erreichen. Obwohl es für die meisten Zeiten funktioniert, ist es ein wenig riskant und fehleranfällig, wenn Sie bedenken, dass dieser Code in einer komplexeren Umgebung ausgeführt wird.

Das ist, weil TimeZone.setDefault die Standardzeitzone für die ganze JVM ändert. Jede andere Anwendung, die in derselben JVM ausgeführt wird, wird davon betroffen sein. Andere Teile derselben Anwendung sind ebenfalls betroffen, und selbst derselbe Code, der in mehreren Threads ausgeführt wird, kann zu falschen Ergebnissen führen (und race conditions are hard to debug).

Ich habe festgestellt, dass Sie TimeZone.setDefault(TimeZone.getTimeZone(timezone)); verwenden. Dies bedeutet, dass Sie bereits mit einer bestimmten Zeitzone arbeiten, sodass Sie sich nicht auf die Standardeinstellung der JVM verlassen müssen. Wenn Sie einen bestimmten Zeitzonennamen haben, verwenden Sie diesen anstelle des Standardnamens. So empfehle ich Ihnen, dass die addDays Methode wie folgt sein sollte:

public ZonedDateTime addDays(long myUTCTimeInSeconds, int days, String timezone) { 
    // get the instant from the UTC seconds 
    Instant instant = Instant.ofEpochSecond(myUTCTimeInSeconds); 
    // get the instant at the specified timezone 
    ZonedDateTime z = instant.atZone(ZoneId.of(timezone)); 

    // add days 
    return z.plusDays(days); 
} 

Die Verbesserungen vorgenommen:

  • plusDays bereits 1 Tag subtrahiert, wenn Sie -1 es passieren. Sie müssen den Wert nicht überprüfen und die Methode abs verwenden.
  • verwenden die JVM Standard-Zeitzone nicht: statt ZoneId.systemDefault(), verwenden Sie die timezone, die Sie bereits haben (die Sie in der setDefault Methode wurden)
  • instant.atZone ist ZonedDateTime.ofInstant gleichwertig.IMO, atZone ist mehr "lesbar", aber in diesem Fall ist es eine Frage der Wahl und Code-Stil. Es macht keinen Unterschied im Endergebnis.

Damit könnten Sie tun:

// call directly, no need to change the default timezone 
System.out.println(addDays(1459123200, -1, "Europe/Stockholm")); 

Dieser Druck wird:

2016-03-27T03: 00 + 02: 00 [Europa/Stockholm]

Verwandte Themen