Pengantar Joda-Time

1. Perkenalan

Joda-Time adalah perpustakaan pemrosesan tanggal dan waktu yang paling banyak digunakan, sebelum rilis Java 8. Tujuannya adalah untuk menawarkan API intuitif untuk tanggal dan waktu pemrosesan dan juga mengatasi masalah desain yang ada di Java Date / Time API.

Konsep sentral yang diimplementasikan di pustaka ini diperkenalkan di inti JDK dengan dirilisnya versi Java 8. API tanggal dan waktu baru ditemukan di paket java.time (JSR-310). Ringkasan fitur-fitur ini dapat ditemukan di artikel ini.

Setelah rilis Java 8, penulis menganggap proyek sebagian besar telah selesai dan menyarankan untuk menggunakan API Java 8 jika memungkinkan.

2. Mengapa Menggunakan Joda-Time?

API tanggal / waktu, sebelum Java 8, menghadirkan banyak masalah desain.

Di antara masalahnya adalah fakta bahwa class Date dan SimpleDateFormatter tidak aman untuk thread. Untuk mengatasi masalah ini, Joda-Time menggunakan kelas yang tidak dapat diubah untuk menangani tanggal dan waktu.

Kelas Tanggal tidak mewakili tanggal sebenarnya, melainkan menetapkan waktu instan, dengan presisi milidetik. Tahun dalam Tanggal dimulai dari 1900, sedangkan sebagian besar operasi tanggal biasanya menggunakan waktu Epoch yang dimulai dari 1 Januari 1970.

Juga, offset hari, bulan dan tahun dari suatu Tanggal berlawanan dengan intuisi. Hari dimulai dari 0, sedangkan bulan dimulai dari 1. Untuk mengaksesnya, kita harus menggunakan kelas Kalender . Joda-Time menawarkan API yang bersih dan lancar untuk menangani tanggal dan waktu.

Joda-Time juga menawarkan dukungan untuk delapan sistem kalender , sedangkan Java hanya menawarkan 2: Gregorian - java.util.GregorianCalendar dan Jepang - java.util.JapaneseImperialCalendar .

3. Penyiapan

Untuk menyertakan fungsionalitas pustaka Joda-Time, kita perlu menambahkan dependensi berikut dari Maven Central:

 joda-time joda-time 2.10 

4. Sekilas Perpustakaan

Joda-Time memodelkan konsep tanggal dan waktu menggunakan kelas-kelas dalam paket org.joda.time .

Di antara kelas-kelas itu yang paling umum digunakan adalah:

  • LocalDate - mewakili tanggal tanpa waktu
  • LocalTime - mewakili waktu tanpa zona waktu
  • LocalDateTime - mewakili tanggal dan waktu tanpa zona waktu
  • Instan - mewakili titik waktu yang tepat dalam milidetik dari periode Java 1970-01-01T00: 00: 00Z
  • Durasi - mewakili durasi dalam milidetik antara 2 titik waktu
  • Periode - mirip dengan Durasi , tetapi memungkinkan akses ke komponen individu dari objek tanggal dan waktu, seperti tahun, bulan, hari, dll.
  • Interval - mewakili interval waktu antara 2 momen

Fitur penting lainnya adalah pengurai dan pemformat tanggal . Ini dapat ditemukan di paket org.joda.time.format .

The sistem kalender dan zona waktu kelas tertentu dapat ditemukan di org.joda.time.chrono dan org.joda.time.tz paket.

Mari kita lihat beberapa contoh di mana kami menggunakan fitur utama Joda-Time untuk menangani tanggal dan waktu.

5. Mewakili Tanggal dan Waktu

5.1. Tanggal dan Waktu Saat Ini

Tanggal saat ini, tanpa informasi waktu, dapat diperoleh dengan menggunakan sekarang () metode dari yang LOCALDATE kelas :

LocalDate currentDate = LocalDate.now();

Ketika kita hanya membutuhkan waktu saat ini, tanpa informasi tanggal, kita dapat menggunakan kelas LocalTime :

LocalTime currentTime = LocalTime.now();

Untuk mendapatkan representasi dari tanggal dan waktu saat ini tanpa mempertimbangkan zona waktu, kita dapat menggunakan LocalDateTime :

LocalDateTime currentDateAndTime = LocalDateTime.now();

Sekarang, dengan menggunakan currentDateAndTime , kita dapat mengonversinya ke jenis tanggal dan waktu pemodelan objek.

Kita bisa mendapatkan objek DateTime (yang memperhitungkan zona waktu) dengan menggunakan metode toDateTime () . Ketika waktu tidak diperlukan, kita dapat mengubahnya menjadi LocalDate dengan metode toLocalDate () , dan ketika kita hanya membutuhkan waktu, kita dapat menggunakan toLocalTime () untuk mendapatkan objek LocalTime :

DateTime dateTime = currentDateAndTime.toDateTime(); LocalDate localDate = currentDateAndTime.toLocalDate(); LocalTime localTime = currentDateAndTime.toLocalTime();

Semua metode di atas memiliki metode kelebihan beban yang menerima objek DateTimeZone untuk membantu kami mewakili tanggal atau waktu dalam zona waktu yang ditentukan:

LocalDate currentDate = LocalDate.now(DateTimeZone.forID("America/Chicago"));

Selain itu, Joda-Time menawarkan integrasi yang sangat baik dengan Java Date and Time API. Konstruktor menerima objek java.util.Date dan juga, kita bisa menggunakan metode toDate () untuk mengembalikan objek java.util.Date :

LocalDateTime currentDateTimeFromJavaDate = new LocalDateTime(new Date()); Date currentJavaDate = currentDateTimeFromJavaDate.toDate();

5.2. Tanggal dan Waktu Kustom

Untuk merepresentasikan tanggal dan waktu kustom, Joda-Time memberi kita beberapa konstruktor. Kita dapat menentukan objek berikut:

  • sebuah Instan
  • objek Tanggal Jawa
  • a String representasi dari tanggal dan waktu menggunakan format ISO
  • bagian dari tanggal dan waktu: tahun, bulan, hari, jam, menit, detik, milidetik
Date oneMinuteAgoDate = new Date(System.currentTimeMillis() - (60 * 1000)); Instant oneMinutesAgoInstant = new Instant(oneMinuteAgoDate); DateTime customDateTimeFromInstant = new DateTime(oneMinutesAgoInstant); DateTime customDateTimeFromJavaDate = new DateTime(oneMinuteAgoDate); DateTime customDateTimeFromString = new DateTime("2018-05-05T10:11:12.123"); DateTime customDateTimeFromParts = new DateTime(2018, 5, 5, 10, 11, 12, 123); 

Cara lain kita dapat menentukan tanggal dan waktu khusus adalah dengan mem-parsing representasi String yang diberikan dari tanggal dan waktu dalam format ISO:

DateTime parsedDateTime = DateTime.parse("2018-05-05T10:11:12.123");

Kita juga dapat mengurai representasi kustom dari tanggal dan waktu dengan menentukan DateTimeFormatter kustom :

DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss"); DateTime parsedDateTimeUsingFormatter = DateTime.parse("05/05/2018 10:11:12", dateTimeFormatter);

6. Bekerja dengan Tanggal dan Waktu

6.1. Menggunakan Instan

Sebuah Instan mewakili jumlah milidetik dari 1970-01-01T00: 00: 00Z sampai saat tertentu dalam waktu. Misalnya, momen saat ini dalam waktu dapat diperoleh menggunakan konstruktor default atau metode now () :

Instant instant = new Instant(); Instant.now();

To create an Instant for a custom moment in time we can use either one of the constructors or use the methods ofEpochMilli() and ofEpochSecond():

Instant instantFromEpochMilli = Instant.ofEpochMilli(milliesFromEpochTime); Instant instantFromEpocSeconds = Instant.ofEpochSecond(secondsFromEpochTime);

The constructors accept a String representing a date and time in the ISO format, a Java Date or a long value representing the number of milliseconds from 1970-01-01T00:00:00Z:

Instant instantFromString = new Instant("2018-05-05T10:11:12"); Instant instantFromDate = new Instant(oneMinuteAgoDate); Instant instantFromTimestamp = new Instant(System.currentTimeMillis() - (60 * 1000));

When date and time are represented as a String we have the option to parse the String using our desired format:

Instant parsedInstant = Instant.parse("05/05/2018 10:11:12", dateTimeFormatter);

Now that we know what Instant represents and how we can create one, let's see how it can be used.

To compare to Instant objects we can use compareTo() because it implements the Comparable interface, but also we can use the Joda-Time API methods provided in the ReadableInstant interface which Instant also implements:

assertTrue(instantNow.compareTo(oneMinuteAgoInstant) > 0); assertTrue(instantNow.isAfter(oneMinuteAgoInstant)); assertTrue(oneMinuteAgoInstant.isBefore(instantNow)); assertTrue(oneMinuteAgoInstant.isBeforeNow()); assertFalse(oneMinuteAgoInstant.isEqual(instantNow));

Another helpful feature is that Instant can be converted to a DateTime object or event a Java Date:

DateTime dateTimeFromInstant = instant.toDateTime(); Date javaDateFromInstant = instant.toDate();

When we need to access parts of a date and time, like the year, the hour and so on, we can use the get() method and specify a DateTimeField:

int year = instant.get(DateTimeFieldType.year()); int month = instant.get(DateTimeFieldType.monthOfYear()); int day = instant.get(DateTimeFieldType.dayOfMonth()); int hour = instant.get(DateTimeFieldType.hourOfDay());

Now that we covered the Instant class let's see some examples of how we can use Duration, Period and Interval.

6.2. Using Duration, Period and Interval

A Duration represents the time in milliseconds between two points in time or in this case it could be two Instants. We'll use this when we need to add or subtract a specific amount of time to or from another Instant without considering chronology and time zones:

long currentTimestamp = System.currentTimeMillis(); long oneHourAgo = currentTimestamp - 24*60*1000; Duration duration = new Duration(oneHourAgo, currentTimestamp); Instant.now().plus(duration);

Also, we can determine how many days, hours, minutes, seconds or milliseconds the duration represents:

long durationInDays = duration.getStandardDays(); long durationInHours = duration.getStandardHours(); long durationInMinutes = duration.getStandardMinutes(); long durationInSeconds = duration.getStandardSeconds(); long durationInMilli = duration.getMillis();

The main difference between Period and Duration is that Period is defined in terms of its date and time components (years, months, hours, etc.) and doesn't represent an exact number of milliseconds. When using Period date and time calculations will consider the time zone and daylight saving.

For example, adding a Period of 1 month to the February 1st will result in the date representation of March 1st. By using Period the library will take into account leap years.

If we're to use a Duration we the result wouldn't be correct, because the Duration represents a fixed amount of time that doesn't take into account chronology or time zones:

Period period = new Period().withMonths(1); LocalDateTime datePlusPeriod = localDateTime.plus(period);

An Interval, as the name states, represents the date and time interval between two fixed points in time represented by two Instant objects:

Interval interval = new Interval(oneMinuteAgoInstant, instantNow);

The class is useful when we need to check whether two intervals overlap or calculate the gap between them. The overlap() method will return the overlapping Interval or null when they don't overlap:

Instant startInterval1 = new Instant("2018-05-05T09:00:00.000"); Instant endInterval1 = new Instant("2018-05-05T11:00:00.000"); Interval interval1 = new Interval(startInterval1, endInterval1); Instant startInterval2 = new Instant("2018-05-05T10:00:00.000"); Instant endInterval2 = new Instant("2018-05-05T11:00:00.000"); Interval interval2 = new Interval(startInterval2, endInterval2); Interval overlappingInterval = interval1.overlap(interval2);

The difference between intervals can be calculated using the gap() method, and when we want to know whether the end of an interval is equal to the start of another interval we can use the abuts() method:

assertTrue(interval1.abuts(new Interval( new Instant("2018-05-05T11:00:00.000"), new Instant("2018-05-05T13:00:00.000"))));

6.3. Date and Time Operations

Some of the most common operations are to add, subtract and convert date and time. The library provides specific methods for each of the classes LocalDate, LocalTime, LocalDateTime, and DateTime. It's important to note that these classes are immutable so that every method invocation will create a new object of its type.

Let's take LocalDateTime for the current moment and try to change its value:

LocalDateTime currentLocalDateTime = LocalDateTime.now();

To add an extra day to currentLocalDateTime we use the plusDays() method:

LocalDateTime nextDayDateTime = currentLocalDateTime.plusDays(1);

We can also use plus() method to add a Period or Duration to our currentLocalDateTime:

Period oneMonth = new Period().withMonths(1); LocalDateTime nextMonthDateTime = currentLocalDateTime.plus(oneMonth);

The methods are similar for the other date and time components, for example, plusYears() for adding extra years, plusSeconds() for adding more seconds and so on.

To subtract a day from our currentLocalDateTime we can use the minusDays() method:

LocalDateTime previousDayLocalDateTime = currentLocalDateTime.minusDays(1);

Besides, doing calculations with date and time, we can also, set individual parts of the date or time. For example, setting the hour to 10 can be achieved using the withHourOfDay() method. Other methods that start with the prefix “with” can be used to set components of that date or time:

LocalDateTime currentDateAtHour10 = currentLocalDateTime .withHourOfDay(0) .withMinuteOfHour(0) .withSecondOfMinute(0) .withMillisOfSecond(0);

Another important aspect is that we can convert from a date and time class type to another. To do this, we can use specific methods provided by the library:

  • toDateTime() – converts LocalDateTime to a DateTime object
  • toLocalDate() – converts LocalDateTime to a LocalDate object
  • toLocalTime() – converts LocalDateTime to a LocalTime object
  • toDate() – converts LocalDateTime to a Java Date object

7. Working with Time Zones

Joda-Time makes it easy for us to work with different time zones and changed between them. We have the DateTimeZone abstract class which is used to represent all aspects regarding a time zone.

The default time zone used by Joda-Time is selected from the user.timezone Java system property. The library API lets us specify, individually for each class or calculation what timezone should be used. For example, we can create a LocalDateTime object

When we know that we'll use a specific time zone across the whole application, we can set the default time zone:

DateTimeZone.setDefault(DateTimeZone.UTC);

From now one all the date and time operations, if not otherwise specified, will be represented in the UTC time zone.

To see all the available time zones we can use the method getAvailableIDs():

DateTimeZone.getAvailableIDs()

When we need to represent the date or time in a specific time zone we can use any of the classes LocalTime, LocalDate, LocalDateTime, DateTime and specify in the constructor the DateTimeZone object:

DateTime dateTimeInChicago = new DateTime(DateTimeZone.forID("America/Chicago")); DateTime dateTimeInBucharest = new DateTime(DateTimeZone.forID("Europe/Bucharest")); LocalDateTime localDateTimeInChicago = new LocalDateTime(DateTimeZone.forID("America/Chicago"));

Also, when converting between those classes we can specify the desired time zone. The method toDateTime() accepts a DateTimeZone object and toDate() accepts java.util.TimeZone object:

DateTime convertedDateTime = localDateTimeInChicago.toDateTime(DateTimeZone.forID("Europe/Bucharest")); Date convertedDate = localDateTimeInChicago.toDate(TimeZone.getTimeZone("Europe/Bucharest"));

8. Conclusion

Joda-Time adalah pustaka fantastis yang dimulai dengan tujuan utama untuk memperbaiki masalah di JDK terkait operasi tanggal dan waktu. Ini segera menjadi pustaka de facto untuk penanganan tanggal dan waktu dan baru-baru ini konsep utama darinya diperkenalkan di Java 8.

Penting untuk dicatat bahwa penulis menganggapnya "sebagai proyek yang sebagian besar sudah selesai" dan merekomendasikan untuk memigrasi kode yang ada untuk menggunakan implementasi Java 8.

Kode sumber artikel tersedia di GitHub.