12/08/2018, 13:29

Java 8 Date and Time API

Tiếp theo loạt bài về Java 8, nay mình post bài tìm hiểu về Date and Time API. Trước đây mình không có ý định tìm hiểu nó vì nghĩ rằng nó không có gì đặc biệt, rằng java.util.Date, và java.util.Calendar là đủ rồi. Nay tì hiểu nó thì mình đã hiểu tại sao Oracle lại đưa nó vào Java 8. Giờ ta từng ...

Tiếp theo loạt bài về Java 8, nay mình post bài tìm hiểu về Date and Time API. Trước đây mình không có ý định tìm hiểu nó vì nghĩ rằng nó không có gì đặc biệt, rằng java.util.Date, và java.util.Calendar là đủ rồi. Nay tì hiểu nó thì mình đã hiểu tại sao Oracle lại đưa nó vào Java 8. Giờ ta từng bước khá phá sự tiện lợi thú vị của LocalDate/Time nào.

Mọi lớp của Date/Time API trong Java 8 thuộc package java.time. Ta bắt đầu tìm hiểu lớp đầu tiên của gói là java.time.LocalDate. Mỗi một LocalDate là một giá trị năm-tháng-ngày không có giờ, phút, và giây.

// Khởi tạo ngày tháng năm hiện tại
LocalDate today = LocalDate.now();
// Khởi tạo ngày 2016-05-25
LocalDate tenthFeb2014 = LocalDate.of(2016, Month.MAY, 25);
// hoặc 1981-09-01
LocalDate firstSep1981 = LocalDate.of(1981, 9, 1);
// Khởi tạo ngày thứ 65 của năm 2016 (2016-03-06)
LocalDate sixtyFifthDayOf2010 = LocalDate.ofYearDay(2016, 65);

Khi ta muốn làm việc với thời gian (giờ, phút, giây) ta dùng lớp LocalTime.

// Khởi tạo giờ hiện tại
LocalTime currentTime = LocalTime.now();
// hay lúc 12 giờ trưa
LocalTime midday = LocalTime.of(12, 0);
// 13:30:15
LocalTime afterMidday = LocalTime.of(13, 30, 15);
// hay giây thứ 12345 của một ngày là (03:25:45)
LocalTime fromSecondsOfDay = LocalTime.ofSecondOfDay(12345);

Còn khi ta làm việc với ngày và giờ thì sử dụng lớp LocalDateTime

// Khởi tạo ngày giờ hiện tại
LocalDateTime currentDateTime = LocalDateTime.now();
// hay 2016-05-25 12:30
LocalDateTime twentyMay2016 = LocalDateTime.of(2016, 5, 25, 12, 30);
// hay
LocalDateTime christmas2014 = LocalDateTime.of(2016, Month.MAY, 25, 12, 30);

Mặc định thì lớp LocalDate/Time sử dụng đồng hồ hệ thống với timezone mặc định. Ta có thể thay đổi bằng cách truyền tham số timezone khi khởi tạo.

// Thời gian theo Hà Nội
LocalTime currentTimeInHaNoi = LocalTime.now(ZoneId.of("Vietnam/Hanoi"));
//hay UTC time zone
LocalTime nowInUtc = LocalTime.now(Clock.systemUTC());

Giờ ta khám phá một số hàm tiện ích khi làm việc với LocalDate/Time

  • Một số hàm cho ngày
LocalDate date = LocalDate.of(2016, 5, 15);
boolean isBefore = LocalDate.now().isBefore(date);
// => trả về true/false
// Lấy tên của tháng
Month may = date.getMonth();// => MAY
// Lấy tháng dạng số
int mayValue = may.getValue(); // 5
// Lấy số ngày lớn nhất/nhỏ nhất của tháng (thay đổi theo năm nhuân, không nhuận)
LocalDate date2 = LocalDate.of(2016, 2, 15);
Month february = date2.getMonth();// => FEBRUARY
int minLength = february.minLength(); // 28
int maxLength = february.maxLength(); // 29
// Lấy tháng trước tháng hiện tại
Month firstMonthOfQuarter = february.firstMonthOfQuarter(); // JANUARY
// Lấy ngày sau ngày hôm nay
LocalDate tomorrow = LocalDate.now().plusDays(1);
// Trước hiện tại 5 giờ and 30 phút
LocalDateTime dateTime = LocalDateTime.now().minusHours(5).minusMinutes(30);
// Lấy ngày đầu tiên của tháng
LocalDate firstDayOfMonth = date.with(TemporalAdjusters.firstDayOfMonth());
// => 2016-05-01
// Lấy ngày cuối của tháng
LocalDate lastDayOfMonth = date.with(TemporalAdjusters.lastDayOfMonth());
// => 2016-05-31
  • Tiện ích hơn nhiều khi ta import static FirstMonthOfQuarter.*
mport static java.time.temporal.TemporalAdjusters.*;
...
// 2016-05-31
LocalDate lastDayOfYear = date.with(lastDayOfYear());
// 2016-05-01
LocalDate firstDayOfNextMonth = date.with(firstDayOfNextMonth());
// Chủ nhật tiếp theo
LocalDate nextSunday = date.with(next(DayOfWeek.SUNDAY));
  • Một số hàm cho năm
int year = date.getYear(); // 2016
int dayOfYear = date.getDayOfYear(); // 46
int lengthOfYear = date.lengthOfYear(); // 365
boolean isLeapYear = date.isLeapYear(); // false
DayOfWeek dayOfWeek = date.getDayOfWeek();
int dayOfWeekIntValue = dayOfWeek.getValue(); // 6
String dayOfWeekName = dayOfWeek.name(); // SATURDAY
int dayOfMonth = date.getDayOfMonth(); // 25
LocalDateTime startOfDay = date.atStartOfDay(); // 2016-05-25 00:00
// Một số hàm đặc biệt khác cho năm
Year currentYear = Year.now();
Year twoThousand = Year.of(2000);
boolean isLeap = currentYear.isLeap(); // false
int length = currentYear.length(); // 365
// sixtyFourth day of 2014 (2014-03-05)
LocalDate date = Year.of(2014).atDay(64);
  • Một số hàm cho giờ
LocalTime time = LocalTime.of(15, 30); // 15:30:00
int hour = time.getHour(); // 15
int second = time.getSecond(); // 0
int minute = time.getMinute(); // 30
int secondOfDay = time.toSecondOfDay(); // 55800

Từ Java 7 trở về trước khi làm việc với Time zones thì khá phức tạp.

// Làm việc với Calendar
Calendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(timezoneAlteredTime);

DateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");
formatter.setCalendar(calendar);
formatter.setTimeZone(TimeZone.getTimeZone(timeZone));
String newZealandTime = formatter.format(calendar.getTime());

// Sử dụng Date
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = isoFormat.parse("2010-05-23T09:01:02");

Với Java 8 cung cấp lớp ZonedDateTime và OffsetDateTime giúp ta làm việc với Time zones trở nên đơn giản hơn.

ZoneId losAngeles = ZoneId.of("America/Los_Angeles");
ZoneId berlin = ZoneId.of("Europe/Berlin");
// 2016-05-25 12:00
LocalDateTime dateTime = LocalDateTime.of(2016, 05, 25, 12, 0);
// 2016-05-25 13:00, Europe/Berlin (+01:00)
ZonedDateTime berlinDateTime = ZonedDateTime.of(dateTime, berlin);
// 2016-05-25 03:00, America/Los_Angeles (-08:00)

ZonedDateTime losAngelesDateTime = berlinDateTime.withZoneSameInstant(losAngeles);
int offsetInSeconds = losAngelesDateTime.getOffset().getTotalSeconds(); // -28800

Java 8 cung cấp lớp Instant hỗ trợ làm việc với timestamps (chi tiết đến mili giây). Instant tính mốc thời gian từ giây thứ nhất của ngày 01-01-1970 gọi là EPOCH. Giá trị của nó sẽ nhận giá trị âm khi mốc thời gian trước thời điểm epoch.

// Ngày giờ hiện tại
Instant now = Instant.now();
// Tính theo unix timestamp
Instant fromUnixTimestamp = Instant.ofEpochSecond(1262347200);
// => 2010-01-01 12:00:00
// theo mili giây
Instant fromEpochMilli = Instant.ofEpochMilli(1262347200000l);

Tính toán khoảng thời gian giữa hai mốc thời gian có lẽ là cái khó khăn nhất khi sử dụng Date/Calendar. Với Java 8 Date/Time API thì tiện lợi hơn nhiều.

  • Period

Với Period ta tính chính xác số năm, tháng, ngày giữa firstDate và seconDate một cách đơn giản.

LocalDate firstDate = LocalDate.of(2010, 5, 17); // 2010-05-17
LocalDate secondDate = LocalDate.of(2015, 3, 7); // 2015-03-07

Period period = Period.between(firstDate, secondDate);
int days = period.getDays(); // 18
int months = period.getMonths(); // 9
int years = period.getYears(); // 4

boolean isNegative = period.isNegative(); // false

Period twoMonthsAndFiveDays = Period.ofMonths(2).plusDays(5);
LocalDate sixthOfJanuary = LocalDate.of(2014, 1, 6);
// Cộng 2 tháng và 5 ngày cho sixthOfJanuary ta được ngày 2014-03-11
LocalDate eleventhOfMarch = sixthOfJanuary.plus(twoMonthsAndFiveDays);
  • Duration

Duration cùng với Instant sẽ tính toán chi tiết số giờ, đến mili giây giữa hai môc thời gian.

Instant firstInstant= Instant.ofEpochSecond( 1294881180 ); // 2011-01-13 01:13
Instant secondInstant = Instant.ofEpochSecond(1294708260); // 2011-01-11 01:11

Duration between = Duration.between(firstInstant, secondInstant);
// negative because firstInstant is after secondInstant (-172920)
long seconds = between.getSeconds();
// get absolute result in minutes (2882)
long absoluteResult = between.abs().toMinutes();
// two hours in seconds (7200)
long twoHoursInSeconds = Duration.ofHours(2).getSeconds();

Một tiện ích lớn nữa của Java Date/Time API là tính năng định dạng và chuyển đổi thời gian một cách dễ dàng.

// 2014-04-01 10:45
LocalDateTime dateTime = LocalDateTime.of(2014, Month.APRIL, 1, 10, 45);
// Định dạng theo ISO date cơ bản (20140220)
String asBasicIsoDate = dateTime.format(DateTimeFormatter.BASIC_ISO_DATE);
// theo ISO week date (2014-W08-4)
String asIsoWeekDate = dateTime.format(DateTimeFormatter.ISO_WEEK_DATE);
// theo ISO date time (2014-02-20T20:04:05.867)
String asIsoDateTime = dateTime.format(DateTimeFormatter.ISO_DATE_TIME);
// Hay theo pattern (01/04/2014)
String asCustomPattern = dateTime.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));

// parsing date strings
LocalDate fromIsoDate = LocalDate.parse("2014-01-20");
LocalDate fromIsoWeekDate = LocalDate.parse("2014-W14-2", DateTimeFormatter.ISO_WEEK_DATE);
LocalDate fromCustomPattern = LocalDate.parse("20.01.2014", DateTimeFormatter.ofPattern("dd.MM.yyyy"));

Giờ thì ta đã thấy được tiện ích của Date/Time API trong Java 8, một tiện ích mà chỉ chút nữa tôi đã bỏ qua khi áp đặt cảm tính chủ quan của mình.

0