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.