Vraag Waarom trekt deze twee keer (in 1927) een vreemd resultaat af?


Als ik het volgende programma gebruik, dat twee datumreeksen parseert, verwijst de referentie keer 1 seconde uit elkaar en vergelijkt ze:

public static void main(String[] args) throws ParseException {
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    String str3 = "1927-12-31 23:54:07";  
    String str4 = "1927-12-31 23:54:08";  
    Date sDt3 = sf.parse(str3);  
    Date sDt4 = sf.parse(str4);  
    long ld3 = sDt3.getTime() /1000;  
    long ld4 = sDt4.getTime() /1000;
    System.out.println(ld4-ld3);
}

De uitvoer is:

353

Waarom is ld4-ld3 niet 1 (zoals ik zou verwachten van het één-seconde verschil in de tijd), maar 353?

Als ik de datums in tijden 1 seconde later verander:

String str3 = "1927-12-31 23:54:08";  
String str4 = "1927-12-31 23:54:09";  

Dan ld4-ld3 zal zijn 1.


Java-versie:

java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode)

Timezone(`TimeZone.getDefault()`):

sun.util.calendar.ZoneInfo[id="Asia/Shanghai",
offset=28800000,dstSavings=0,
useDaylight=false,
transitions=19,
lastRule=null]

Locale(Locale.getDefault()): zh_CN

6015
2017-07-27 08:15


oorsprong


antwoorden:


Het is een wijziging in de tijdzone op 31 december in Shanghai.

Zien deze pagina voor details van 1927 in Shanghai. Kort na middernacht eind 1927 gingen de klokken 5 minuten en 52 seconden terug. Dus "1927-12-31 23:54:08" is eigenlijk twee keer gebeurd en het lijkt erop dat Java het als het ontleedt later mogelijk moment voor die lokale datum / tijd - vandaar het verschil.

Gewoon een nieuwe aflevering in de vaak vreemde en wondere wereld van tijdzones.

BEWERK: Stop met persen! Geschiedenis verandert ...

De oorspronkelijke vraag zou niet langer hetzelfde gedrag vertonen, indien opnieuw gebouwd met versie 2013a van TZDB. In 2013a zou het resultaat 358 seconden zijn, met een overgangstijd van 23:54:03 in plaats van 23:54:08.

Ik heb dit alleen opgemerkt omdat ik dergelijke vragen verzamel in Noda Time, in de vorm van unit tests... De test is nu veranderd, maar het gaat maar laten zien - zelfs historische gegevens zijn niet veilig.

BEWERK: De geschiedenis is weer veranderd ...

In TZDB 2014f is de tijd van de wijziging verplaatst naar 1900-12-31, en het is nu slechts een verandering van 343 seconden (dus de tijd tussen t en t+1 is 344 seconden, als je begrijpt wat ik bedoel).

BEWERK: Om een ​​vraag rond een transitie in 1900 te beantwoorden ... lijkt het erop dat de implementatie van de Java-tijdzone behandeld wordt alle tijdzones als gewoon in hun standaardtijd staan ​​voor elk moment vóór het begin van 1900 UTC:

import java.util.TimeZone;

public class Test {
    public static void main(String[] args) throws Exception {
        long startOf1900Utc = -2208988800000L;
        for (String id : TimeZone.getAvailableIDs()) {
            TimeZone zone = TimeZone.getTimeZone(id);
            if (zone.getRawOffset() != zone.getOffset(startOf1900Utc - 1)) {
                System.out.println(id);
            }
        }
    }
}

De bovenstaande code produceert geen uitvoer op mijn Windows-computer. Dus elke tijdzone met een andere offset dan de standaardzone aan het begin van 1900 zal dat als een overgang tellen. TZDB zelf heeft wat gegevens die eerder teruggaan dan dat, en vertrouwt niet op enig idee van een "vaste" standaardtijd (wat is wat getRawOffset veronderstelt een geldig concept te zijn) dus andere bibliotheken hoeven deze kunstmatige overgang niet te introduceren.


9719
2017-07-27 08:31



Je bent een lokale tijd discontinuïteit:

Toen de lokale standaardtijd op het punt stond om zondag 1 januari 1928 te bereiken,   00:00:00 klokken werden 0:05:52 uur tot zaterdag 31 omgedraaid.   December 1927, 23:54:08 lokale standaardtijd in plaats daarvan

Dit is niet bijzonder vreemd en is vrijwel overal tegelijkertijd gebeurd, omdat tijdzones zijn veranderd of veranderd als gevolg van politieke of administratieve acties.


1462
2017-07-27 08:38



De moraal van deze vreemdheid is:

  • Gebruik waar mogelijk datums en tijden in UTC.
  • Als u een datum of tijd niet in UTC kunt weergeven, geeft u altijd de tijdzone aan.
  • Als u geen invoerdatum / -tijd nodig hebt in UTC, hebt u een expliciet aangegeven tijdzone nodig.

579
2017-07-28 11:50



Bij het verhogen van de tijd moet u terug converteren naar UTC en vervolgens optellen of aftrekken. Gebruik de lokale tijd alleen voor weergave.

Op deze manier kunt u door alle perioden lopen waar uren of minuten twee keer gebeuren.

Als u bent geconverteerd naar UTC, voegt u elke seconde toe en converteert u naar lokale tijd voor weergave. Je zou om 11:54:08 uur gaan. LMT - 23:59:59 uur LMT en vervolgens 11:54:08 uur CST - 23:59:59 uur CST.


319
2017-07-30 16:55



In plaats van elke datum te converteren, gebruikt u de volgende code

long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);

En zie het resultaat is:

1

264
2018-05-16 05:31



Het spijt me dat ik dat moet zeggen, maar de tijd dat er discontinuïteit was, is een beetje veranderd

JDK 6 twee jaar geleden en in JDK 7 onlangs nog in update 25.

Les om te leren: vermijd kosteloos niet-UTC-tijden, behalve misschien voor weergave.


187
2018-02-17 22:44



Zoals door anderen is uitgelegd, is er een tijddiscontinuïteit daar. Er zijn twee mogelijke tijdzone verschuivingen voor 1927-12-31 23:54:08 op Asia/Shanghai, maar slechts één offset voor 1927-12-31 23:54:07. Dus, afhankelijk van welke offset wordt gebruikt, is er een verschil van één seconde of een verschil van 5 minuten en 53 seconden.

Deze kleine verschuiving van verschuivingen, in plaats van de gebruikelijke zomertijd van een uur die we gewend zijn, verduistert het probleem een ​​beetje.

Merk op dat de 2013a-update van de tijdzonedatabase deze discontinuïteit een paar seconden eerder opschuift, maar het effect zou nog steeds waarneembaar zijn.

De nieuwe java.time pakket op Java 8 laat het gebruik dit duidelijker zien en bieden hulpmiddelen om het aan te pakken. Gegeven:

DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
dtfb.appendLiteral(' ');
dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb.toFormatter();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");

String str3 = "1927-12-31 23:54:07";  
String str4 = "1927-12-31 23:54:08";  

ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);

Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());

Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());

Dan durationAtEarlierOffset zal een seconde zijn, terwijl durationAtLaterOffset zal vijf minuten en 53 seconden zijn.

Deze twee offsets zijn ook hetzelfde:

// Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();

Maar deze twee zijn verschillend:

// +08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();

// +08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();

Je kunt hetzelfde probleem vergelijken 1927-12-31 23:59:59 met 1928-01-01 00:00:00in dit geval is het echter de eerdere offset die de langere divergentie produceert, en het is de eerdere datum met twee mogelijke verschuivingen.

Een andere manier om dit te benaderen, is controleren of er een overgang gaande is. We kunnen dit als volgt doen:

// Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

U kunt controleren of de overgang een overlap is - in dat geval is er meer dan één geldige offset voor die datum / tijd - of een gat - in welk geval die datum / tijd niet geldig is voor die zone-ID - door de isOverlap() en isGap() methoden op zot4.

Ik hoop dat dit mensen helpt dit soort problemen aan te pakken zodra Java 8 algemeen beschikbaar wordt, of aan degenen die Java 7 gebruiken en de JSR 310 als backport gebruiken.


167
2018-01-03 14:43