由于传统的Date,Caleandar,SimpleDateFormat都是可变的,线程不安全的,多线程环境下需要考虑线程安全问题。而java8推出的新的日期API很好的解决了线程安全问题,而且操作简单,绝对优雅。
一、常用API
1.LocalDateTime
java8中将时间和日期进行了区分,用LocalDateTime表示日期和时间,LocalDate表示日期,LocalTime表示时间。从命名也可以看出:LocalDateTime = LocalDate + LocalTime。LocalDateTime类似于 Calendar
式例:
// 获取当前时间
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); //打印:2019-12-05T16:31:06.153
// 获取指定时间
LocalDateTime localDateTime2 = LocalDateTime.of(2019, 12, 5, 16, 28, 59);
System.out.println(localDateTime2); //打印:2019-12-05T16:28:59
// 日期加减
LocalDateTime localDateTime3 = localDateTime2.plusDays(1).plusSeconds(1);
System.out.println(localDateTime3); //打印:2019-12-06T16:29
// 获取具体的年、月、日、时、分、秒 //打印:现在是: 2019 年中的第 339 天
System.out.println("现在是: " + localDateTime.getYear() + " 年中的第 " + localDateTime.getDayOfYear() +" 天");
2.LocalDate
LocalDate是日期的API,不包含时间,用法基本上同LocalDateTime一样(都实现了Temporal接口)。
式例:
// 获取当前日期
LocalDate localDate = LocalDate.now();
System.out.println(localDate); //打印:2019-12-05
// 获取指定时间
LocalDate localDate2 = LocalDate.of(2019, 12, 5);
System.out.println(localDate2); //打印:2019-12-05
// 日期加减
LocalDate localDate3 = localDate2.plusDays(1);
System.out.println(localDate3); //打印:2019-12-06
// 获取具体的年、月、日 //打印:现在是: 2019 年中的第 339 天
System.out.println("现在是: " + localDate.getYear() + " 年中的第 " + localDate.getDayOfYear() +" 天");
3.LocalTime
LocalTime是时间的API,不包含日期,用法基本上同LocalDateTime一样(都实现了Temporal接口)。
式例:
// 获取当前时间
LocalTime localTime = LocalTime.now();
System.out.println(localTime); //打印:16:40:58.014
// 获取指定时间
LocalTime localTime2 = LocalTime.of(16, 12, 45);
System.out.println(localTime2); //打印:16:12:45
// 时间加减
LocalTime localTime3 = localTime2.plusHours(1);
System.out.println(localTime3); //打印:17:12:45
// 获取具体的时、分、秒 //打印:现在是: 16 点 40 分
System.out.println("现在是: " + localTime.getHour() + " 点 " + localDateTime.getMinute() +" 分");
4.Instant
Instant代表的是时间戳,类似于Date,在java.util.Date类与LocalDate、LocalDateTime类之间转换中 均可以通过Instant作为中间类完成转换,Instant的使用还是比较方便的。
tip:地球上不同地区经度不同会划分时区,以零度经线上为准(格林尼治天文台旧址,UTC时区)为准,将地球上各个部分分为了24个时区。向西走,每过一个时区,就要把表拨慢1个小时;同理每向东走一个时区,就要把表拨快1个小时。最后,中国处于东8区。
式例:
// 获取UTC时间(格林尼治时间)
Instant instant = Instant.now();
System.out.println(instant); // 打印结果: 2019-12-05T09:05:16.641Z
// 获取北京时间(东8区) OffsetTime表示有时差的时间,除了UTC时间,都是OffsetTime
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime); // 打印结果: 2019-12-05T17:05:16.641+08:00
// 获取毫秒数
instant.plusSeconds(10).plusMillis(1000);
System.out.println(instant.toEpochMilli()); // 打印:1575536872052
5.Period
Period是用来计算日期间隔的函数,式例:
LocalDate localDate1 = LocalDate.now();
LocalDate localDate2 = localDate1.plusDays(1);
Period period = Period.between(localDate1, localDate2); //打印:间隔:0年,0月,1天
System.out.println("间隔:" + period.getYears() + "年," + period.getMonths() +"月,"+ period.getDays() +"天");
6.Duration
Duration是用来计算间隔时间间隔的,式例:
LocalDateTime localDateTime1 = LocalDateTime.now();
LocalDateTime localDateTime2 = localDateTime1.plusDays(1);
Duration duration = Duration.between(localDateTime1, localDateTime2);
System.out.println("间隔:" + duration.getSeconds() + "秒"); //间隔:86400秒
LocalTime localTime1 = LocalTime.now();
LocalTime localTime2 = localTime1.plusHours(1);
Duration duration1 = Duration.between(localTime1, localTime2);
System.out.println("间隔:" + duration1.getSeconds() + "秒"); //间隔:3600秒
Instant instant1 = Instant.now();
Instant instant2 = instant1.plusSeconds(100);
Duration duration2 = Duration.between(instant1, instant2);
System.out.println("间隔:" + duration2.getSeconds() + "秒"); //间隔:100秒
7.DateTimeFormatter
DateTimeFormatter是日期格式化工具,类似于SimpleDateFormat,但是DateTimeFormatter是线程安全的,式例:
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.now();
String dateStr =dateTimeFormatter.format(localDateTime);
System.out.println(dateStr); // 打印结果: 2019-12-05 17:10:39
LocalDateTime localDateTime = LocalDateTime.parse(dateStr, dateTimeFormatter);
System.out.println(localDateTime); // 打印结果: 2019-12-05T17:10:39
LocalDate localDate = LocalDate.parse(dateStr, dateTimeFormatter);
System.out.println(localDate); // 打印结果: 2019-12-05
二、应用场景
1.求时间差
Date startTime = new Date();
long seconds = Duration.between(startTime.toInstant(), Instant.now()).getSeconds();
2.date 与 LocalDateTime的转换
// date 转化为LocalDateTime
Date date = new Date();
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();
System.out.println(localDateTime); // 打印: 2019-12-05T17:37:27.459
// LocalDateTime 转化为date
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zdt = localDateTime.atZone(zoneId);
Date date = Date.from(zdt.toInstant());
System.out.println(date); // 打印:Thu Dec 05 17:38:37 CST 2019
3.LocalDateTime与LocalDate、LocalTime互转
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime.toLocalDate()); //打印:2019-12-05
System.out.println(localDateTime.toLocalTime()); //打印:17:36:16.927
4.LocalDateTime、LocalDate、LocalTime与字符串的相互转换
String nowDateStr = LocalDate.now().toString();
System.out.println(nowDateStr);//2019-12-05
LocalDate nowDate = LocalDate.parse("2019-12-05");
System.out.println(nowDate.toString());//2019-12-05
String nowTimeStr = LocalTime.now().toString();
System.out.println(nowTimeStr);//17:40:52.823
LocalTime nowTime = LocalTime.parse("12:10:13");
System.out.println(nowTime.toString());//12:10:13
System.out.println(LocalDateTime.now().toString());//2019-12-05T17:40:52.824
System.out.println(LocalDateTime.now().toLocalDate().toString());//2019-12-05
System.out.println(LocalDateTime.now().toLocalTime().toString());//17:40:52.824
三、DateUtil工具类
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @description:
* @author: zhangcb
* @create: 2019-12-20 10:11
**/
public class DateUtil {
/**
* 求两个日期时间差
* @param startTime 开始时间
* @param endTime 结束时间
* @return 相隔秒
*/
public static long between(Date startTime, Date endTime){
return Duration.between(startTime.toInstant(), endTime.toInstant()).getSeconds();
}
/**
* 求两个日期时间差
* @param startTime 开始时间
* @param endTime 结束时间
* @return 相隔秒
*/
public static long between(LocalDateTime startTime, LocalDateTime endTime){
return Duration.between(startTime, endTime).getSeconds();
}
/**
* 格式化日期
* @param date 待格式化的日期
* @param pattern 格式化正则
* @return 格式化结果串
*/
public static String format(Date date, String pattern){
return new SimpleDateFormat(pattern).format(date);
}
/**
* 格式化日期
* @param localDateTime 待格式化的日期
* @param pattern 格式化正式
* @return 格式化结果串
*/
public static String format(LocalDateTime localDateTime, String pattern){
return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
}
/**
* 格式化日期
* @param localDate 待格式化的日期
* @param pattern 格式化正则, 这里使用的类型 {@link LocalDate}, 所以正则只能设定到天
* @return 格式化结果串
*/
public static String format(LocalDate localDate, String pattern){
return localDate.format(DateTimeFormatter.ofPattern(pattern));
}
/**
* 将 {@link LocalDateTime} 转换成 {@link Date}
* @param localDateTime {@link LocalDateTime} 待转换的日期
* @return 转换成Date结果
*/
public static Date from(LocalDateTime localDateTime){
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
return Date.from(instant);
}
/**
* 将 {@link Date} 转换成 {@link LocalDateTime}
* @param date {@link Date} 待转换的日期
* @return 转换成 {@link LocalDateTime} 结果
*/
public static LocalDateTime from(Date date){
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
/**
* 获取{@link Date}在开始时间和结束时间内的日期时间段{@link Date}集合
* @param start 开始时间
* @param end 结束时间
* @return 时间天数集合
*/
public static List<Date> dateZones(Date start, Date end){
return dateZones(from(start), from(end));
}
/**
* 获取 {@link LocalDate} 在开始时间和结束时间内的日期时间段 {@link LocalDate} 集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<Date> dateZones(LocalDate start, LocalDate end){
return Stream.iterate(start, x -> x.plusDays(1))
.limit(ChronoUnit.DAYS.between(start, end) + 1)
.map(e -> Date.from(e.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()))
.collect(Collectors.toList());
}
/**
* 获取{@link LocalDateTime} 在开始时间和结束时间内的日期时间段{@link Date}集合
* @param start 开始时间
* @param end 结束时间
* @return 时间天数集合
*/
public static List<Date> dateZones(LocalDateTime start, LocalDateTime end){
// 用起始时间作为流的源头,按照每次加一天的方式创建一个无限流
return Stream.iterate(start.toLocalDate(), x -> x.plusDays(1))
// 截断无限流,长度为起始时间和结束时间的差+1个
.limit(ChronoUnit.DAYS.between(start, end) + 1)
// 由于最后要的是字符串,所以map转换一下
.map(e -> Date.from(e.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()))
// 把流收集为List
.collect(Collectors.toList());
}
/**
* 获取{@link Date}在开始时间和结束时间内的日期时间段{@link LocalDate}集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<LocalDate> localDateZones(Date start, Date end){
return localDateZones(from(start), from(end));
}
/**
* 获取 {@link LocalDate} 在开始时间和结束时间内的日期时间段 {@link LocalDate} 集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<LocalDate> localDateZones(LocalDate start, LocalDate end){
return Stream.iterate(start, x -> x.plusDays(1))
.limit(ChronoUnit.DAYS.between(start, end) + 1)
.collect(Collectors.toList());
}
/**
* 获取 {@link LocalDateTime} 在开始时间和结束时间内的日期时间段 {@link LocalDate} 集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<LocalDate> localDateZones(LocalDateTime start, LocalDateTime end){
// 用起始时间作为流的源头,按照每次加一天的方式创建一个无限流
return Stream.iterate(start.toLocalDate(), x -> x.plusDays(1))
// 截断无限流,长度为起始时间和结束时间的差+1个
.limit(ChronoUnit.DAYS.between(start, end) + 1)
.map(e -> e.atStartOfDay().toLocalDate())
// 把流收集为List
.collect(Collectors.toList());
}
}