本文系统地介绍下Java
中的时间类,先来看下概况:
从Java 1.0
的Date
开始,介绍了它存在的问题,然后在Java 1.1
中引入了Calendar
,
但是它并没有完全解决Date
存在的问题,如月份仍从0
开始计算,可变类导致的线程安全问题等。
接着,Joda-Time
应运而生,直至Java 8
提供了java.time
包,接下来让我们看下这个包内的主要类。
Date
我们先来看下Date
主要存在哪些问题?
第一,年份从1900
年开始,月份从0
开始。
Date date1 = new Date(120, 5, 10);
// Wed Jun 10 00:00:00 CST 2020
第二,构造时间可能会存在自动调整。
Date date2 = new Date(120, 5, 31);
Date date3 = new Date(120, 6, 1);
System.out.println(date2);
System.out.println(date3);
System.out.println(date2.equals(date3));
// Wed Jul 01 00:00:00 CST 2020
// Wed Jul 01 00:00:00 CST 2020
// true
第三,由于Date
类是可变的,所以存在线程安全问题。
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
es.submit(() -> {
try {
System.out.println(sdf.parse("15081947"));
} catch (ParseException e) {
e.printStackTrace();
}
});
}
es.shutdown();
// Thu Aug 16 00:00:00 CST 1509
// Thu Aug 16 00:00:00 CST 1509
// Thu Aug 16 00:00:00 CST 1509
// Sat Apr 16 00:00:00 CST 1137
// Sat Apr 16 00:00:00 CST 1137
// Thu Aug 16 00:00:00 CST 1509
// Thu Aug 16 00:00:00 CST 1509
同时,Date
还有如下缺点:
- 无法支持国际化。
- 操作复杂。
Calendar
Calendar
解决了Date
月份从1900
开始的问题,还是其他的问题并未解决。
来看下它的常见用法:
Calendar calendar = Calendar.getInstance();
calendar.set(2020, Calendar.SEPTEMBER, 12, 15, 56, 00);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
int weekOfYear = calendar.get(Calendar.WEEK_OF_YEAR);
int weekOfMonth = calendar.get(Calendar.WEEK_OF_MONTH);
int hour = calendar.get(Calendar.HOUR);
int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
int millisecond = calendar.get(Calendar.MILLISECOND);
System.out.println("year = " + year);
System.out.println("month = " + month);
System.out.println("dayOfMonth = " + dayOfMonth);
System.out.println("dayOfWeek = " + dayOfWeek);
System.out.println("weekOfYear = " + weekOfYear);
System.out.println("weekOfMonth = " + weekOfMonth);
System.out.println("hour = " + hour);
System.out.println("hourOfDay = " + hourOfDay);
System.out.println("minute = " + minute);
System.out.println("second = " + second);
System.out.println("millisecond = " + millisecond);
// year = 2020
// month = 8
// dayOfMonth = 12
// dayOfWeek = 7
// weekOfYear = 37
// weekOfMonth = 2
// hour = 3
// hourOfDay = 15
// minute = 56
// second = 0
// millisecond = 385
Java8
我们可以使用LocalDate
、LocalTime
、LocalDateTime
处理本地时间,
也可以使用ZonedId
、ZonedDateTine
、ZoneOffset
处理不同时区的时间,
还可以方便地对时间进行各种操作、计算时间之间的间隔ChronoUnit、Period、Duration
,
TemporalAdjusters
还提供一系列方法便于我们求年、月
中的指定日期,
同时,我们还可以通过格式化和解析处理时间和字符串,
最后,Java8
还保证了向后兼容性,通过Instant
来实现Date
、Calendar
与Java8
时间类之间的转换。
Local
下面通过构建实例、基本信息和常用方法三个维度来梳理下LocalDate
、LocalTime
和LocalDateTime
。
LocalDate
public class LocalDateTest {
public static void main(String[] args) {
/** 1. How to create a LocalDate instance? */
System.out.println("-----------------------------------------");
LocalDate ld1 = LocalDate.now();
System.out.println("ld1 = " + ld1);
LocalDate ld2 = LocalDate.of(2020, 9, 12);
System.out.println("ld2 = " + ld2);
LocalDate ld3 = LocalDate.parse("2020-09-12");
System.out.println("ld3 = " + ld3);
System.out.println("-----------------------------------------");
/** 2. Basic Information. */
LocalDate date = LocalDate.of(2020, 9, 11);
int year = date.getYear();
Month month = date.getMonth();
int dayOfMonth = date.getDayOfMonth();
DayOfWeek dayOfWeek = date.getDayOfWeek();
int dayOfYear = date.getDayOfYear();
int len = date.lengthOfMonth();
boolean leapYear = date.isLeapYear();
System.out.println("year = " + year); // 2020
System.out.println("month = " + month); // SEPTEMBER
System.out.println("dayOfMonth = " + dayOfMonth); // 11
System.out.println("dayOfWeek = " + dayOfWeek); // FRIDAY
System.out.println("dayOfYear = " + dayOfYear); // 255
System.out.println("len = " + len); // 30
System.out.println("leapYear = " + leapYear); // true
System.out.println("-----------------------------------------");
/** 3. Utility Methods. */
LocalDate ld = LocalDate.now();
LocalDate tomorrow = ld.plusDays(1);
LocalDate yesterday = ld.minusDays(1);
LocalDate lastMonth1 = ld.minusMonths(1);
LocalDate lastMonth2 = ld.minus(1, ChronoUnit.MONTHS);
LocalDate ld4 = LocalDate.parse("2020-09-12");
LocalDate ld5 = LocalDate.parse("2020-10-01");
boolean before = ld4.isBefore(ld5);
boolean after = ld4.isAfter(ld5);
System.out.println("tomorrow = " + tomorrow);
System.out.println("yesterday = " + yesterday);
System.out.println("lastMonth1 = " + lastMonth1);
System.out.println("lastMonth2 = " + lastMonth2);
System.out.println("before = " + before);
System.out.println("after = " + after);
System.out.println("-----------------------------------------");
}
}
// -----------------------------------------
// ld1 = 2020-09-12
// ld2 = 2020-09-12
// ld3 = 2020-09-12
// -----------------------------------------
// year = 2020
// month = SEPTEMBER
// dayOfMonth = 11
// dayOfWeek = FRIDAY
// dayOfYear = 255
// len = 30
// leapYear = true
// -----------------------------------------
// tomorrow = 2020-09-13
// yesterday = 2020-09-11
// lastMonth1 = 2020-08-12
// lastMonth2 = 2020-08-12
// before = true
// after = false
// -----------------------------------------
LocalTime
public class LocalTimeTest {
public static void main(String[] args) {
/**
* 1. How to create a LocalTime instance?
*/
LocalTime lt1 = LocalTime.now();
LocalTime lt2 = LocalTime.of(11, 22, 0);
LocalTime lt3 = LocalTime.parse("11:22:00");
System.out.println("-----------------------------------------");
System.out.println("lt1 = " + lt1);
System.out.println("lt2 = " + lt2);
System.out.println("lt3 = " + lt3);
DateTimeFormatter localTimeFormatter = DateTimeFormatter.ISO_LOCAL_TIME;
String lt1Str = lt1.truncatedTo(ChronoUnit.SECONDS).format(localTimeFormatter);
System.out.println("lt1Str = " + lt1Str);
System.out.println("-----------------------------------------");
/**
* 2. Basic Information.
*/
LocalTime now = LocalTime.now();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
int nano = now.getNano();
System.out.println("now = " + now);
System.out.println("hour = " + hour);
System.out.println("minute = " + minute);
System.out.println("second = " + second);
System.out.println("nano = " + nano);
System.out.println("-----------------------------------------");
/**
* 3. Utility Methods.
*/
LocalTime lt = LocalTime.now();
LocalTime addTwoHours = lt.plusHours(2);
LocalTime minus30Mins = lt.minus(30, ChronoUnit.MINUTES);
LocalTime withHour3 = lt.withHour(3);
System.out.println("now = " + lt);
System.out.println("addTwoHours = " + addTwoHours);
System.out.println("minus30Mins = " + minus30Mins);
System.out.println("withHour3 = " + withHour3);
System.out.println("-----------------------------------------");
}
}
// -----------------------------------------
// lt1 = 18:32:42.948844200
// lt2 = 11:22
// lt3 = 11:22
// lt1Str = 18:32:42
// -----------------------------------------
// now = 18:32:42.982458100
// hour = 18
// minute = 32
// second = 42
// nano = 982458100
// -----------------------------------------
// now = 18:32:42.988980100
// addTwoHours = 20:32:42.988980100
// minus30Mins = 18:02:42.988980100
// withHour3 = 03:32:42.988980100
// -----------------------------------------
LocalDateTime
public class LocalDateTimeTest {
public static void main(String[] args) {
/**
* 1. How to create a LocalDateTime instance.
*/
LocalDateTime ldt1 = LocalDateTime.now();
LocalDateTime ldt2 = LocalDateTime.of(2020, 9, 12, 14, 45, 20);
LocalDateTime ldt3 = LocalDateTime.parse("2020-09-12T14:45:20");
System.out.println("-----------------------------------------");
System.out.println("ldt1 = " + ldt1);
System.out.println("ldt2 = " + ldt2);
System.out.println("ldt3 = " + ldt3);
System.out.println("-----------------------------------------");
/**
* 2. Basic Information.
*/
LocalDateTime datetime = LocalDateTime.now();
int year = datetime.getYear();
Month month = datetime.getMonth();
int monthValue = datetime.getMonthValue();
int dayOfYear = datetime.getDayOfYear();
int dayOfMonth = datetime.getDayOfMonth();
DayOfWeek dayOfWeek = datetime.getDayOfWeek();
int hour = datetime.getHour();
int minute = datetime.getMinute();
int second = datetime.getSecond();
int nano = datetime.getNano();
System.out.println("year = " + year);
System.out.println("month = " + month);
System.out.println("monthValue = " + monthValue);
System.out.println("dayOfYear = " + dayOfYear);
System.out.println("dayOfMonth = " + dayOfMonth);
System.out.println("dayOfWeek = " + dayOfWeek);
System.out.println("hour = " + hour);
System.out.println("minute = " + minute);
System.out.println("second = " + second);
System.out.println("nano = " + nano);
System.out.println("-----------------------------------------");
/**
* 3. Utility Methods.
*/
LocalDateTime today = LocalDateTime.now();
LocalDateTime tomorrow = today.plusDays(1);
LocalDateTime yesterday = today.minus(1, ChronoUnit.DAYS);
LocalDateTime lastMonth = today.minusMonths(1);
boolean before = today.isBefore(lastMonth);
boolean after = today.isAfter(lastMonth);
System.out.println("today = " + today);
System.out.println("tomorrow = " + tomorrow);
System.out.println("yesterday = " + yesterday);
System.out.println("lastMonth = " + lastMonth);
System.out.println("before = " + before);
System.out.println("after = " + after);
System.out.println("-----------------------------------------");
}
}
// -----------------------------------------
// ldt1 = 2020-09-12T18:33:44.990003600
// ldt2 = 2020-09-12T14:45:20
// ldt3 = 2020-09-12T14:45:20
// -----------------------------------------
// year = 2020
// month = SEPTEMBER
// monthValue = 9
// dayOfYear = 256
// dayOfMonth = 12
// dayOfWeek = SATURDAY
// hour = 18
// minute = 33
// second = 45
// nano = 12078800
// -----------------------------------------
// today = 2020-09-12T18:33:45.019102500
// tomorrow = 2020-09-13T18:33:45.019102500
// yesterday = 2020-09-11T18:33:45.019102500
// lastMonth = 2020-08-12T18:33:45.019102500
// before = false
// after = true
// -----------------------------------------
Zone
首先,我们来看下支持哪些时区?
Set<String> zonedIds = ZoneId.getAvailableZoneIds();
System.out.println("size() = " + zonedIds.size());
zonedIds.forEach(System.out::println);
// size = 599
// America/Los_Angeles
// Europe/London
// Europe/Paris
// Asia/Shanghai
// ...
再来看下如何创建一个ZonedDateTime
实例?
ZonedDateTime shDt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime lsDT = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
ZonedDateTime parisDT = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
System.out.println("shDt = " + shDt);
System.out.println("lsDT = " + lsDT);
System.out.println("parisDT = " + parisDT);
// shDt = 2020-09-12T15:06:46.609112900+08:00[Asia/Shanghai]
// lsDT = 2020-09-12T00:06:46.610617900-07:00[America/Los_Angeles]
// parisDT = 2020-09-12T09:06:46.612123200+02:00[Europe/Paris]
Instant
Instant
通常表示一个时间戳,需要注意两点:
第一,它通常表示的是UTC
时间。
第二,对于Date
和Calendar
的兼容性就是通过它实现的。
来看下常见用法:
public class InstantTest {
public static void main(String[] args) {
Instant now = Instant.now();
int nano = now.getNano();
long epochSecond = now.getEpochSecond();
long milliseconds = now.toEpochMilli();
System.out.println("now = " + now);
System.out.println("nano = " + nano);
System.out.println("epochSecond = " + epochSecond);
System.out.println("milliseconds = " + milliseconds);
}
}
// now = 2020-09-12T10:44:52.080563900Z
// nano = 80563900
// epochSecond = 1599907492
// milliseconds = 1599907492080
Intervals
如何计算两个时间的间隔?
我们可以使用Period
和Duration
类,前者通常表示间隔的年、月、日
,后者通常表示间隔的小时、分钟、秒
等。
但是我们还可以通过ChronoUnit
并指定单位:
LocalDateTime born = LocalDateTime.of(1949, 10, 1, 10, 00, 00);
LocalDateTime now = LocalDateTime.now();
long years = ChronoUnit.YEARS.between(born, now);
long months = ChronoUnit.MONTHS.between(born, now);
long weeks = ChronoUnit.WEEKS.between(born, now);
long days = ChronoUnit.DAYS.between(born, now);
long hours = ChronoUnit.HOURS.between(born, now);
long minutes = ChronoUnit.MINUTES.between(born, now);
long seconds = ChronoUnit.SECONDS.between(born, now);
System.out.println("years = " + years);
System.out.println("months = " + months);
System.out.println("weeks = " + weeks);
System.out.println("days = " + days);
System.out.println("hours = " + hours);
System.out.println("minutes = " + minutes);
System.out.println("seconds = " + seconds);
// years = 70
// months = 851
// weeks = 3702
// days = 25914
// hours = 621944
// minutes = 37316687
// seconds = 2239001254
TemporalAdjusters
如何计算当月的最后一天?这个月的最后一个星期六?
此时,就轮到TemporalAdjusters
大展身手了。
public class TemporalAdjustersTest {
public static void main(String[] args) {
LocalDate date = LocalDate.now();
LocalDate firstDayOfMonth = date.with(TemporalAdjusters.firstDayOfMonth());
LocalDate firstDayOfYear = date.with(TemporalAdjusters.firstDayOfYear());
LocalDate firstDayOfNextMonth = date.with(TemporalAdjusters.firstDayOfNextMonth());
LocalDate firstDayOfNextYear = date.with(TemporalAdjusters.firstDayOfNextYear());
LocalDate firstFriday = date.with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY));
LocalDate lastDayOfMonth = date.with(TemporalAdjusters.lastDayOfMonth());
LocalDate lastDayOfYear = date.with(TemporalAdjusters.lastDayOfYear());
LocalDate lastSaturday = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.SATURDAY));
System.out.println("firstDayOfMonth = " + firstDayOfMonth);
System.out.println("firstDayOfYear = " + firstDayOfYear);
System.out.println("firstDayOfNextMonth = " + firstDayOfNextMonth);
System.out.println("firstDayOfNextYear = " + firstDayOfNextYear);
System.out.println("firstFriday = " + firstFriday);
System.out.println("lastDayOfMonth = " + lastDayOfMonth);
System.out.println("lastDayOfYear = " + lastDayOfYear);
System.out.println("lastSaturday = " + lastSaturday);
}
}
// firstDayOfMonth = 2020-09-01
// firstDayOfYear = 2020-01-01
// firstDayOfNextMonth = 2020-10-01
// firstDayOfNextYear = 2021-01-01
// firstFriday = 2020-09-04
// lastDayOfMonth = 2020-09-30
// lastDayOfYear = 2020-12-31
// lastSaturday = 2020-09-26
Format & Parse
再来看下如何进行格式化和解析。
public class FormatAndParseTest {
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
System.out.println("-----------------------------------------");
System.out.println("now = " + now);
String formattedDT = now.format(formatter);
System.out.println("formattedDT = " + formattedDT);
System.out.println("-----------------------------------------");
String dtStr = "2020-10-01 08:30:30";
LocalDateTime ldt = LocalDateTime.parse(dtStr, formatter);
System.out.println("dtStr = " + dtStr);
System.out.println("ldt = " + ldt);
System.out.println("-----------------------------------------");
}
}
// -----------------------------------------
// now = 2020-09-12T18:50:37.969089500
// formattedDT = 2020-09-12 18:50:37
// -----------------------------------------
// dtStr = 2020-10-01 08:30:30
// ldt = 2020-10-01T08:30:30
// -----------------------------------------
Backward Compatibility
最后,我们再来看下Java
时间对于Date
和Calendar
的兼容性。
public class CompatibilityTest {
public static void main(String[] args) {
Date date = new Date();
Instant dateInstant = date.toInstant();
LocalDateTime ldt1 = LocalDateTime.ofInstant(dateInstant, ZoneId.systemDefault());
System.out.println("-----------------------------------------");
System.out.println("date = " + date);
System.out.println("ldt1 = " + ldt1);
System.out.println("-----------------------------------------");
Calendar calendar = Calendar.getInstance();
Instant calInstant = calendar.toInstant();
LocalDateTime ldt2 = LocalDateTime.ofInstant(calInstant, ZoneId.of("Asia/Shanghai"));
System.out.println("calendar = " + calendar.getTime());
System.out.println("ldt2 = " + ldt2);
System.out.println("-----------------------------------------");
}
}
// -----------------------------------------
// date = Sat Sep 12 18:51:19 CST 2020
// ldt1 = 2020-09-12T18:51:19.713
// -----------------------------------------
// calendar = Sat Sep 12 18:51:19 CST 2020
// ldt2 = 2020-09-12T18:51:19.757
// -----------------------------------------
- 如何将
LocalDateTime
转换成Date
?
public class TimeUtils {
public static Date localDateTime2Date(LocalDateTime ldt){
return Date.from(ldt.atZone(ZoneId.of("Asia/Shanghai")).toInstant());
}
}
Reference
- Introduction to Java 8 Date and Time API
- Java Date and Calendar examples
- The Evolution of the Java Date Time API
- 14. Evolution of date time API
- Intuitive, Robust Date and Time Handling, Finally Comes to Java
- Java 8 – Convert LocalDate and LocalDateTime to Date
一位喜欢提问、尝试的程序员
(转载本站文章请注明作者和出处 姚屹晨-yaoyichen)