30 Mart ve 1 Mart 2020 arasındaki fark neden 29 yerine 28 gün veriyor?


124
TimeUnit.DAYS.convert(
   Math.abs(
      new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse("30-03-2020 00:00:00").getTime() - 
      new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse("1-03-2020 00:00:00").getTime()
   ),
   TimeUnit.MILLISECONDS)

Sonuç 28, ancak 29 olmalıdır.

Sorun saat dilimi / yeri olabilir mi?


17
Not: SimpleDateFormatArtık kullanılmıyor, çünkü artık kullanılmıyor. java.timeBunun yerine paketleri kullanın. Bu SimpleDateFormatdurumda, kullanın DateTimeFormatter. Java 7 olması durumunda, aşağıdaki Andy Turner'ın yorumuna bakın.
MC İmparatoru

28
Do not kez matematik yapmak. Uygun bir zaman kitaplığı kullanın ( java.time[Java 7'de olduğunuzu not etsem de ], ThreeTenBp , Joda).
Andy Turner

14
Birisinin gittiği toplantıda yer almak isterdim "Tamam, şimdi zaman dilimlerimiz var,% 100 eşek moduna geçelim ve gün ışığından yararlanma denilen ve asit gezimden sonra bana bir rüyada gelen bu şeyi uygulayalım. dün gece."
MonkeyZeus

5
@gmauch TimeUnitDST hakkında hiçbir şey bilmiyormuş gibi davranmaz. Javadoc'un dediği gibi: Bir nanosaniye mikrosaniyenin binde biri, mikrosaniye milisaniyenin binde biri, saniyenin binde biri milisaniye, bir dakika altmış saniye, bir dakika altmış dakika ve bir gün olarak tanımlanır. yirmi dört saat . --- DST yılın 2 günü tam 24 saat olmamasına neden TimeUnitolduğundan, DST söz konusu olduğunda yanlış olur.
Andreas

1
Bu sorun, yalnızca gün ışığından yararlanma saatlerine sahip olan bilgisayarlarda oluşur. Yaz saati uygulaması olmayan saat dilimlerinde doğru gün sayısını (29) verir!
Gopinath

Yanıtlar:


207

Sorun, Yaz Saati Uygulaması vardiyası nedeniyle (8 Mart 2020 Pazar), bu tarihler arasında 28 gün 23 saat olması . TimeUnit.DAYS.convert(...) keser 28 güne sonucu.

Sorunu görmek için (ABD Doğu saat dilimindeyim):

SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
long diff = fmt.parse("30-03-2020 00:00:00").getTime() -
            fmt.parse("1-03-2020 00:00:00").getTime();

System.out.println(diff);
System.out.println("Days: " + TimeUnit.DAYS.convert(Math.abs(diff), TimeUnit.MILLISECONDS));
System.out.println("Hours: " + TimeUnit.HOURS.convert(Math.abs(diff), TimeUnit.MILLISECONDS));
System.out.println("Days: " + TimeUnit.HOURS.convert(Math.abs(diff), TimeUnit.MILLISECONDS) / 24.0);

Çıktı

2502000000
Days: 28
Hours: 695
Days: 28.958333333333332

Düzeltmek için DST olmayan bir saat dilimi kullanın, örn. UTC :

SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
long diff = fmt.parse("30-03-2020 00:00:00").getTime() -
            fmt.parse("1-03-2020 00:00:00").getTime();

Çıktı

2505600000
Days: 29
Hours: 696
Days: 29.0

121
"Düzeltmek" zamanlar ile matematik yapmayın. Uygun bir tarih / saat kütüphanesi kullanın.
Andy Turner

62
@AndyTurner Yerleşik Java 7 API'lerini kullanarak düzeltmek için. O yana olabilir sabitlenebilir, geçerli bir cevaptır gösteren. Birini tam bir kütüphane (Joda-Time, ThreeTen, vb.) Eklemeye zorlamak, sadece bu hesaplamayı yapmak için aşırıya kaçar. Tabii, bir kütüphane kullanılması tavsiye edilir, ancak gerekli değildir .
Andreas

38
Saygılarımla katılmıyorum. Bir hesaplama yapmak istiyorsanız, doğru yapın; doğru yapmak için maliyet ödemek.
Andy Turner

16
Benim € 0.02: Herhangi bir harici kütüphane olmadan Java 7'de yapmanın “doğru” yolu, bir GregorianCalendarnesne kullanmak ve bitiş tarihine ulaşılana kadar bir seferde 1 gün eklemek olacaktır. Bundan kaçınmak için yüksek bir bedel ödeyecektim. Ve zaten Java 8, 9, 10, 11, 12, 13,… 'nin parçası olan bir kütüphanenin backport'unu eklemek yüksek bir fiyat değildir. Aksine, bir dahaki sefere tarih veya saatle herhangi bir şey yapmanız gerektiğinde, zaten bir kazanç olacaktır.
Ole VV

27
UTC'de bile, Haziran'ın son günü bazen gerçek bir uyarı veya öngörülebilirlik olmadan bir saniye çok kısadır. Her zaman bir tarih-saat kütüphanesi kullanın.
Affe

41

Bu sorunun nedeni Andreas'ın cevabında zaten belirtilmiştir .

Soru tam olarak neyi saymak istediğinizdir. Gerçek farkın 28 yerine 29 olması gerektiğini ve "konum / bölge zamanının sorun olup olmayacağını " sormanız , gerçekten neyi saymak istediğinizi ortaya çıkarır. Görünüşe göre, herhangi bir saat dilimi farkından kurtulmak istiyorsunuz.

Sadece günleri zaman ve saat dilimi olmadan hesaplamak istediğinizi varsayıyorum.

Java 8

Aşağıda, arasındaki gün sayısının nasıl doğru bir şekilde hesaplanabileceğine dair bir örnekte, tam olarak bunu temsil eden bir sınıf kullanıyorum - zaman ve saat dilimi olmayan bir tarih - LocalDate.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d-MM-yyyy HH:mm:ss");
LocalDate start = LocalDate.parse("1-03-2020 00:00:00", formatter);
LocalDate end = LocalDate.parse("30-03-2020 00:00:00", formatter);

long daysBetween = ChronoUnit.DAYS.between(start, end);

Not ChronoUnit, DateTimeFormatterve LocalDateuygun sizin için kullanılamaz Java 8, en azından gerektiren etiketi. Ancak, belki de gelecekteki okuyucular içindir.

Ole VV tarafından belirtildiği gibi , Java 8 Tarih ve Saat API işlevini Java 6 ve 7'ye destekleyen ThreeTen Backport da var .


2
@ OleV.V. ThreeTen olduğunu biliyorum, bazı kullanıcılar birkaç kez bahsetmiş olabilir. (5772882 metnini içeren kullanıcının tüm yayınlarını ve yorumlarını döndüren bir SEDE sorgusuna bağlanmak üzereydim ThreeTen; ancak ne yazık ki yazma sırasında çevrimdışı.) Yazıyı güncelleyeceğim.
MC İmparatoru

2
@OleVV Hiç de kötü bir şey değil. Bence çoğu insan basitçe cahil java.time, çünkü okulda hala eski dersleri kullanıyorlar. Bir kayıp olurdu - Ama Java 8 Tarih ve Saat API çok iyi tasarlanmış değil kullanmak için.
MC İmparatoru
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.