JDK 1.0 ile beraber Date sınıfı java’ya kazandırılmıştı. Date sınıfında görülen eksiklikler sonrasında, bu eksikleri kapatmak için bir sonraki sürümde yani JDK 1.1 ile Calendar sınıfı geldi. Fakat bu tasarımların kusurları ve tutarsızlıkları nedeniyle, kurumsal uygulamaların çoğu JodaTime gibi third-party çözümleri kullanmak zorunda kalmıştır. Java’nın sürümleri ile ilgili detaylı yazıyı okumak için tıklayınız.
Java 8 ile beraber, eski tarih-zaman API’sinin (java.util.Date ve java.util.Calendar) dezavantajlarını karşılamak için yeni bir tarih-zaman API’si tanıtıldı. Bu yeni API, java.time paketi altında bulunur. Eğer JodaTime ile çalıştıysanız, öğrenmeniz gerçekten kolay olacaktır. Aslına bakarsanız, JodaTime’ı hiç duymamış insanların bile öğrenmesi kolay olacaktır; çünkü basit düşünülerek tasarlanmıştır.
Önce aşağıdaki kodlara bakıp geçmişi hatırlayalım ve sonra dezavantajlardan bahsedelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Date today = new Date(); System.out.println(today); // Wed Dec 05 21:42:48 EET 2018 // Varsayılan zaman dilimi içeriyor. Date elevenApril2018Date = new Date(118, 3, 11); System.out.println(elevenApril2018Date); // Wed Apr 11 00:00:00 EET 2018 // Ay 0'dan başlıyor ve yıl 1900'den başlıyor. zaman dilimi içeriyor. Calendar elevenApril2018Calendar = new GregorianCalendar(2018,3,11); System.out.println(elevenApril2018Calendar.getTime()); // Wed Apr 11 00:00:00 EET 2018 // Ay 0'dan başlıyor fakat yılı düzeltilmiş. zaman dilimi içeriyor. DateFormat ddMMyyySDF = new SimpleDateFormat("dd/MM/yyyy"); System.out.println(ddMMyyySDF.format(elevenApril2018Date)); // 11/04/2018 // zaman dilim içermemesi için formatlamak gerekiyor Fakat DateFormat thread-safe değildir. |
- Immutable ve Thread Safe – java.util.Date ve java.util.Calendar API’leri thread-safe değildir ve olası eşzamanlılık sorunlarına yol açmaktadır. (Immutable ile ilgili detaylı bilgiyi buradan okuyabilirsiniz.) Java 8’deki Tarih ve Zaman API’leri immutable‘dir ve bu nedenle thread-safe’dir.
- Uygun API tasarımı ve çeşitli yardımcı metotlar – java.util.Date ve java.util.Calendar API’leri yardımcı metotlar açısından epeyce eksiktir. Bu sebeplerden dolayı java 8 olmayan projelerimizde ayrıyeten bir DateUtil API’si yazılmıştır. Yeni Tarih / Zaman API’sı ISO-8601 merkezlidir ve çeşitli faydalı yöntemlere sahiptir.
- Zaman Dilimi – Daha önce java.util.Date ve java.util.Calendar API’leri ile farklı zaman dilimi (timezone) kullanmak çok zordu. Ayrıca zaman dilimlerine sıkı sıkı bağımlıydı. Java 8 ile beraber zaman dilimleri bağımsız oldular. İhtiyaç doğrultusunda rahatlıkla kullanabilirsiniz.
- Okunabilirlik – Java 8 tarih-zaman API’sinden önce, Java tarafından sağlanan yöntemlerin çoğunun okunabilirliği yoktu ve kullanımdan önce kodun altında yatan çalışma hakkında bilgiye ihtiyaç vardı.
- Kötü Tasarım – Date sınıfı tarih değil, timestamp’dir. Calendar ise tarih ve zamanın karışımıdır. Ayrıca yıl1900’den başlar, ay 1’den başlar ve gün 0’dan başlar.
ISO-8601 Standardı
ISO-8601, standart bir tarih ve saat sunma yöntemini kullanmak isteyen herkes tarafından kullanılabilir. Uluslararası iletişim kurarken belirsizlik ve karışıklığı ortadan kaldırmaya yardımcı olur. Detaylı bilgi için tıklayınız. Java da artık bu standardı kullanmaya başladı.
Java 8 : Date/Time API
LocalDate, LocalTime, LocalDateTime, Period ve Duration gibi yaygın olarak kullanılan tüm sınıflar java.time paketinin bir parçasıdır.
LocalDate
LocalDate sınıfı, ISO-8601 standardını kullanan ve zaman dilimi olmayan tarih sınıfıdır. Örneğin ; 2018-12-03.
1 2 3 4 5 6 7 8 9 10 11 |
LocalDate localDate1 = LocalDate.of(2018, 4, 11); LocalDate localDate2 = LocalDate.parse("2018-02-13"); LocalDate localDate3 = LocalDate.now(); LocalDate localDate4 = LocalDate.ofYearDay(2015, 100); System.out.println(localDate1.toString()); //2018-04-11 System.out.println(localDate1.getDayOfWeek().toString()); //WEDNESDAY System.out.println(localDate1.getDayOfMonth()); //11 System.out.println(localDate1.getDayOfYear()); //101 System.out.println(localDate1.isLeapYear()); //false System.out.println(localDate1.plusDays(12).toString()); //2018-04-23 |
LocalTime
LocalTime sınıfı, ISO-8601 standardını kullanan ve zaman dilimi olmayan zaman sınıfıdır.
Örneğin ; 10:15:30.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
LocalTime localTime1 = LocalTime.of(15, 30, 28); LocalTime localTime2 = LocalTime.now(); //toString() in format 09:57:59.744 LocalTime localTime3 = LocalTime.of(12, 20); LocalTime localTime4 = LocalTime.ofSecondOfDay(10160); LocalTime localTime5 = LocalTime.parse("06:30"); System.out.println(localTime1); // 15:30:28 System.out.println(localTime1.getHour()); // 15 System.out.println(localTime1.getMinute()); // 30 System.out.println(localTime1.getSecond()); // 28 System.out.println(localTime1.MIDNIGHT); // 00:00 System.out.println(localTime1.NOON); // 12:00 System.out.println("seconds of the day: " + localTime4); // seconds of the day: 02:49:20 |
LocalDateTime
LocalDateTime sınıfı, ISO-8601 standardını kullanan ve zaman dilimi olmayan tarih-zaman sınıfıdır. Örneğin ; 2018-05-15T10:01:14.911
1 2 3 4 5 6 7 8 9 |
LocalDateTime localDateTime1 = LocalDateTime.now(); LocalDateTime localDateTime2 = LocalDateTime.of(1987, Month.APRIL, 11, 14, 0, 0); LocalDateTime localDateTime3 = LocalDateTime.parse("2018-02-13T06:30"); LocalDateTime localDateTime4 = LocalDate.parse("2018-02-13").atTime(LocalTime.parse("06:30")); LocalDateTime localDateTime5 = LocalTime.parse("06:30").atDate(LocalDate.parse("2018-02-13")); LocalTime timeNow = LocalTime.now(); LocalDate dateToday = LocalDate.now(); LocalDateTime localDateTime6 = LocalDateTime.of(dateToday, timeNow); |
Zoned Date-Time API
Java 8 de, zaman dilimini belirli bir tarih ve saatle başa çıkmak için ZonedDateTime sınıfı vardır. ZoneId, farklı bölgeleri temsil etmek için kullanılan bir tanımlayıcıdır.
1 2 3 4 5 |
ZoneId currentZone = ZoneId.systemDefault(); // Asia/Istanbul ZoneId id = ZoneId.of("Asia/Istanbul"); ZonedDateTime zonedDateTime = ZonedDateTime.parse("2018-12-03T10:15:30+05:30[Asia/Istanbul]"); ZonedDateTime zonedDateTime1 = ZonedDateTime.now(); // 2018-12-08T10:20:45.063+03:00[Asia/Istanbul] Set<String> allZoneIds = ZoneId.getAvailableZoneIds(); |
Chrono Units Enum
Zaman birimlerini göstermek için java.time.temporal.ChronoUnit enum’u geldi. Bu enum içerisinde 100 yıl , 10 yıl , 1 yıl , 1 hafta gibi zaman birimleri bulunmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
LocalDate today = LocalDate.now(); LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS); System.out.println("1 hafta sonra : " + nextWeek); LocalDate nextMonth = today.plus(1, ChronoUnit.MONTHS); System.out.println("1 ay sonra : " + nextMonth); LocalDate nextYear = today.plus(1, ChronoUnit.YEARS); System.out.println("1 yıl sonra : " + nextYear); LocalDate nextDecade = today.plus(1, ChronoUnit.DECADES); System.out.println("10 yıl sonra : " + nextDecade); |
Period and Duration
Java 8 ile, iki tarih ve iki zaman arasındaki farkları hesaplamak için iki özel sınıf tanıtıldı.
- Period – Tarih bazlı aralık ile ilgilenir.
- Duration – Zaman bazlı aralık ile ilgilenir.
1 2 3 4 5 6 7 |
LocalDate currTime = LocalDate.now(); LocalDate dateAfter10Days = currTime.plus(Period.ofDays(10)); LocalDate dateBeforeWeek = currTime.minus(Period.ofWeeks(3)); Period periofDiff = Period.between(dateBeforeWeek, dateAfter10Days); //P1M1D System.out.println("day difference: " + periofDiff.getDays()); System.out.println("months difference: " + periofDiff.getMonths()); |
1 2 3 4 5 6 |
LocalTime givenTime = LocalTime.of(2, 30, 15); // 02:30:15 LocalTime updatedTime = givenTime.plus(Duration.ofMinutes(20)).plus(Duration.ofSeconds(30)); // 02:50:45 Duration drt = Duration.between(givenTime, updatedTime); // PT20M30S System.out.println("İki saat arasındaki saniye farkı : " + drt.getSeconds()); // 1230 System.out.println("iki saat arasındaki dakika farkı : " + drt.toMinutes()); // 20 |
Temporal Adjusters
TemporalAdjuster, tarih matematiğini yapmak için kullanılır. Örneğin, “Ayın İkinci Cumartesi” veya “Gelecek Salı” olsun. Hadi bir örnek yapalım.
1 2 3 4 5 6 7 8 9 |
LocalDate localDate = LocalDate.now(); // Bir sonraki ilk cumartesi LocalDate nextTuesday = localDate.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)); //Ayın ikinci cumartesisi LocalDate firstInYear = LocalDate.of(localDate.getYear(),localDate.getMonth(), 1); LocalDate secondSaturday = firstInYear.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY)) .with(TemporalAdjusters.next(DayOfWeek.SATURDAY)); |
Backward Compatibility (Geriye dönük uyumluluk)
Yeni Date-Time API’sine dönüştürmek için Date and Calendar sınıflarına toInstant() yöntemi eklendi. LocalDateTime veya ZonedDateTime nesnesi almak için ofInstant (Insant, ZoneId) yöntemini kullanabilirsiniz.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//Get the current date Date currentDate = new Date(); System.out.println("Current date: " + currentDate); //Get the instant of current date in terms of milliseconds Instant now = currentDate.toInstant(); ZoneId currentZone = ZoneId.systemDefault(); LocalDateTime localDateTime = LocalDateTime.ofInstant(now, currentZone); System.out.println("Local date: " + localDateTime); ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now, currentZone); System.out.println("Zoned date: " + zonedDateTime); |
DateTimeFormatter
İstenilen deseni kullanarak belirli bir tarihi ayrıştırmak mümkündür.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
LocalDate fromString = LocalDate.parse( "2014-01-20" ); LocalDate parsedFromPatern = LocalDate.parse( "2014/03/03", DateTimeFormatter.ofPattern( "yyyy/MM/dd" ) ); DateTimeFormatter builder = new DateTimeFormatterBuilder() .appendLiteral("Day of Month is :") .appendValue(ChronoField.DAY_OF_MONTH) .appendLiteral(", Month is :") .appendValue(ChronoField.MONTH_OF_YEAR) .appendLiteral(", Year is : ") .appendValue(ChronoField.YEAR) .appendLiteral(", Time is :") .appendValue(ChronoField.HOUR_OF_DAY) .appendLiteral(":") .appendText(ChronoField.MINUTE_OF_HOUR, TextStyle.FULL_STANDALONE) .parseCaseSensitive() .toFormatter(); LocalDateTime localDateTime = LocalDateTime.now(); String date = localDateTime.format(builder); System.out.println(date); |
Bu yazıda, tarihler ve zamanlar arasındaki farklılaşma ve yeni tarih / zaman API’sı tarafından sunulan özelliklerin ve olanakların çoğunu açıklamaya çalıştım. Ama daha yazamadığım birçok özelliği mevcuttur. Java dokümantasyonunu okuyarak, bir yandan da örnekler yaparak öğrendiklerinizi pekiştirebilirsiniz. Ayrıca yazdığım test kodlarını githubdan çekebilirsiniz.
Kaynaklar
- https://www.iso.org/iso-8601-date-and-time-format.html
- https://docs.oracle.com/javase/tutorial/datetime/TOC.html
- https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html
- https://docs.oracle.com/javase/8/docs/api/java/time/temporal/package-summary.html
- https://jcp.org/en/jsr/detail?id=310
ZonedDateTime kullanamıyorum. Api 26 dan sonrasını falan destekliyor. Zonet. Of now falan hata veriyor. İşin içinden bir türlü çıkamadım ltfen yardım. Tek istediğim telin saatini değil güncel saati alabilmek api 18 ve sonrası için.