Soru Neden iki kez (1927'de) tuhaf bir sonuç veriyorsunuz?


İki tarih dizisini birbirinden ayırma sürelerini 1 saniye olarak ayrıştıran ve karşılaştıran aşağıdaki programı çalıştırırsam:

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

Çıktı:

353

Neden ki ld4-ld3 değil 1 (zamanlardaki bir saniyelik farktan beklediğim gibi), ama 353?

Tarihleri ​​1 saniyeden sonra değiştirirseniz:

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

Sonra ld4-ld3 olacak 1.


Java sürümü:

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

6025
2017-07-27 08:15


Menşei


Gerçek hayattaki bir senaryodaki gerçek durumu gerçekten karıştırdınız mı ya da bu soru sadece bir bilinmez olmak mıydı - sadece eğlenmek için mi? - Costi Ciudatu
@Costi Ciudatu: FWIW, bunu daha büyük bir böceği azaltmanın sonucu olarak kolayca hayal edebiliyordum - yani, "Bu iki tarih tam olarak bir yıl değil, bir yıl arayla neden ayrılıyor?" - Brooks Moses
Asıl cevap, her zaman, Unix çağrısı gibi, günlüğe kaydetme için bir çağdan beri, her zaman saniyelerce kullanılır. Bu, 64 bit tamsayı gösterimi ile (çağlardan önce pullara izin vermek istiyorsanız) imzalanır. Herhangi bir gerçek dünya saat sistemi, gün sayısı veya gün ışığından yararlanma gibi doğrusal olmayan, monoton olmayan davranışlara sahiptir. - Phil H
orijinal olarak gönderildi Oracle Bug ID 7070044 23 Tem 11'de - Arno
@Costi Ciudatu gibi, hangi hata raporunun OP'yi kazandığını merak ediyorum. Ayrıca, bir bulmaca olmanın dışında, kullanıcıların yüzde 99,9'unu (9 basamaklı bir çok ekleyin) faydalı olmadığından eminim. "Oyun" ün için madalyayı alır. - Menelaos Bakopoulos


Cevaplar:


Şanghay'da 31 Aralık'ta bir saat dilimi değişikliği.

Görmek bu sayfa Şanghay'da 1927 detayları için. Temel olarak 1927'nin sonunda gece yarısı saat 5 dakika 52 saniyeye çıktı. Yani "1927-12-31 23:54:08" aslında iki kere oldu ve Java'nın bunu sonra Bu yerel tarih / saat için olası anlık - dolayısıyla fark.

Zaman dilimlerinin sık sık garip ve harika dünyasında sadece başka bir bölüm.

DÜZENLE: Basın durdur! Tarih değişir ...

Orijinal soru, 2013a sürümü ile yeniden oluşturulduğunda artık aynı davranışı göstermeyecektir. TZDB. 2013a'da sonuç, 23:54:08 yerine 23:54:03 bir geçiş süresiyle 358 saniye olacaktır.

Sadece bunu fark ettim çünkü Noda Zamanında böyle sorular soruyorum. birim testleri... Test şimdi değiştirildi, ama sadece göstermeye devam ediyor - tarihsel veriler bile güvenli değil.

DÜZENLE: Tarih tekrar değişti ...

TZDB 2014f'de, değişikliğin zamanı 1900-12-31'e geçti ve şu an sadece 343 saniyelik bir değişim oldu. t ve t+1 ne demek istediğimi görürsen, 344 saniyedir.

DÜZENLE: 1900’deki bir geçişle ilgili bir soruyu yanıtlamak için ... Java zaman dilimi uygulamasının davranışı gibi görünüyor herşey Saat dilimleri, standart saatlerinde 1900 UTC’nin başlangıcından önce herhangi bir an için geçerli olur:

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

Yukarıdaki kod, Windows makinemde çıktı üretmiyor. Dolayısıyla, 1900'ün başında standart olandan başka herhangi bir zaman dilimine sahip olan herhangi bir zaman dilimi bunu bir geçiş olarak sayacaktır. TZDB'nin kendisinden önceki bazı verileri vardır ve "sabit" standart bir zamana dair herhangi bir fikre dayanmamaktadır ( getRawOffset geçerli bir kavram olduğunu varsayar, bu yüzden diğer kütüphaneler bu yapay geçişi tanıtmaya ihtiyaç duymaz.


9729
2017-07-27 08:31



@Gareth: Hayır, ancak o dönemde Şanghay saat dilimi geçişlerinin ayrıntılarını kontrol etmek ilk çağrı limanımdı. Son zamanlarda Noda Time için zaman dilimi geçişleri üzerinde çalışıyorum, bu yüzden belirsizlik olasılığı zaten benim düşüncelerimin ön planda olduğunu ... - Jon Skeet
@Johannes: Dünya çapında daha normal bir zaman dilimi yapmak için inanıyorum - Ortaya çıkan ofset UTC + 8'dir. Paris 1911'de aynı şeyi yaptı, örneğin: timeanddate.com/worldclock/clockchange.html?n=195&year=1911 - Jon Skeet
@Charles: o zamanlar, gezginler yerel saatin her yerde farklı olmasını beklediklerini biliyordu (çünkü öyleydi). Buna ek olarak, saatler hızlı bir şekilde mekanik ve sürüklendi, bu yüzden insanlar seyahat etmeseler bile her iki günde bir yerel saat kulesine göre ayarladık. Öyleyse kule saatleri (aynı zamanda sürüklendi) nasıl kuruldu? Güneş, günlük zirveye ulaştığında saat 12: 00'a ayarlayarak en kolay şekilde ... aynı boyda olmayan her yerde farklıydı. Demiryolu tarifeleri bir çeşit standartlaşmaya ihtiyaç duyulana kadar, hemen hemen her yerde normdu. - Michael Borgwardt
Ama o zaman: Dünyada bu tür bir bilgi çağlardan nasıl kurtuldu, bu yüzden yaklaşık bir asır önce, yazılımda uygulandı? 2011 yılında, bir yazılım mühendisine zaman dilimi tuhaflıklarından bahseden herkes, bir inek gibi ele alındı. (Ve gerçekten, insanlar tüm yazılımların bunu soyutlamasını beklerler ve eğer 'öğlen' dedikleri zaman, eğer çok muğlaksa, umursamazlar. Biz yazılım mühendisi bununla ilgilenmeli). Ancak, 1927 yılının Aralık ayında, Shangai'deki birisini hayal etmek, böyle bir şeyi not etmekle ilgili olduğunu düşünmek ve bu bilginin bir şekilde hiçbir şekilde kaybolmadığını, silinmediğini, bir şeyin ... aklının uçurulduğunu düşünmek. - phtrivier
@EdS. Aslında sadece 15 dakika sürdü, çünkü 16 dakikalık bir farkın sebebi, 27 Temmuz 2011'de Jon Skeet'in ne kadar müthiş bir durumdan kaynaklanan hafif bir zaman dilimi farklılığından kaynaklanıyor. - JessieArr


Biriyle karşılaştınız yerel zaman süreksizliği:

Yerel standart zaman, 1 Ocak 1928’te Pazar günü sona ermek üzereyken,   00:00:00 saatler geriye döndü 0:05:52 saat 31'e, Cumartesi.   Aralık 1927, 23:54:08 yerine yerel standart saat

Bu özellikle tuhaf değildir ve politik veya idari eylemler nedeniyle zaman dilimleri olarak değiştiği veya değiştiği her yerde hemen hemen her yerde gerçekleşmiştir.


1463
2017-07-27 08:38



@Jason: Yatmadan önce okumak için, (şimdi) IANA zaman dilimi veri tabanını (daha önce Olson adında hoş bir adam tarafından yönetilen) harika bir kaynak olacağını öneriyorum: iana.org/time-zones. Bildiğim kadarıyla, açık kaynak dünyasının büyük bir çoğunluğu (söz konusu kütüphaneler) bunu ana zaman dilimi verileri kaynağı olarak kullanır. - Sune Rasmussen


Bu garipliğin ahlaki:

  • Mümkün olan yerlerde UTC’de tarihleri ​​ve saatleri kullanın.
  • UTC'de tarih veya saat gösteremiyorsanız, her zaman saat dilimini belirtin.
  • UTC'de bir giriş tarihi / saati gerektirmiyorsa, açıkça belirtilen bir zaman dilimi gerektirir.

580
2017-07-28 11:50



UTC'ye dönüştürme / depolama alanı, UTC'ye dönüşümdeki süreksizlik ile karşılaşacağınız şekilde açıklanan sorun için gerçekten yardımcı olmaz. - unpythonic
@Mark Mann: Eğer programınız UTC'yi her yerde dahili olarak kullanıyorsa, sadece UI'da yerel bir saat dilimine dönüştürebilirsiniz; bakım bu tür süreksizlikler hakkında. - Raedwald
@Raedwald: Tabii ki - 1927-12-31 23:54:08 için UTC zamanı nedir? (Şu an için, UTC'nin 1927'de bile bulunmadığı göz ardı edildi). at bazı Bu zaman ve tarihin sisteminize girdiğine dikkat edin ve bununla ne yapacağınıza karar vermelisiniz. Kullanıcıya UTC'de zaman girmek zorunda olduklarını söylemek problemi sadece kullanıcıya yönlendirir, onu ortadan kaldırmaz. - Nick Bastin
Yaklaşık bir yıldır büyük bir uygulamanın tarih / saat refactoring üzerinde çalışıyor olması, bu iş parçacığı üzerinde faaliyet miktarında haklı hissediyorum. Takvim gibi bir şey yapıyorsanız, UTC'yi "basitçe" kaydedemezsiniz, çünkü zaman içinde oluşturulabilecek zaman dilimlerinin tanımları zamanla değişecektir. "Kullanıcı amaç zamanı" - kullanıcının yerel saati ve saat dilimi - ve arama ve sıralama için UTC'yi saklıyoruz ve IANA veri tabanı güncellendiğinde, tüm UTC zamanlarını yeniden hesaplıyoruz. - taiganaut


Arttırma zamanı geldiğinde, UTC'ye geri dönmeli ve sonra ekleyiniz ya da çıkarmalısınız. Yerel saati sadece gösterim için kullanın.

Bu sayede saat ya da dakikaların iki kere olduğu herhangi bir dönemden geçebilirsiniz.

UTC'ye dönüştürdiyseniz, her saniye ekleyin ve görüntülemek için yerel saate dönüştürün. 11:54:08'den geçersin. LMT - 11:59:59. LMT ve sonra 11:54:08 öğleden sonra. CST - 11:59:59. CST.


320
2017-07-30 16:55





Her bir tarihi dönüştürmek yerine, aşağıdaki kodu kullanırsınız

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

Ve sonuç şu:

1

265
2018-05-16 05:31



Korkarım ki durum bu değil. Kodumu sisteminizde deneyebilirsiniz, çıktı olacak 1çünkü farklı yerlere sahibiz. - Freewind
Bu yalnızca doğrudur, çünkü ayrıştırıcı girdisinde yerel ayarı belirtmediniz. Bu, Java'da kötü kodlama stili ve devasa bir tasarım hatasıdır - doğal olarak yerelleştirilmesi. Şahsen, bunun için Java'yı kullandığım her yerde "TZ = UTC LC_ALL = C" koyuyorum. Ayrıca, bir kullanıcıyla doğrudan etkileşimde bulunmadığınız ve açıkça bunu istemediğiniz sürece, uygulamanın yerelleştirilmiş sürümlerinden kaçınmalısınız. Yerelleştirmeler dahil HERHANGİ BİR hesaplamalar yapmayın, kesinlikle gerekli olmadıkça her zaman Locale.ROOT ve UTC saat dilimlerini kullanın. - user1050755


Bunu söylediğim için üzgünüm, ama zaman sürekliliği biraz ilerledi.

JDK 6 iki yıl önce ve içinde JDK 7 yakın zamanda güncelleme 25.

Öğrenilecek ders: Görüntüleme için belki de hariç tüm maliyetlerde UTC olmayan zamanlardan kaçının.


188
2018-02-17 22:44



Bu yanlış. Süreksizlik bir hata değildir - sadece TZDB'nin daha yeni bir versiyonunun biraz farklı verileri vardır. Örneğin, Java 8 ile makinemde, kodu biraz değiştirirseniz "1927-12-31 23:54:02" ve "1927-12-31 23:54:03" ifadelerini kullanabilirsiniz. süreksizlik - ama 353 saniye yerine, 353 yerine. TZDB'nin daha yeni versiyonları da yine bir fark var - detaylar için cevabımı görün. Burada gerçek bir hata yok, sadece belirsiz tarih / zaman metin değerlerinin nasıl ayrıştırıldığı hakkında bir tasarım kararı. - Jon Skeet
Asıl sorun, programcıların yerel ve evrensel zaman (her iki yönde) arasındaki dönüşümün% 100 güvenilir olmadığını ve olmadığını anlamamasıdır. Eski zaman damgaları için, yerel saatte sahip olduğumuz veriler en iyi ihtimalle titriyor. Gelecekteki zaman damgaları için politik eylemler, belirli bir yerel zamanın hangi evrensel zamana eşleştiğini değiştirebilir. Güncel ve yakın geçmiş zaman damgaları için, tz veritabanını güncelleme ve değişiklikleri yayma işleminin yasaların uygulama zamanlamasından daha yavaş olabileceği sorununa sahip olabilirsiniz. - plugwash


Başkaları tarafından açıklandığı gibi, orada bir zaman süreksizliği var. İçin iki olası zaman dilimi ofsetleri vardır 1927-12-31 23:54:08 en Asia/Shanghaiama sadece bir tane 1927-12-31 23:54:07. Dolayısıyla, hangi ofsetin kullanıldığına bağlı olarak, bir saniye farkı veya 5 dakika ve 53 saniye farkı vardır.

Ofsetlerin bu ufak değişimi, alışılmış bir saatlik yaz saati yerine (yaz saati) alıştığımız problemi biraz bozar.

Zaman dilimi veritabanının 2013a güncellemesinin bu süreksizliği birkaç saniye önce değiştirdiğini, ancak etkinin yine de gözlemlenebilir olacağını unutmayın.

Yeni java.time Java 8 paketinde, bunu daha açık bir şekilde görebilmemiz ve işlemek için araçlar sunalım. Verilen:

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());

Sonra durationAtEarlierOffset bir saniye olacak durationAtLaterOffset beş dakika ve 53 saniye olacak.

Ayrıca, bu iki ofset aynıdır:

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

Ama bu ikisi farklı:

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

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

Aynı problemi karşılaştırarak görebilirsiniz 1927-12-31 23:59:59 ile 1928-01-01 00:00:00Bununla birlikte, bu durumda, daha uzun sapmayı üreten önceki sapmadır ve iki olası ofsete sahip olan önceki tarihtir.

Buna yaklaşmanın başka bir yolu, bir geçişin olup olmadığını kontrol etmektir. Bunu şöyle yapabiliriz:

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

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

Geçişin bir çakışma olup olmadığını kontrol edebilirsiniz - bu durumda o tarih / saat için birden fazla geçerli sapma veya bir boşluk - bu tarih / saat o bölge kimliği için geçerli değilse - isOverlap() ve isGap() üzerinde yöntemler zot4.

Umarım bu, Java 8 yaygın olarak kullanılabilir hale geldiğinde veya JSR 310 backport'u benimseyen Java 7'yi kullananlar için bu tür sorunları ele alır.


168
2018-01-03 14:43



Merhaba Daniel, kod parçanı çalıştırdım ama beklendiği gibi çıktı vermiyor. durationAtEarlierOffset ve durationAtLaterOffset gibi her ikisi de yalnızca 1 saniyedir ve ayrıca zot3 ve zot4 her ikisi de boştur. Bu kodu kopyalayıp makinemde çalıştırdım. Burada yapılması gereken bir şey var mı? Bir parça kod görmek istersen bana haber ver. İşte kod tutorialspoint.com/... Bana neler olduğunu bilmeme izin verir misin? - vineeshchauhan
@vineeshchauhan Bu Java'nın sürümüne bağlıdır, çünkü bu tzdata'da değişmiştir ve farklı JDK sürümleri tzdata'nın farklı sürümlerini paketlemektedir. Kendi kurulu Java'mda, zamanlar 1900-12-31 23:54:16 ve 1900-12-31 23:54:17Ancak, paylaştığınız sitede çalışmaz, bu yüzden, I'den farklı bir Java sürümü kullanıyorlar. - Daniel C. Sobral