menu

Showing posts with label DateTime. Show all posts
Showing posts with label DateTime. Show all posts

Wednesday, October 15, 2014

Using Date and Time with Zone and DST Offset Values In Java

Using Date and Time with Zone and DST Offset Values In JavaIntroduction:                                                                                                                                       
     In java applications, the preferred way of using Date and Time calculations is to use Calendar class or Joda library.When we use Calendar or Joda,since the Zone and DST offset values are automatically taken in the consideration, especially adding or subtracting Date or Time objects will be less error prone. However, since the Calendar and Joda library are more complex than standart Date class and the since the cost of creating these objects are much more heavier than creating standart Date class, in some situations we may want to do Date and Time calculation by ourselves. In this write we point out the tricks of using standart Date and Time objects.

1.) Zone difference when substracting Time and Date:
     As you know, Turkey is in GMT(Greenwich Mean Time) +2 Time Zone.When we use getTime() method of Date or Time class we get the time from "epoch"(01.01.1970) to now in millisecond. If we use this time to construct a new Date or Time object we get a result with added 2 hours since we are in +2 Time Zone. i.e;



SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SSS");
SimpleDateFormat sdfTime = new SimpleDateFormat("HH:mm:ss.SSSZ");
Date d = sdf.parse("01.01.2014 23:00:00.000");
Date d1 = sdf.parse("02.01.2014 01:00:00.000");
Date diff = new Date(d1.getTime() - d.getTime());
System.out.println("diff between two dates is: " + sdfTime.format(diff));


If we look at the output of this code fragment wee see 4 instead of 2.To solve this issue,either set GMT+0 to Formatter object like;
sdfTime.setTimeZone(TimeZone.getTimeZone("Etc/GMT+0"))
or substract 2 hours when constructing Date object like;
Date diff = new Date(d1.getTime()-d.getTime()-2*60*60*1000).

The same situation is also valid when using Calendar class, and we have to give GMT+0 time zone.

In addition the TimeZone can be given in the JVM level.
TimeZone.setDefault(TimeZone.getTimeZone("Etc/GMT+0"));

2.) DST (Daylight Saving Time):
     As we know, to utilize day light clocks are forwarded 1 hour at each spring and backwarded 1 hour at each autumn. i.e; at 2014 in turkey the clocks are forwarded 1 hour at 31 March 2014 Monday and they will be backwarded at 26 October 2014 Sunday. That means at 31 March 2014 the day will be 23 hours instead of 24 hours and at 26 October 2014 the day will be 25 hours. If we are using Date and Time classes instead of Calendar we have to consider this situations.Lets explain it with an example;

SimpleDateFormat sdfAmbigious = new SimpleDateFormat("dd.MM.yyyy");
Date twentySixOctober2014 = sdfAmbigious.parse("26.10.2014");
Date twentySevenOctober2014 = new Date(twentySixOctober2014 .getTime() + (24 * 60 * 60 *1000));
System.out.println("We would have been at the next day after adding 24 hours : " + sdfAmbigious.format(twentySevenOctober2014));
If we look at the output, although we add 24h we still in the 26th October.That's because 26th October is a 25h day. 
Similarly, at the 23h day, just by adding 23h we can go to the next day.

Lets consider we are doing critical day calculations and we let a process to be done only one time at each day.If we make the control with 24h, at a day with 25h we may accidentially let two processes(i.e; at 30th minutes and 24h 30th minutes). Similary, at a day with 23h, if we look the past 24h to check whether there is a process, we may get the process of yesterday, and we may accidentially return error to the user, although there is no process at 23h day.(i.e; if there is a process at 23:59 yesterday, if we let 24h later it will go the next day after the 23h day)

Note: In turkey since there is local elections at 30.03.2014 the clocks are forwarded 1 hour at 31.03.2014 instead of 30.03.2014. Since the JRE TimeZone normally is ruled to forward the clocks at 30.03.2014, if you don't do an update the applications will work like the clocks forwarded 1 hour at 30.03.2014(http://www.oracle.com/technetwork/java/javase/tzdata-versions-138805.html)
If you don't make the related java update, you have to add 1 hour for 30 March, and substract 1 hour at 31 March in your calculations.

Note: In time calculations the beginning of time is  01.01.1970. That is, the getTime() method of Date object will return a long value indicating a time value from 01.01.1970 to the time that the Date object represent. 01.01.1970 is generally known as "Epoch" or "Unix Time" and is firstly used in 32 bits Unix machine and standardized.Unix was developed at 1969 and the first version of it is issued at 1971.At that time epoch was choosen as 1970.With a 32 bits signed integer the maximum date that can be represented from 01.01.1970 is 19-01-2038.This case is known as 2038 problem and it will be experienced at 2038 in the machines that still using 32bits.

Note: In any case, at the applications that running on the web as client-server topology, its a must to check the Zone and DST configurations at each participating system.

Conclusion:
     Like in all programming languages,in java when dealing with Date and Time calculations, we have to carefully consider Zone and DST configurations.When we use Date and Time classes directly, we have to take care of this configurations or we may either left this job to the Calendar class (again have to give Time Zone with time calculations) or left it to JODA(http://www.joda.org/joda-time/) library. 







Monday, June 9, 2014

Java'da Date ve Time Kullanımında Zone ve DST Offset Değerleri

Java'da Date ve Time Kullanımında Zone ve DST Offset DeğerleriGiriş:     

     Java uygulamalarında Date ve Time kullanımında önerilen yöntem Calendar sınıfının kullanılması ya da Joda kütüphanesinin kullanılmasıdır. Calendar ve özellikle de Joda kullanıldığında Zone ve DST Offset değerleri otomatik dikkate alındığından özellikle 2 Date ya da 2 Time sınıfının farkı alındığı durumlarda hata şansı azalacaktır.Ancak Calendar sınıfının yada Joda kütüphanesinin standart Date sınıfına göre kompleks bir yapıya sahip olması ve  nesne oluşturma maliyeti göz önüne alındığında bazı durumlarda Date ve Time hesapmalarını kendimiz yapmak isteyebiliriz. Bu yazıda bu aşamada dikkat etmemiz gereken bazı noktalara değiniyor olacağız.

1.) Time ve Date farkını alırken Zone farkı:
     Bildiğiniz gibi Türkiye GMT(Greenwich Mean Time) +2 zaman dilimindedir. Direkt Date ya da Time sınıfının getTime() metodu ile "epoch" (01.01.1970) dan şu ana kadar geçen zamanı milisaniye cinsinden almış oluruz. Eğer bu zamanı kullanarak yeni bir Date ya da Time nesnesi oluşturursak bulunduğumuz zaman dilimi +2 olduğundan çıkan sonuca otomatik 2 saat eklenecek ve istediğimiz fark yerine 2 saat fazlasını bulmuş olacağız. Bu durumu aşağıdaki örnekte gözlemleyebiliriz.

SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SSS");
SimpleDateFormat sdfTime = new SimpleDateFormat("HH:mm:ss.SSSZ");
Date d = sdf.parse("01.01.2014 23:00:00.000");
Date d1 = sdf.parse("02.01.2014 01:00:00.000");
Date diff = new Date(d1.getTime() - d.getTime());
System.out.println("diff between two dates is: " + sdfTime.format(diff));

Bu programın çıktısına bakarsak beklenen sonuç olan 2 saat yerine 4 saat olduğunu görürüz. Bu sorunu çözmek için ya sdfTime.setTimeZone(TimeZone.getTimeZone("Etc/GMT+0")); kodu ile Formatter sınıfına GMT+0 zaman dilimini vermeli ya da Date nesnesini oluştururken 2 saati çıkararak işlem yapmalıyız.
Date diff = new Date(d1.getTime() - d.getTime() - 2 * 60 * 60 * 1000) . 

Bu durum Calendar sınıfı kullanıldığında da dikkate alınmalı ve Calendar nesnesine zaman dilimi olarak yine GMT+0 verilmelidir. Ayrıca TimeZone JVM seviyesinde de verilebilir.
TimeZone.setDefault(TimeZone.getTimeZone("Etc/GMT+0"));
2.) DST (Daylight Saving Time):
     Bilindiği üzere gün ışığından daha fazla yararlanmak adına her ilkbaharda saatler 1 saat ileri alınmakta ve her sonbaharda da 1 saat geri alınmaktadır.Örneğin 2014 senesinde Türkiye'de 31 Mart 2014 tarihinde saatler 1 saat ileri alındı ve 26 Ekim 2014 Pazar günü de 1 saat geri alınacak. Bu durumda 31 Mart 2014 günü 24 saat değil 23 saat olarak yaşanırken 26 Ekim 2014 günü 25 saat olarak yaşanacak. İşte bu noktada kritik saat hesaplamaları yapan uygulamalarda Calendar yerine direkt Date ya da Time sınıfları kullanılıyor ise DST zamanı dikkate alınmak durumundadır. Örnekle açıklarsak;

SimpleDateFormat sdfAmbigious = new SimpleDateFormat("dd.MM.yyyy");
Date yirmiAltiEkim2014 = sdfAmbigious.parse("26.10.2014");
Date yirmiYediEkim2014 = new Date(yirmiAltiEkim2014.getTime() + (24 * 60 * 60 *1000));
System.out.println("24 saat sonra ertesi güne geçmeliydik : " + sdfAmbigious.format(yirmiYediEkim2014 ));

Bu kodun çıktısına baktığımızda 26.10.2014 tarihine 24 saat eklememize rağmen günün yine 26.10.2014 olduğunu görürüz. Bunun sebebi 26.10.2014 tarihinin 25 saat yaşanacak olmasından dolayı, 24 saat eklendiğinde halen aynı gün içinde kalmasındandır.
Saatlerin ileriye alındığı tarihte ise 23 saat ekleyerek ertesi güne geçilmesi mümkün olacaktır.
Bu durumda kritik gün hesabı yaptığımız ve bir işlemin günde 1 kere yapılmasına izin verdiğimizi düşünelim.Eğer günde 1 kere kontrolünü 24 saat üzerinden yapıyorsak saatlerin 1 saat geriye alınıp  gün 25 saat olduğunda aynı gün 2 işlem yapılmasına izin vermiş olabiliriz(30. dakika da ve 24.üncü saat ve 30.dakika da 2 işlem gelmiş olsun). Aynı şekilde saatlerin 1 saat ileriye alındığı ve günün 23 saat olduğu günde de önceki işlem tarihine bağlı olarak 1 gün önceki işlemi görüp kullanıcıya o gün işlem girilmiş gibi hata dönebiliriz.(Önceki gün 23:59 da işlem girilmiş ise 24 saat sonrasına kadar işlem girişine izin vermediğimizde aradaki 23 saatlik günde hiç işlem girişi yapılamayacaktır.)


Not: Hatırlanacağı gibi yerel seçimler nedeniyle saatler 30.03.2014 tarihinde ileri alınması gerekirken 31.03.2014 tarihinde ileri alınmıştı. JRE TimeZone ayarı normalde 30.03.2014 e ayarlı olduğundan gerekli update yapılmaz ise uygulamalar 30.03.2014 tarihinde saatler ileri alınmış gibi çalışacaktır. (http://www.oracle.com/technetwork/java/javase/tzdata-versions-138805.html)
Update yapılmaz ise uygulama içerisinde 30 Mart için 1 saat ekleme ve 31 Mart için 1 saat çıkarma yapmak gerekecektir.


Not: Zaman hesaplamalarında zamanın başlangıcı  01.01.1970 dir. Yani bir Date nesnesinin getTime() metodu ile dönen long değeri aslında 01.01.1970 den Date nesnenin temsil ettiği zamana kadar olan değeri tutar.01.01.1970 değeri "Epoch" ya da "Unix Time" olarak bilinmektedir ve ilk defa 32 bit Unix makina da kullanılıp standartlaşmıştır.Unix 1969 da geliştirilmiş ve ilk versiyonu 1971 de yayınlanmıştır ve epoch olarak 1970 tarihi seçilmiştir. 32 bit signed integer ile 01.01.1970 den itibaren saydığımızda son gösterilebilecek tarih 2038-01-19 dir. Bu durum 2038 problemi olarak bilinmektedir ve 2038 de halen 32 bit işlemci kullanan makinalarda bu sorun gözlemlenecektir.  

Not: Her durumda web üzerinde çalışan Client-Server uygulamalarında karşılıklı konuşan sistemlerin Zone ve DST ayarlarını kontrol etmek faydalı olacaktır.

Sonuç:
     Diğer tüm programlama dillerinde olduğu gibi java'da da Date ve Time hesaplamalarında Zone, DST farklılıklarını dikkate almak gerekmektedir. Uygulamalarda direkt Date ya da Time sınıflarını kullandığımızda bu farklılıkları kendimiz ele almalıyız. Bu işi Calendar(saat hesaplamalarında time zone verilmelidir.) sınıfı yardımı ile yapabileceğimiz gibi JODA(http://www.joda.org/joda-time/) kütüphanesi kullanarak bu farklılıkların ele alınması işinin çoğunu JODA kütüphanesine de yükleyebiliriz.