บทนำสู่ Java 8 Date / Time API

1. ภาพรวม

Java 8 แนะนำ APIs ใหม่สำหรับวันและเวลาเพื่อแก้ไขข้อบกพร่องของพี่java.util.Dateและjava.util.Calendar

ในบทความนี้เรามาเริ่มต้นด้วยปัญหาในDateและCalendar API ที่มีอยู่แล้วมาพูดคุยกันว่า Java 8 Date and Time API ใหม่แก้ไขปัญหาอย่างไร

นอกจากนี้เรายังจะมีลักษณะที่บางส่วนของชั้นเรียนหลักของ Java 8 โครงการใหม่ที่เป็นส่วนหนึ่งของjava.timeแพคเกจเช่นLOCALDATE , localtime, LocalDateTime, ZonedDateTime ระยะเวลาระยะเวลาและ API การสนับสนุนของพวกเขา

2. ปัญหาเกี่ยวกับวันที่ / เวลา API ที่มีอยู่

  • ความปลอดภัยของเธรด - คลาสวันที่และปฏิทินไม่ปลอดภัยสำหรับเธรดทำให้นักพัฒนาต้องรับมือกับความปวดหัวจากปัญหาการทำงานพร้อมกันอย่างหนักและต้องเขียนโค้ดเพิ่มเติมเพื่อจัดการกับความปลอดภัยของเธรด ในทางตรงกันข้ามAPI วันที่และเวลาใหม่ที่เปิดตัวใน Java 8 นั้นไม่เปลี่ยนรูปและปลอดภัยต่อเธรดดังนั้นจึงไม่ต้องปวดหัวพร้อมกันนั้นจากนักพัฒนา
  • การออกแบบ API และความง่ายในการทำความเข้าใจ - API วันที่และปฏิทินได้รับการออกแบบมาไม่ดีด้วยวิธีการที่ไม่เพียงพอในการดำเนินการในแต่ละวัน ใหม่วันที่ / เวลา APIs มาตรฐาน ISO เป็นศูนย์กลางและเป็นไปตามรูปแบบโดเมนที่สอดคล้องกันสำหรับวันเวลาระยะเวลาและระยะเวลา มีวิธียูทิลิตี้มากมายที่รองรับการใช้งานทั่วไป
  • ZonedDateและTime - นักพัฒนาต้องเขียนตรรกะเพิ่มเติมเพื่อจัดการตรรกะเขตเวลาด้วย API เก่าในขณะที่ API ใหม่การจัดการเขตเวลาทำได้ด้วย Localและ ZonedDate / Time APIs

3. การใช้LocalDate , LocalTimeและLocalDateTime

เรียนที่ใช้กันมากที่สุดคือLOCALDATE , localtimeและLocalDateTime ตามชื่อของพวกเขาแสดงถึงวันที่ / เวลาในท้องถิ่นจากบริบทของผู้สังเกตการณ์

คลาสเหล่านี้ส่วนใหญ่จะใช้เมื่อไม่จำเป็นต้องระบุเขตเวลาอย่างชัดเจนในบริบท ในส่วนนี้เราจะกล่าวถึง API ที่ใช้บ่อยที่สุด

3.1. การทำงานกับLocalDate

LOCALDATEแสดงถึงวันที่ในรูปแบบ ISO (YYYY-MM-DD) โดยเวลา

สามารถใช้เพื่อจัดเก็บวันที่เช่นวันเกิดและวันจ่ายเงิน

สามารถสร้างอินสแตนซ์ของวันที่ปัจจุบันได้จากนาฬิการะบบดังนี้:

LocalDate localDate = LocalDate.now();

LOCALDATEตัวแทนเฉพาะวันเดือนและปีสามารถรับใช้“ ของ ” วิธีการหรือโดยการใช้“ แจง ” วิธีการ ตัวอย่างเช่นข้อมูลโค้ดด้านล่างแสดงถึงLocalDateสำหรับวันที่ 20 กุมภาพันธ์ 2015:

LocalDate.of(2015, 02, 20); LocalDate.parse("2015-02-20");

LOCALDATEมีวิธีการสาธารณูปโภคต่าง ๆ ที่จะได้รับความหลากหลายของข้อมูล มาดูวิธีการ API เหล่านี้กันอย่างรวดเร็ว

ข้อมูลโค้ดต่อไปนี้รับวันที่ในท้องถิ่นปัจจุบันและเพิ่มหนึ่งวัน:

LocalDate tomorrow = LocalDate.now().plusDays(1);

ตัวอย่างนี้รับวันที่ปัจจุบันและลบหนึ่งเดือน สังเกตว่ามันยอมรับenumเป็นหน่วยเวลาอย่างไร:

LocalDate previousMonthSameDay = LocalDate.now().minus(1, ChronoUnit.MONTHS);

ในสองตัวอย่างโค้ดต่อไปนี้เราจะแยกวิเคราะห์วันที่“ 2016-06-12” และรับวันในสัปดาห์และวันของเดือนตามลำดับ สังเกตค่าที่ส่งคืนค่าแรกคือวัตถุที่เป็นตัวแทนของDayOfWeekในขณะที่ค่าที่สองในintแทนค่าลำดับของเดือน:

DayOfWeek sunday = LocalDate.parse("2016-06-12").getDayOfWeek(); int twelve = LocalDate.parse("2016-06-12").getDayOfMonth();

เราสามารถทดสอบได้ว่าวันที่เกิดขึ้นในปีอธิกสุรทิน ในตัวอย่างนี้เราทดสอบว่าวันที่ปัจจุบันเป็นปีอธิกสุรทินหรือไม่:

boolean leapYear = LocalDate.now().isLeapYear();

ความสัมพันธ์ของวันที่กับวันอื่นสามารถกำหนดได้ว่าจะเกิดขึ้นก่อนหรือหลังวันที่อื่น:

boolean notBefore = LocalDate.parse("2016-06-12") .isBefore(LocalDate.parse("2016-06-11")); boolean isAfter = LocalDate.parse("2016-06-12") .isAfter(LocalDate.parse("2016-06-11"));

ขอบเขตวันที่สามารถหาได้จากวันที่ที่กำหนด ในสองตัวอย่างต่อไปนี้เราได้รับLocalDateTimeที่แสดงถึงจุดเริ่มต้นของวัน (2016-06-12T00: 00) ของวันที่ที่กำหนดและLocalDateที่แสดงถึงจุดเริ่มต้นของเดือน (2016-06-01) ตามลำดับ:

LocalDateTime beginningOfDay = LocalDate.parse("2016-06-12").atStartOfDay(); LocalDate firstDayOfMonth = LocalDate.parse("2016-06-12") .with(TemporalAdjusters.firstDayOfMonth());

ตอนนี้เรามาดูกันว่าเราทำงานกับเวลาท้องถิ่นอย่างไร

3.2. การทำงานกับLocalTime

localtimeหมายถึงเวลาโดยไม่ต้องวันที่

เช่นเดียวกับLocalDateอินสแตนซ์ของLocalTimeสามารถสร้างได้จากนาฬิการะบบหรือโดยใช้วิธี "แยกวิเคราะห์" และ "จาก" ดู API ที่ใช้กันทั่วไปอย่างรวดเร็วด้านล่าง

สามารถสร้างอินสแตนซ์ของLocalTimeปัจจุบันได้จากนาฬิการะบบดังต่อไปนี้:

LocalTime now = LocalTime.now();

ในด้านล่างตัวอย่างโค้ด,เราจะสร้างlocaltimeคิดเป็น 06:30 โดยแยกเป็นตัวแทนสตริง:

LocalTime sixThirty = LocalTime.parse("06:30");

วิธีโรงงาน“ของ” สามารถนำมาใช้ในการสร้างlocaltime ตัวอย่างเช่นโค้ดด้านล่างจะสร้างLocalTimeแทนเวลา 06:30 น. โดยใช้วิธีการจากโรงงาน:

LocalTime sixThirty = LocalTime.of(6, 30);

ตัวอย่างด้านล่างนี้จะสร้างLocalTimeโดยการแยกวิเคราะห์สตริงและเพิ่มหนึ่งชั่วโมงโดยใช้ API“ บวก” ผลลัพธ์จะเป็นLocalTimeแทน 07:30 AM:

LocalTime sevenThirty = LocalTime.parse("06:30").plus(1, ChronoUnit.HOURS);

มีวิธีการรับที่หลากหลายซึ่งสามารถใช้เพื่อรับหน่วยเวลาเฉพาะเช่นชั่วโมงนาทีและวินาทีดังต่อไปนี้:

int six = LocalTime.parse("06:30").getHour();

We can also check if a specific time is before or after another specific time. The below code sample compares two LocalTime for which the result would be true:

boolean isbefore = LocalTime.parse("06:30").isBefore(LocalTime.parse("07:30"));

The max, min and noon time of a day can be obtained by constants in LocalTime class. This is very useful when performing database queries to find records within a given span of time. For example, the below code represents 23:59:59.99:

LocalTime maxTime = LocalTime.MAX

Now let's dive into LocalDateTime.

3.3. Working With LocalDateTime

The LocalDateTime is used to represent a combination of date and time.

This is the most commonly used class when we need a combination of date and time. The class offers a variety of APIs and we will look at some of the most commonly used ones.

An instance of LocalDateTime can be obtained from the system clock similar to LocalDate and LocalTime:

LocalDateTime.now();

The below code samples explain how to create an instance using the factory “of” and “parse” methods. The result would be a LocalDateTime instance representing 20 February 2015, 06:30 AM:

LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);
LocalDateTime.parse("2015-02-20T06:30:00");

There are utility APIs to support addition and subtraction of specific units of time like days, months, year and minutes are available. The below code samples demonstrates the usage of “plus” and “minus” methods. These APIs behave exactly like their counterparts in LocalDate and LocalTime:

localDateTime.plusDays(1);
localDateTime.minusHours(2);

Getter methods are available to extract specific units similar to the date and time classes. Given the above instance of LocalDateTime, the below code sample will return the month February:

localDateTime.getMonth();

4. Using ZonedDateTime API

Java 8 provides ZonedDateTime when we need to deal with time zone specific date and time. The ZoneId is an identifier used to represent different zones. There are about 40 different time zones and the ZoneId are used to represent them as follows.

In this code snippet we create a Zone for Paris:

ZoneId zoneId = ZoneId.of("Europe/Paris"); 

A set of all zone ids can be obtained as below:

Set allZoneIds = ZoneId.getAvailableZoneIds();

The LocalDateTime can be converted to a specific zone:

ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);

The ZonedDateTime provides parse method to get time zone specific date time:

ZonedDateTime.parse("2015-05-03T10:15:30+01:00[Europe/Paris]");

Another way to work with time zone is by using OffsetDateTime. The OffsetDateTime is an immutable representation of a date-time with an offset. This class stores all date and time fields, to a precision of nanoseconds, as well as the offset from UTC/Greenwich.

The OffSetDateTime instance can be created as below using ZoneOffset. Here we create a LocalDateTime representing 6:30 am on 20th February 2015:

LocalDateTime localDateTime = LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);

Then we add two hours to the time by creating a ZoneOffset and setting for the localDateTime instance:

ZoneOffset offset = ZoneOffset.of("+02:00"); OffsetDateTime offSetByTwo = OffsetDateTime .of(localDateTime, offset);

We now have a localDateTime of 2015-02-20 06:30 +02:00. Now let's move on to how to modify date and time values using the Period and Duration classes.

5. Using Period and Duration

The Period class represents a quantity of time in terms of years, months and days and the Duration class represents a quantity of time in terms of seconds and nano seconds.

5.1. Working With Period

The Period class is widely used to modify values of given a date or to obtain the difference between two dates:

LocalDate initialDate = LocalDate.parse("2007-05-10");

The Date can be manipulated using Period as shown in the following code snippet:

LocalDate finalDate = initialDate.plus(Period.ofDays(5));

The Period class has various getter methods such as getYears, getMonths and getDays to get values from a Period object. The below code example returns an int value of 5 as we try to get difference in terms of days:

int five = Period.between(initialDate, finalDate).getDays();

The Period between two dates can be obtained in a specific unit such as days or month or years, using ChronoUnit.between:

long five = ChronoUnit.DAYS.between(initialDate, finalDate);

This code example returns five days. Let's continue by taking a look at the Duration class.

5.2. Working With Duration

Similar to Period, the Duration class is use to deal with Time. In the following code we create a LocalTime of 6:30 am and then add a duration of 30 seconds to make a LocalTime of 06:30:30am:

LocalTime initialTime = LocalTime.of(6, 30, 0); LocalTime finalTime = initialTime.plus(Duration.ofSeconds(30));

The Duration between two instants can be obtained either as a Duration or as a specific unit. In the first code snippet we use the between() method of the Duration class to find the time difference between finalTime and initialTime and return the difference in seconds:

long thirty = Duration.between(initialTime, finalTime).getSeconds();

In the second example we use the between() method of the ChronoUnit class to perform the same operation:

long thirty = ChronoUnit.SECONDS.between(initialTime, finalTime);

Now we will look at how to convert existing Date and Calendar to new Date/Time.

6. Compatibility with Date and Calendar

Java 8 has added the toInstant() method which helps to convert existing Date and Calendar instance to new Date Time API as in the following code snippet:

LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); LocalDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault());

The LocalDateTime can be constructed from epoch seconds as below. The result of the below code would be a LocalDateTime representing 2016-06-13T11:34:50:

LocalDateTime.ofEpochSecond(1465817690, 0, ZoneOffset.UTC);

Now let's move on to Date and Time formatting.

7. Date and Time Formatting

Java 8 provides APIs for the easy formatting of Date and Time:

LocalDateTime localDateTime = LocalDateTime.of(2015, Month.JANUARY, 25, 6, 30);

The below code passes an ISO date format to format the local date. The result would be 2015-01-25 :

String localDateString = localDateTime.format(DateTimeFormatter.ISO_DATE);

The DateTimeFormatter provides various standard formatting options. Custom patterns can be provided to format method as well, like below, which would return a LocalDate as 2015/01/25:

localDateTime.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));

We can pass in formatting style either as SHORT, LONG or MEDIUM as part of the formatting option. The below code sample would give an output representing LocalDateTime in 25-Jan-2015, 06:30:00:

localDateTime .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)) .withLocale(Locale.UK);

Let us take a look at alternatives available to Java 8 Core Date/Time APIs.

8. Backport and Alternate Options

8.1. Using Project Threeten

For organization that are on the path of moving to Java 8 from Java 7 or Java 6 and want to use date and time API, project threeten provides the backport capability. Developers can use classes available in this project to achieve the same functionality as that of new Java 8 Date and Time API and once they move to Java 8, the packages can be switched. Artifact for the project threeten can be found in the maven central repository:

 org.threeten threetenbp 1.3.1 

8.2. Joda-Time Library

Another alternative for Java 8 Date and Time library is Joda-Time library. In fact Java 8 Date Time APIs has been led jointly by the author of Joda-Time library (Stephen Colebourne) and Oracle. This library provides pretty much all capabilities that is supported in Java 8 Date Time project. The Artifact can be found in the maven central by including the below pom dependency in your project:

 joda-time joda-time 2.9.4 

9. Conclusion

Java 8 provides a rich set of APIs with consistent API design for easier development.

The code samples for the above article can be found in the Java 8 Date/Time git repository.