参考资料
在Java 8之前,所有关于时间和日期的API都存在各种使用方面的缺陷,主要有:
由于以上这些问题,出现了一些三方的日期处理框架,例如Joda-Time,date4j等开源项目。但是,Java需要一套标准的用于处理时间和日期的框架,于是Java 8中引入了新的日期API。新的日期API是JSR-310规范的实现,Joda-Time框架的作者正是JSR-310的规范的倡导者,所以能从Java 8的日期API中看到很多Joda-Time的特性。
⏹只有年月日的时间
// 构造2017年1月1日的时间对象
LocalDate localDate1 = LocalDate.of(2017, 1, 1);
// 获取当前时间
LocalDate localDate2 = LocalDate.now();
// 通过年月对象来构造
LocalDate localDate3 = YearMonth.of(2022, 12).atDay(1);
// 通过LocalDateTime获取LocalDate
LocalDate localDate4 = LocalDateTime.now().toLocalDate();
LocalDate localDate5 = LocalDate.from(LocalDateTime.now());
⏹只有时间,没有年月日
// 构造时分秒对象
LocalTime localTime1 = LocalTime.of(10, 15, 12);
// 构造时分对象
LocalTime localTime2 = LocalTime.of(10, 15);
// 构造当前时间对象
LocalTime localTime3 = LocalTime.now();
// 通过LocalDateTime获取LocalTime
LocalTime localTime4 = LocalDateTime.now().toLocalTime();
LocalTime localTime5 = LocalTime.from(LocalDateTime.now());
⏹有年月日和时间
// 构造年月日时分秒对象
LocalDateTime localDateTime1 = LocalDateTime.of(2023, 5, 15, 10, 15, 10);
// 构造年月日时分对象
LocalDateTime localDateTime2 = LocalDateTime.of(2023, 5, 15, 10, 15);
// 构造当前时间对象
LocalDateTime localDateTime3 = LocalDateTime.now();
// 通过LocalDate + LocalTime来构造
LocalDateTime localDateTime4 = LocalDate.of(2023, 5, 15).atTime(10, 15, 10);
LocalDateTime localDateTime5 = LocalDate.of(2023, 5, 15).atTime(LocalTime.of(10, 15, 10));
// 一天的开头和结尾LocalDateTime
LocalDateTime localDateTime6 = LocalDate.of(2023, 5, 15).atStartOfDay();
LocalDateTime localDateTime7 = LocalDate.of(2023, 5, 15).atTime(LocalTime.MIN);
LocalDateTime localDateTime8 = LocalDate.of(2023, 5, 15).atTime(LocalTime.MAX);
// 通过Instant来构建(需要指定时区)
LocalDateTime localDateTime9 = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
⏹Instant用于表示一个时间戳,它与我们常使用的System.currentTimeMillis()有些类似,不过
// 获取当前时间的时间戳(UTC时间,非北京/东京时间)
Instant instant1 = Instant.now();
// 获取北京时间的时间戳
Instant instant2 = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8));
// 通过LocalDateTime构造Instant
Instant instant3 = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant();
// 通过Date构造Instant
Instant instant4 = new Date().toInstant();
⏹年月时间对象
YearMonth yearMonth1 = YearMonth.of(2022, 12);
YearMonth yearMonth2 = YearMonth.now();
// 通过LocalDate和LocalDateTime来构造
YearMonth yearMonth3 = YearMonth.from(LocalDate.now());
YearMonth yearMonth4 = YearMonth.from(LocalDateTime.now());
⏹月日时间对象,多用来表示周期性事件,例如生日和纪念日
MonthDay monthDay1 = MonthDay.of(2022, 12);
MonthDay monthDay2 = MonthDay.from(LocalDateTime.now());
MonthDay monthDay3 = MonthDay.now();
⏹Period是以年月日来衡量一个时间段的对象。
⏹可以通过.between()
来创建一个段时间,由于以年月日来衡量,因此接收的参数只能是LocalDate类型的,不能是LocalDateTime
类型的。
// 2年3个月6天的时间段
Period period1 = Period.of(2, 3, 6);
// 2017-01-05 到 2017-02-05 这段时间
Period period2 = Period.between(
LocalDate.of(2017, 1, 5),
LocalDate.of(2017, 2, 5)
);
// 当前日期和2023年11月11日之间的这段时间
Period untilPeriod = LocalDate.now().until(LocalDate.of(2023, 11, 11));
// 1整年,1个月,1个周,1整天的时间段
Period periodYears = Period.ofYears(1);
Period periodMonths = Period.ofMonths(1);
Period periodWeeks = Period.ofWeeks(1);
Period periodDays = Period.ofDays(1);
// 3年3月3天的时间段
Period year3Month3Day3 = Period.ofYears(3)
.plus(Period.ofMonths(3))
.plus(Period.ofDays(3));
⏹Duration是以年月日时分秒
来衡量一个时间段的对象。
// ChronoUnit.YEARS ChronoUnit.MONTHS ChronoUnit.WEEKS 无法使用,会报错.
// 5天
Duration duration4 = Duration.of(5, ChronoUnit.DAYS);
// 5个小时
Duration duration5 = Duration.of(5, ChronoUnit.HOURS);
// 5分钟
Duration duration6 = Duration.of(5, ChronoUnit.MINUTES);
// 5秒
Duration duration7 = Duration.of(5, ChronoUnit.SECONDS);
// 1000毫秒
Duration duration8 = Duration.of(1000, ChronoUnit.MILLIS);
// 2017-01-05 10:07:00 到 2017-02-05 10:07:00 这个时间段
Duration duration9 = Duration.between(
// 2017-01-05 10:07:00
LocalDateTime.of(2017, Month.JANUARY, 5, 10, 7, 0),
// 2017-02-05 10:07:00
LocalDateTime.of(2017, Month.FEBRUARY, 5, 10, 7, 0)
);
// 3天3小时的时间段对象
Duration duration10 = Duration.ofDays(3).plusHours(3);
⏹获取时间戳的四种方式
System.currentTimeMillis();
Clock.systemDefaultZone().millis();
Calendar.getInstance().getTimeInMillis();
Instant.now().toEpochMilli()
int year = LocalDate.now().getYear();
int monthValue1 = LocalDate.now().getMonthValue();
Month month = LocalDate.now().getMonth();
int monthValue2 = month .getValue();
LocalDate localDate = YearMonth.of(2022, 12).atEndOfMonth();
int dayCount = LocalDate.of(2016, 2, 1).lengthOfMonth();
System.out.println(dayCount); // 29
ValueRange range = LocalDate.of(2023, 9, 13).range(ChronoField.DAY_OF_MONTH);
range.getMaximum(); // 30
YearMonth yearMonth = YearMonth.of(2023, Month.JANUARY);
System.out.println(yearMonth.lengthOfMonth()); // 31
⏹日期是月份的第几号
int dayOfMonth = LocalDate.now().getDayOfMonth();
⏹当前日期在本年度处于第几天
int dayOfYear1 = LocalDate.of(2023, 9, 13).getDayOfYear(); // 256
int dayOfYear2 = LocalDate.of(2023, 9, 13).get(ChronoField.DAY_OF_YEAR); // 256
⏹当前日期是本周的周几
int dayOfWeek = LocalDate.now().getDayOfWeek().getValue();
int num = LocalDate.of(2021, 8, 28).get(ChronoField.ALIGNED_WEEK_OF_MONTH); // 5
int num = LocalDate.of(2021, 8, 28).get(ChronoField.ALIGNED_WEEK_OF_YEAR); // 35
⏹对齐周的取值范围为1~7
⏹对齐周是从日历上看,当前日所在月份按周向上对齐。
⏹2023年8月28号所在的对齐周是7,因为31是7月所在的日期,不是8月的。
int num1 = LocalDate.of(2023, 8, 15).get(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH); // 1
int num2 = LocalDate.of(2023, 8, 28).get(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH); // 7
⏹方式1
// 开始日
LocalDate startLocalDate = LocalDate.of(2023, 5, 10);
// 结束日
LocalDate endLocalDate = LocalDate.of(2023, 6, 8);
// 开始日和结束日之间的天数间隔
long distance = ChronoUnit.DAYS.between(startLocalDate, endLocalDate);
// 指定格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
List<String> localDateList =
// 使用生成流生成日期
Stream.iterate(startLocalDate, d -> d.plusDays(1))
.limit(distance + 1)
// 将日期全部格式化
.map(localDate_ -> localDate_.format(formatter))
.collect(toList());
System.out.println(localDateList);
/*
2023-05-10
, 2023-05-11
, 2023-05-12
, 2023-05-13
, 2023-05-14
, 2023-05-15
, 2023-05-16
, ......
*/
⏹方式2:参考该文章
Java 获取两个日期之间的所有日期
long betweenYears = ChronoUnit.YEARS.between(LocalDate.of(2017, 1, 1), LocalDate.of(2022, 10, 16));
System.out.println(betweenYears); // 5
long untilYears = LocalDate.of(2017, 1, 1).until(LocalDate.of(2022, 10, 16), ChronoUnit.YEARS);
System.out.println(untilYears); // 5
long betweenMonths = ChronoUnit.MONTHS.between(LocalDate.of(2017, 1, 1), LocalDate.of(2018, 5, 16));
System.out.println(betweenMonths); // 16
long untilMonths = LocalDate.of(2017, 1, 1).until(LocalDate.of(2018, 5, 16), ChronoUnit.MONTHS);
System.out.println(untilMonths); // 16
long betweenDays = ChronoUnit.DAYS.between(LocalDate.of(2017, 2, 13), LocalDate.of(2017, 5, 18));
System.out.println(betweenDays); // 94
long untilDays = LocalDate.of(2017, 2, 13).until(LocalDate.of(2017, 5, 18), ChronoUnit.DAYS);
System.out.println(untilDays); // 94
LocalDateTime fromLocalDateTime = LocalDate.of(2017, 2, 13).atTime(8, 0, 0);
LocalDateTime toLocalDateTime = LocalDate.of(2017, 5, 18).atTime(8, 30, 0);
long days = Duration.between(fromLocalDateTime, toLocalDateTime).toDays();
System.out.println(days); // 94
⏹间隔时
long untilHours = LocalTime.of(10, 15).until(LocalTime.of(12, 45), ChronoUnit.HOURS);
System.out.println(untilHours); // 2
⏹间隔分
long untilMinutes = LocalTime.of(10, 15).until(LocalTime.of(12, 45), ChronoUnit.MINUTES);
System.out.println(untilMinutes); // 150
方法名 | 描述 |
---|---|
dayOfWeekInMonth |
返回同一个月中每周的第几天 |
firstDayOfMonth |
返回当月的第一天 |
firstDayOfNextMonth |
返回下月的第一天 |
firstDayOfNextYear |
返回下一年的第一天 |
firstDayOfYear |
返回本年的第一天 |
firstInMonth |
返回同一个月中第一个星期几 |
lastDayOfMonth |
返回当月的最后一天 |
lastDayOfNextMonth |
返回下月的最后一天 |
lastDayOfNextYear |
返回下一年的最后一天 |
lastDayOfYear |
返回本年的最后一天 |
lastInMonth |
返回同一个月中最后一个星期几 |
next/previous |
返回后一个/前一个给定的星期几 |
nextOrSame/previousOrSame |
返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回 |
LocalDate of1 = LocalDate.of(2022, 10, 16);
// 本月第一天
LocalDate result1 = of1.with(TemporalAdjusters.firstDayOfMonth()); // 2022-10-01
// 本月最后一天
LocalDate result2 = of1.with(TemporalAdjusters.lastDayOfMonth()); // 2022-10-31
// 本月第一个周五
LocalDate of1 = LocalDate.of(2022, 10, 16);
LocalDate result3 = of1.with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY)); // 2022-10-07
// 上一个周六
LocalDate result4 = LocalDate.of(2023, 9, 16).with(TemporalAdjusters.previous(DayOfWeek.SATURDAY));
System.out.println(result4); // 2023-09-09
// 下一个周五
LocalDate result5 = LocalDate.of(2023, 9, 16).with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println(result5); // 2023-09-22
LocalDate salaryDate = this.getSalaryDate(2023, 6);
System.out.println(salaryDate); // 2023-06-23
// 下一个工资日,工资是每个月的25号发,如果25号是周六或者周日的话,就改为该周的周五发.
private LocalDate getSalaryDate(Integer year, Integer month) {
// 自定义一个时间矫正器,用来计算工资日
TemporalAdjuster salaryTemporalAdjuster = temporal -> {
LocalDate date = LocalDate.class.cast(temporal);
// 获取当前月的25号的LocalDate对象
LocalDate localDate25 = date.plusDays(24);
// 获取指定月份25号的DayOfWeek对象
DayOfWeek salaryWeek = localDate25.getDayOfWeek();
// 如果指定月份的25号既不是周六也不是周日的话,直接返回当月的25号的LocalDate
if (DayOfWeek.SATURDAY.compareTo(salaryWeek) != 0 && DayOfWeek.SUNDAY.compareTo(salaryWeek) != 0) {
return localDate25;
}
// 如果指定月的25号是周六或者周日的话,那么将日期提前到上一个周五当做工资日
return localDate25.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
};
return LocalDate.of(year, month, 1).with(salaryTemporalAdjuster);
}
LocalDate localDate1 = LocalDate.of(2023, 8, 14).withYear(1997);
// 1997-08-14
LocalDate localDate2 = LocalDate.of(2023, 8, 14).withMonth(5);
// 2023-05-14
LocalDate localDate3 = LocalDate.of(2023, 8, 14).withDayOfMonth(29);
// 2023-08-29
LocalDate localDate4 = LocalDate.of(2023, 8, 14).withDayOfYear(6);
// 2023-01-06
// 3天前
LocalDate.now().minusDays(3);
// 3天后
LocalDate.now().plusDays(3);
// 构造3天的时间段对象
Period period3Day = Period.ofDays(3);
// 3天前
LocalDate.now().plus(period3Day);
// 3天后
LocalDate.now().minus(period3Day);
// 3天前
LocalDate.now().plus(3, ChronoUnit.DAYS);
// 3天后
LocalDate.now().minus(3, ChronoUnit.DAYS);
// 1周前
LocalDate.now().minusWeeks(1);
// 1周后
LocalDate.now().plusWeeks(1);
// 1月前
LocalDate.now().minusMonths(1);
// 1月后
LocalDate.now().plusMonths(1);
// 1月前
LocalDate.now().minus(1, ChronoUnit.MONTHS);
// 1月后
LocalDate.now().plus(1, ChronoUnit.MONTHS);
// 1年前
LocalDate.now().minusYears(1);
// 1年后
LocalDate.now().plusYears(1);
LocalDate.of(2023, 8, 14).isBefore(LocalDate.of(2023, 8, 15)); // true
LocalDate.of(2023, 8, 14).compareTo(LocalDate.of(2023, 8, 15)); // -1
LocalDate.of(2023, 8, 14).isAfter(LocalDate.of(2023, 8, 15)); // false
LocalDate.of(2023, 8, 14).compareTo(LocalDate.of(2023, 8, 15)); // -1
⏹普通日期判断相等
// 使用.equals()判断
LocalDate.of(2023, 9, 12).equals(LocalDate.now());
// 使用.compareTo()判断
LocalDate.of(2023, 8, 14).compareTo(LocalDate.of(2023, 8, 14)); // 0
⏹生日判断相等
LocalDate birthdayLocalDate = LocalDate.of(1994, 12, 16);
// 构造月日对象
MonthDay birthMonthDay = MonthDay.of(
birthdayLocalDate.getMonth()
, birthdayLocalDate.getDayOfMonth()
);
MonthDay.now().equals(birthMonthDay);
LocalDate.now().isLeapYear();
Year.isLeap(2020);
// 构造一个日期格式化对象
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
// 字符串 --> 日期
LocalDate parseLocalDate = LocalDate.parse("2016年10月25日", formatter);
LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy年MM月"));
DateTimeFormatter.ofPattern("yyyy年MM月").format(LocalDate.now());
⏹从包含的信息量而言,LocalDateTime > LocalDate > LocalTime,因此可以自上而下转换,不能自下而上转换。
⏹LocalDate和Date类、时间戳之间转换
LocalDate localDate = LocalDate.from(LocalDateTime.now());
LocalTime localTime = LocalTime.from(LocalDateTime.now());
// 构造一个LocalDateTime对象
LocalDateTime localDateTime1 = LocalDate.of(2023, 5, 15).atTime(LocalTime.MAX);
// LocalDateTime --> Instant
Instant instant = localDateTime1.atZone(ZoneId.systemDefault()).toInstant();
// Instant --> LocalDateTime
LocalDateTime localDateTime2 = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
YearMonth yearMonth = YearMonth.from(LocalDate.now());
MonthDay monthDay = MonthDay.from(LocalDate.now());
⏹Date --> LocalDateTime
Date date = new Date();
// Date 转换 Instant
Instant instantOfDate = date.toInstant();
// Instant 转换 LocalDateTime
LocalDateTime localDateTime = LocalDateTime.ofInstant(instantOfDate, ZoneId.systemDefault());
⏹LocalDateTime --> Date
Instant instantLocalDateTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant();
Date dateLocalDateTime = Date.from(instantLocalDateTime);
⏹Date --> LocalDate
public LocalDate date2LocalDate(Date date) {
ZonedDateTime zdt = date.toInstant().atZone(ZoneId.systemDefault());
return zdt.toLocalDate();
}
⏹LocalDate --> Date
public Date localDate2Date(LocalDate localDate){
ZonedDateTime zdt = localDate.atStartOfDay(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
// 开始时间
Instant startInstant = Instant.now();
// 模拟程序运行耗时
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 终了时间
Instant endInstant = Instant.now();
// 获取开始和终了时间的时间段对象
Duration taskTime = Duration.between(startInstant, endInstant);
long second = taskTime.toSeconds();
// 高并发环境下Data的正确使用方式,要和ThreadLocal进行绑定
private static ThreadLocal<DateFormat> threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 和线程绑定 保证安全
public static String format(Date date) {
return threadLocal.get().format(date);
}