# normal
# 日期的操作和格式化
# 增加和减少日期
LocalDate date = LocalDate.of(2017, 1, 5); // 2017-01-05
LocalDate date1 = date.withYear(2016); // 修改为 2016-01-05
LocalDate date2 = date.withMonth(2); // 修改为 2017-02-05
LocalDate date3 = date.withDayOfMonth(1); // 修改为 2017-01-01
LocalDate date4 = date.plusYears(1); // 增加一年 2018-01-05
LocalDate date5 = date.minusMonths(2); // 减少两个月 2016-11-05
LocalDate date6 = date.plus(5, ChronoUnit.DAYS); // 增加5天 2017-01-10
2
3
4
5
6
7
8
9
TemporalAdjusters
上面例子中对于日期的操作比较简单,但是有些时候我们要面临更复杂的时间操作,比如将时间调到下一个工作日,或者是下个月的最后一天,这时候我们可以使用with()方法的另一个重载方法,它接收一个TemporalAdjuster参数,可以使我们更加灵活的调整日期:
LocalDate date7 = date.with(nextOrSame(DayOfWeek.SUNDAY)); // 返回下一个距离当前时间最近的星期日
LocalDate date9 = date.with(lastInMonth(DayOfWeek.SATURDAY)); // 返回本月最后一个星期六
2
要使上面的代码正确编译,你需要使用静态导入TemporalAdjusters对象:
import static java.time.temporal.TemporalAdjusters.*;
| 方法名 | 描述 |
|---|---|
| dayOfWeekInMonth | 返回同一个月中每周的第几天 |
| firstDayOfMonth | 返回当月的第一天 |
| firstDayOfNextMonth | 返回下月的第一天 |
| firstDayOfNextYear | 返回下一年的第一天 |
| firstDayOfYear | 返回本年的第一天 |
| firstInMonth | 返回同一个月中第一个星期几 |
| lastDayOfMonth | 返回当月的最后一天 |
| lastDayOfNextMonth | 返回下月的最后一天 |
| lastDayOfNextYear | 返回下一年的最后一天 |
| lastDayOfYear | 返回本年的最后一天 |
| lastInMonth | 返回同一个月中最后一个星期几 |
| next / previous | 返回后一个/前一个给定的星期几 |
nextOrSame / previousOrSame | 返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回 |
@Test
public void testWeek(){
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime.getDayOfWeek());
System.out.println(localDateTime.getDayOfMonth());
System.out.println(localDateTime.withDayOfMonth(7));
System.out.println(localDateTime.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY)));
System.out.println(localDateTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)));
}
// MONDAY
// 6
// 2020-04-07T15:30:17.031
// 2020-04-06T15:30:17.031
// 2020-04-06T15:30:17.031
2
3
4
5
6
7
8
9
10
11
12
13
14
自定义的TemporalAdjuster
如果上面表格中列出的方法不能满足你的需求,你还可以创建自定义的TemporalAdjuster接口的实现,TemporalAdjuster也是一个函数式接口,所以我们可以使用Lambda表达式:
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}
2
3
4
比如给定一个日期,计算该日期的下一个工作日(不包括星期六和星期天):
LocalDate date = LocalDate.of(2017, 1, 5);
date.with(temporal -> {
// 当前日期
DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
// 正常情况下,每次增加一天
int dayToAdd = 1;
// 如果是星期五,增加三天
if (dayOfWeek == DayOfWeek.FRIDAY) {
dayToAdd = 3;
}
// 如果是星期六,增加两天
if (dayOfWeek == DayOfWeek.SATURDAY) {
dayToAdd = 2;
}
return temporal.plus(dayToAdd, ChronoUnit.DAYS);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 格式化日期
新的日期API中提供了一个
DateTimeFormatter类用于处理日期格式化操作,它被包含在java.time.format包中,Java 8的日期类有一个format()方法用于将日期格式化为字符串,该方法接收一个DateTimeFormatter类型参数:
LocalDateTime dateTime = LocalDateTime.now();
String strDate1 = dateTime.format(DateTimeFormatter.BASIC_ISO_DATE); // 20170105
String strDate2 = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2017-01-05
String strDate3 = dateTime.format(DateTimeFormatter.ISO_LOCAL_TIME); // 14:20:16.998
String strDate4 = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // 2017-01-05
String strDate5 = dateTime.format(DateTimeFormatter.ofPattern("今天是:YYYY年 MMMM DD日 E", Locale.CHINESE)); // 今天是:2017年 一月 05日 星期四
2
3
4
5
6
同样,日期类也支持将一个字符串解析成一个日期对象,例如:
String strDate6 = "2017-01-05";
String strDate7 = "2017-01-05 12:30:05";
LocalDate date = LocalDate.parse(strDate6, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
LocalDateTime dateTime1 = LocalDateTime.parse(strDate7, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
2
3
4
5
# 时区
Java 8中的时区操作被很大程度上简化了,新的时区类java.time.ZoneId是原有的java.util.TimeZone类的替代品。ZoneId对象可以通过ZoneId.of()方法创建,也可以通过ZoneId.systemDefault()获取系统默认时区:
ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai");
ZoneId systemZoneId = ZoneId.systemDefault();
2
of()方法接收一个“区域/城市”的字符串作为参数,你可以通过
getAvailableZoneIds()方法获取所有合法的“区域/城市”字符串:
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
对于老的时区类TimeZone,Java 8也提供了转化方法
ZoneId oldToNewZoneId = TimeZone.getDefault().toZoneId();
有了ZoneId,我们就可以将一个LocalDate、LocalTime或LocalDateTime对象转化为ZonedDateTime对象:
LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, shanghaiZoneId);
2
将zonedDateTime打印到控制台为:2017-01-05T15:26:56.147+08:00[Asia/Shanghai]
ZonedDateTime对象由两部分构成,LocalDateTime和ZoneId,其中2017-01-05T15:26:56.147部分为LocalDateTime,+08:00[Asia/Shanghai]部分为ZoneId。
另一种表示时区的方式是使用ZoneOffset,它是以当前时间和**世界标准时间(UTC)/格林威治时间(GMT)**的偏差来计算,例如:
ZoneOffset zoneOffset = ZoneOffset.of("+09:00");
LocalDateTime localDateTime = LocalDateTime.now();
OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime, zoneOffset);
2
3
# 其他历法
Java中使用的历法是ISO 8601日历系统,它是世界民用历法,也就是我们所说的公历。平年有365天,闰年是366天。闰年的定义是:非世纪年,能被4整除;世纪年能被400整除。为了计算的一致性,公元1年的前一年被当做公元0年,以此类推。 此外Java 8还提供了4套其他历法(很奇怪为什么没有汉族人使用的农历),每套历法都包含一个日期类,分别是:
- ThaiBuddhistDate:泰国佛教历
- MinguoDate:中华民国历
- JapaneseDate:日本历
- HijrahDate:伊斯兰历
每个日期类都继承ChronoLocalDate类,所以可以在不知道具体历法的情况下也可以操作。不过这些历法一般不常用,除非是有某些特殊需求情况下才会使用。
这些不同的历法也可以用于向公历转换:
LocalDate date = LocalDate.now();
JapaneseDate jpDate = JapaneseDate.from(date);
2
由于它们都继承ChronoLocalDate类,所以在不知道具体历法情况下,可以通过ChronoLocalDate类操作日期:
Chronology jpChronology = Chronology.ofLocale(Locale.JAPANESE);
ChronoLocalDate jpChronoLocalDate = jpChronology.dateNow();
2
我们在开发过程中应该尽量避免使用ChronoLocalDate,尽量用与历法无关的方式操作时间,因为不同的历法计算日期的方式不一样,比如开发者会在程序中做一些假设,假设一年中有12个月,如果是中国农历中包含了闰月,一年有可能是13个月,但开发者认为是12个月,多出来的一个月属于明年的。再比如假设年份是累加的,过了一年就在原来的年份上加一,但日本天皇在换代之后需要重新纪年,所以过了一年年份可能会从1开始计算。
# 计算一周后的时间
同样可以计算月,一年等之后的日期
LocalDate weekLater = today.plus(1, ChronoUnit.WEEKS);
log.info("一周后日期:【{}】",weekLater);
2
# 检查生日这种周期性日期
@Test
public void testBirth(){
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1988,9,2);
MonthDay monthDay = MonthDay.of(birthday.getMonth(),birthday.getDayOfMonth());
MonthDay currentMonthDay = MonthDay.from(today);
log.info(monthDay.equals(currentMonthDay)?"生日到了":"还没到你的生日");
}
2
3
4
5
6
7
8
# 获取今天的日期
LocalDate localDate = LocalDate.now();
# 获取年月日信息
LocalDate today = LocalDate.now();
int year = today.getYear();
int month = today.getMonthValue();
int day = today.getDayOfMonth();
2
3
4
# 获取特定日期
LocalDate date = LocalDate.of(2018,2,6);
# 判断日期是否相等
@Test
public void testBeforeAfter(){
LocalDateTime t1 = LocalDateTime.of(2019,11,11,12,24,24);
LocalDateTime t21 = LocalDateTime.of(2019,11,11,12,24,24);
LocalDateTime t22 = LocalDateTime.of(2019,11,11,12,24,55);
LocalDateTime t3 = LocalDateTime.of(2019,11,11,12,24,55);
System.out.println(t1.isAfter(t21));
System.out.println(t1.isBefore(t21));
System.out.println(t3.isAfter(t1));
Range<LocalDateTime> timeRange = Range.closedOpen(t1,t3);
System.out.println(timeRange.contains(t21));
System.out.println(timeRange.contains(t22));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
输出
false
false
true
true
false
2
3
4
5
# 老一套的date
# date比较大小
拿到getTime进行比较
s1.getCreateTime().getTime() > s2.getCreateTime().getTime()