因为现在公司里有大量要处理时间的业务,以前都是用Date+SimpleDateFormat来搞。从Java 8开始,推出了全新的日期时间API,用java.time包的时间处理类功能更强大,而且Date和SimpleDateFormat是可变类型,非线程安全使他们的应用非常受限,另外Date中的很多方法都已经过时了。
新API基于ISO标准日历系统,java.time包下的所有类都是不可变的:
因为篇幅有限,这篇只总结了LocalDateTime,这也是使用的最多的,LocalDate和LocalTime用法类似,另外LocalDateTime的toLocalDate()和toLocalTime()可以转换为LocalDate和LocalTime。
其实Date时间类是可变类,多线程环境下对共享Date变量进行操作,需要自己保证线程安全。
而LocalDateTime是线程安全的:
同样日期格式化类SimpleDateFormat也不是线程安全的,而DateTimeFormatter是线程安全的:
可以获取当前时间的年、月、日、星期几、时、分、秒,代码如下:
package com.linyf.demo.date;
import java.time.LocalDateTime;
public class LocalDateTime1 {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间:" + now);
System.out.println("当前年份:" + now.getYear());
System.out.println("当前月份:" + now.getMonthValue());
System.out.println("当前日份:" + now.getDayOfMonth());
System.out.println("当前周几:" + now.getDayOfWeek().getValue());
System.out.println("当前时:" + now.getHour());
System.out.println("当前分:" + now.getMinute());
System.out.println("当前秒:" + now.getSecond());
}
}
比如想构造2020年2月2号12时30分30秒:
package com.linyf.demo.date;
import java.time.LocalDateTime;
public class LocalDateTime2 {
public static void main(String[] args) {
LocalDateTime of = LocalDateTime.of(2020, 2, 2, 12, 30, 30);
System.out.println(of);
}
}
package com.linyf.demo.date;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class LocalDateTime3 {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间:" + now);
now = now.plusDays(1);// 加1天
now = now.minusHours(1);// 减一小时
now = now.plus(1, ChronoUnit.WEEKS); //加一周
now = now.withMinute(59); // 直接设置分钟为59
System.out.println("修改后时间:" + now);
}
}
执行结果:
另外plus是有很多重载版本的,可以根据需要选择(minute和with一样,可自行查看),另外可以结合ChronoUnit进行操作:
可以使用基本格式,也可以自定义格式,代码如下:
package com.linyf.demo.date;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTime4 {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
String result1 = now.format(DateTimeFormatter.BASIC_ISO_DATE);
String result2 = now.format(DateTimeFormatter.ISO_DATE);
String result3 = now.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println("基本格式1:" + result1);
System.out.println("基本格式2:" + result2);
System.out.println("自定义格式:" + result3);
}
}
就是可以根据给的时间字符串解析成对应的时间对象,代码如下:
package com.linyf.demo.date;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTime5 {
public static void main(String[] args) {
LocalDateTime parse = LocalDateTime.parse("2020-02-01 11:11", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
System.out.println(parse);
}
}
执行结果:
注意:解析的时候,不能用DateTimeFormatter.ISO_DATE或者DateTimeFormatter.BASIC_ISO_DATE,必须用自定义格式;如果只有年月日,则不能进行解析。
有时候需要比较两个时间是否相等或先后,LocalDateTime提供了对应的方法:
package com.linyf.demo.date;
import java.time.LocalDateTime;
public class LocalDateTime6 {
public static void main(String[] args) {
LocalDateTime time1 = LocalDateTime.of(2019, 4, 1, 12, 0);
LocalDateTime time2 = LocalDateTime.of(2020, 4, 1, 12, 0);
System.out.println("time1 在 time2 之前:" + time1.isBefore(time2));
}
}
LocalDateTime有一个很大的问题,就是spring持久化不支持LocalDateTime,有时候我们接收从前端传过来的对象,里面的日期字段只能用Date,但是使用mybatis存到数据库的时候,是支持LocalDateTime的。
可以这样来操作:
package com.linyf.demo.date;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
public class LocalDateTime7 {
public static void main(String[] args) {
Date date = new Date();
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
System.out.println(localDateTime);
}
}
package com.linyf.demo.date;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
public class LocalDateTime8 {
public static void main(String[] args) {
TemporalAdjuster firstDayOfMonth = TemporalAdjusters.firstDayOfMonth();
TemporalAdjuster firstDayOfNextMonth = TemporalAdjusters.firstDayOfNextMonth();
TemporalAdjuster lastDayOfMonth = TemporalAdjusters.lastDayOfMonth();
LocalDateTime now = LocalDateTime.now();
now = now.with(firstDayOfMonth);
System.out.println("本月第一天:" + now);
now = now.with(lastDayOfMonth);
System.out.println("本月最后一天:" + now);
now = now.with(firstDayOfNextMonth);
System.out.println("下月第一天:" + now);
}
}
执行结果:
利用TemporalAdjuster的静态方法可以获取本月第一天、本月最后一天、下个月第一天、下个月最后一天,LocalDateTime的with方法有一个重载版本可以接收TemporalAdjuster,这是我发现的唯一的用法。
package com.linyf.demo.date;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
public class LocalDateTime9 {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间:" + now);
now = now.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
System.out.println(now);
now = LocalDateTime.now();
now = now.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
System.out.println(now);
}
}
执行结果:
TemporalAdjusters的静态方法previous()和next() 加上DayOfWeek可以获得上周或下周任意一天
package com.linyf.demo.date;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class LocalDateTime10 {
public static void main(String[] args) {
TemporalAdjuster firstDayOfMonth = TemporalAdjusters.firstDayOfMonth();
TemporalAdjuster lastDayOfMonth = TemporalAdjusters.lastDayOfMonth();
LocalDateTime now = LocalDateTime.now();
LocalDateTime startDate = now.with(firstDayOfMonth);
System.out.println("本月第一天:" + startDate);
LocalDateTime endDate = now.with(lastDayOfMonth);
System.out.println("本月最后一天:" + endDate);
long numOfDaysBetween = Duration.between(startDate, endDate).toDays();
System.out.println("相隔天数:" + numOfDaysBetween);
List<LocalDateTime> collect = IntStream.iterate(0, i -> i + 1)
.limit(numOfDaysBetween)
.mapToObj(i -> startDate.plusDays(i))
.collect(Collectors.toList());
collect.add(endDate);
collect.forEach(i -> System.out.println(i));
}
}
1、将LocalDateTime字段以时间戳的形式返回给前端,添加日期转换配置类:
package com.linyf.demo;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
public class LocalDateTimeConverter extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeNumber(value.toInstant(ZoneOffset.of("+8")).toEpochMilli());
}
}
然后在LocalDateTime字段上添加@JsonSerialize(using = LocalDateTimeConver.class)注解。
2、将LocalDateTime字段以指定格式返回给前端,在LocalDateTime字段上添加@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = “yyyy-MM-dd HH:mm:ss”)注解。
3、对前端传入的日期进行格式化:
在LocalDateTime字段上添加@DateTimeFormat(pattern = “yyyy-MM-dd HH:mm:ss”)注解。