在JDK1.8
以前时间工具类一般都是Date
、Calendar
、SimpleDateFormat
等,这些工具类使用相对繁琐,而且线程不安全,所以在JDK1.8
对日期类做了重新规划,解决工具类线程不安全问题,而且提升了性能,部分工具类展示如下。
- LocalDate:本地日期,不包含具体时间,所以时间格式为
yyyy-MM-dd
。 - LocalTime:本地时间,不包含日期,时间格式可以为:
HH:mm:ss
或者HH:mm
。 - LocalDateTime:本地日期时间,但不包含时差和时区信息,日期时间格式:
yyyy-MM-dd HH:mm:ss
。 - ZonedDateTime:最完整的日期时间,包含时区和相对
UTC
或格林威治的时差。
常用方法
adjustInto 调整指定的Temporal和当前LocalDateTime对
. atOffset 结合LocalDateTime和ZoneOffset创建一个
. atZone 结合LocalDateTime和指定时区创建一个ZonedD
. compareTo 比较两个LocalDateTime
. format 格式化LocalDateTime生成一个字符串
. from 转换TemporalAccessor为LocalDateTi
. get 得到LocalDateTime的指定字段的值
. getDayOfMonth 得到LocalDateTime是月的第几天
. getDayOfWeek 得到LocalDateTime是星期几
. getDayOfYear 得到LocalDateTime是年的第几天
. getHour 得到LocalDateTime的小时
. getLong 得到LocalDateTime指定字段的值
. getMinute 得到LocalDateTime的分钟
. getMonth 得到LocalDateTime的月份
. getMonthValue 得到LocalDateTime的月份,从1到12
. getNano 得到LocalDateTime的纳秒数
. getSecond 得到LocalDateTime的秒数
. getYear 得到LocalDateTime的年份
. isAfter 判断LocalDateTime是否在指定LocalDateT
. isBefore 判断LocalDateTime是否在指定LocalDateT
. isEqual 判断两个LocalDateTime是否相等
. isSupported 判断LocalDateTime是否支持指定时间字段或单元
. minus 返回LocalDateTime减去指定数量的时间得到的值
. minusDays 返回LocalDateTime减去指定天数得到的值
. minusHours 返回LocalDateTime减去指定小时数得到的值
. minusMinutes 返回LocalDateTime减去指定分钟数得到的值
. minusMonths 返回LocalDateTime减去指定月数得到的值
. minusNanos 返回LocalDateTime减去指定纳秒数得到的值
. minusSeconds 返回LocalDateTime减去指定秒数得到的值
. minusWeeks 返回LocalDateTime减去指定星期数得到的值
. minusYears 返回LocalDateTime减去指定年数得到的值
. now 返回指定时钟的当前LocalDateTime
. of 根据年、月、日、时、分、秒、纳秒等创建LocalDateTi
. ofEpochSecond 根据秒数(从1970-01-0100:00:00开始)创建L
. ofInstant 根据Instant和ZoneId创建LocalDateTim
. parse 解析字符串得到LocalDateTime
. plus 返回LocalDateTime加上指定数量的时间得到的值
. plusDays 返回LocalDateTime加上指定天数得到的值
. plusHours 返回LocalDateTime加上指定小时数得到的值
. plusMinutes 返回LocalDateTime加上指定分钟数得到的值
. plusMonths 返回LocalDateTime加上指定月数得到的值
. plusNanos 返回LocalDateTime加上指定纳秒数得到的值
. plusSeconds 返回LocalDateTime加上指定秒数得到的值
. plusWeeks 返回LocalDateTime加上指定星期数得到的值
. plusYears 返回LocalDateTime加上指定年数得到的值
. query 查询LocalDateTime
. range 返回指定时间字段的范围
. toLocalDate 返回LocalDateTime的LocalDate部分
. toLocalTime 返回LocalDateTime的LocalTime部分
. toString 返回LocalDateTime的字符串表示
. truncatedTo 返回LocalDateTime截取到指定时间单位的拷贝
. until 计算LocalDateTime和另一个LocalDateTi
. with 返回LocalDateTime指定字段更改为新值后的拷贝
. withDayOfMonth 返回LocalDateTime月的第几天更改为新值后的拷贝
. withDayOfYear 返回LocalDateTime年的第几天更改为新值后的拷贝
. withHour 返回LocalDateTime的小时数更改为新值后的拷贝
. withMinute 返回LocalDateTime的分钟数更改为新值后的拷贝
. withMonth 返回LocalDateTime的月份更改为新值后的拷贝
. withNano 返回LocalDateTime的纳秒数更改为新值后的拷贝
. withSecond 返回LocalDateTime的秒数更改为新值后的拷贝
. withYear 返回LocalDateTime年份更改为新值后的拷贝
LocalDate、LocalTime、LocalDateTime
public static void main(String[] args) {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
LocalDate nowDate = LocalDate.now();
System.out.println("nowDate -> " + nowDate);
LocalTime nowTime = LocalTime.now();
System.out.println("nowTime -> " + nowTime);
String nowTimeFormat = nowTime.format(timeFormatter);
System.out.println("nowTimeFormmat -> " + nowTimeFormat);
LocalDateTime nowDateTime = LocalDateTime.now();
System.out.println("nowDateTime -> " + nowDateTime);
String nowDateTimeFormat = nowDateTime.format(dateTimeFormatter);
System.out.println("nowDateTimeFormat -> " + nowDateTimeFormat);
}
//结果输出:
nowDate -> 2023-09-03
nowTime -> 14:47:48.167
nowTimeFormmat -> 14:47:48
nowDateTime -> 2023-09-03T14:47:48.168
nowDateTimeFormat -> 2023-09-03 14:47:48
在我们开发过程中或多或少都会遇到需要兼容之前的旧代码,所以这篇文章主要记录这些工具类之间的相互转换。
LocalDateTime转Date
LocalDateTime
不能直接转换为Date
对象,需要通过桥梁ZonedDateTime
,也就是带了时区的日期类,这里的转换涉及到两个类:
- ZoneId:时区类。
- Instant:记录瞬时时间,也就是时间戳(类似于
System.currentTimeMillis()
方法)。
ZonedDateTime zonedDateTime = nowDateTime.atZone(ZoneId.systemDefault());
Instant instant = zonedDateTime.toInstant();
Date date = Date.from(instant);
System.out.println("localDateTime2Date -> " + date);
// 输出结果:
localDateTime2Date -> Sun Sep 03 14:49:22 CST 2023
LocalDate转Date
同样的,LocalDate
转Date
日期同样需要借助ZonedDateTime
类:
ZonedDateTime zonedDateTime1 = nowDate.atStartOfDay(ZoneId.systemDefault());
Instant instant1 = zonedDateTime1.toInstant();
Date date1 = Date.from(instant1);
System.out.println("localDate2Date -> " + date1);
//输出结果:
localDate2Date -> Sun Sep 03 00:00:00 CST 2023
Date转LocalDate、LocalDateTime、LocalTime
Date
转JAVA8
相关时间工具类全部以ZonedDateTime
为媒介即可,这里转换相当于LocalDateTime
转Date
对象的一个逆向过程。
Date thisDate = new Date();
// Date转LocalDateTime
Instant instant2 = thisDate.toInstant();
ZonedDateTime zonedDateTime2 = instant2.atZone(ZoneId.systemDefault());
LocalDateTime localDateTime = zonedDateTime2.toLocalDateTime();
System.out.println("localDateTime -> " + localDateTime);
//Date转LocalDate
Instant instant3 = thisDate.toInstant();
ZonedDateTime zonedDateTime3 = instant3.atZone(ZoneId.systemDefault());
LocalDate localDate = zonedDateTime3.toLocalDate();
System.out.println("localDate -> " + localDate);
//Date转LocalTime
Instant instant4 = thisDate.toInstant();
ZonedDateTime zonedDateTime4 = instant4.atZone(ZoneId.systemDefault());
LocalTime localTime = zonedDateTime4.toLocalTime();
System.out.println("localTime -> " + localTime);
// 输出结果:
localDateTime -> 2023-09-03T14:56:36.568
localDate -> 2023-09-03
localTime -> 14:56:36.568
工具类封装
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import java.sql.Time;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 时间工具类
*
* @author lbz
* @since 2023/8/21
*/
@Slf4j
public class DateUtils {
public static final Pattern P_WITHOUT_SECOND = Pattern.compile("^(([0,1])?[0-9]|2[0-3]):[0-5][0-9]$");
public static final Pattern P_WITH_SECOND = Pattern.compile("^(([0,1])?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$");
private static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd";
private static final String TIME_FORMAT_PATTERN = "HH:mm:ss";
private static final String DATE_TIME_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss";
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 格式化日期
*
* @param date
* @return yyyy-MM-dd
*/
public static String getFormatDate(Date date) {
SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT_PATTERN);
return format.format(date);
}
/**
* 格式化时间
*
* @param date
* @return HH:mm:ss
*/
public static String getFormatTime(Date date) {
SimpleDateFormat format = new SimpleDateFormat(TIME_FORMAT_PATTERN);
return format.format(date);
}
/**
* 格式化日期及时间
*
* @param date
* @return yyyy-MM-dd HH:mm:ss
*/
public static String getFormatDateTime(Date date) {
SimpleDateFormat format = new SimpleDateFormat(DATE_TIME_FORMAT_PATTERN);
return format.format(date);
}
/**
* 格式化日期
*
* @param localDate
* @return
*/
public static String getFormatDate(LocalDate localDate) {
return localDate.format(DATE_FORMATTER);
}
/**
* 格式化时间
*
* @param localDate
* @return
*/
public static String getFormatTime(LocalDateTime localDateTime) {
return localDateTime.format(TIME_FORMATTER);
}
/**
* 格式化日期及时间
*
* @param localDate
* @return
*/
public static String getFormatDateTime(LocalDateTime localDateTime) {
return localDateTime.format(DATE_TIME_FORMATTER);
}
/**
* LocalDate 转 Date
*
* @param localDate
* @return Date
*/
public static Date localDateToDate(LocalDate localDate) {
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
}
/**
* LocalDateTime 转 Date
*
* @param localDateTime
* @return Date
*/
public Date localDateTimeToDate(LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
/**
* Date 转 LocalDate
*
* @return
*/
public static LocalDate dateToLocalDate(Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
/**
* Date 转 LocalDateTime
*
* @param date
* @return
*/
public static LocalDateTime dateToLocalDateTime(Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
}
/**
* 计算两个日期时间差
*
* @param start yyyy-MM-dd HH:mm:ss
* @param end yyyy-MM-dd HH:mm:ss
* @return
*/
public static long diff(String start, String end) {
SimpleDateFormat format = new SimpleDateFormat(DATE_TIME_FORMAT_PATTERN);
try {
Date startTime = format.parse(start);
Date endTime = format.parse(end);
return endTime.getTime() - startTime.getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return -1;
}
/**
* 日期运算
*
* @param date 源
* @param part 操作部份
* @param value 改变值
* @return 计算后的日期
*/
public static Date add(Date date, int part, int value) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(part, value);
return calendar.getTime();
}
/**
* 取两个日期的差值
*
* @param from 开始时间
* @param to 结束时间
* @param part Calendar.SECOND--相关多少秒,Calendar.MINUTE--相关多少分,Calendar.HOUR_OF_DAY-时,other-天
* @return 差值
*/
public static long diff(Date from, Date to, int part) {
if (to == null || from == null) {
return 0;
}
long d = to.getTime() - from.getTime();
switch (part) {
case Calendar.SECOND:
return d / 1000;
case Calendar.MINUTE:
return d / 1000 / 60;
case Calendar.HOUR_OF_DAY:
return d / 1000 / 60 / 60;
default:
return d / 1000 / 60 / 60 / 24;
}
}
/**
* 日期格式化函数,格式:yyyy-MM-dd HH:mm:ss
*
* @param date
* @return
*/
public static String format(Date date) {
return format(date, "yyyy-MM-dd HH:mm:ss", null);
}
/**
* 日期格式化函数,缺省时区
*
* @param date
* @param format
* @return
*/
public static String format(Date date, String format) {
return format(date, format, null);
}
/**
* 日期格式化函数
*
* @param date
* @param format
* @param timeZone 时区如东八区GMT+8
* @return
*/
public static String format(Date date, String format, String timeZone) {
if (date == null)
return "";
SimpleDateFormat formatter = new SimpleDateFormat(format);
if (timeZone != null && !timeZone.trim().equals(""))
formatter.setTimeZone(TimeZone.getTimeZone(timeZone));
return formatter.format(date);
}
/**
* 字符转换为日期。
*
* @param source
* @return
*/
public static Date stringToDate(String source) {
return stringToDate(source, "yyyy-MM-dd HH:mm:ss", null);
}
/**
* 字符转换为日期。
*
* @param source
* @param pattern 日期格式串如yyyy-MM-dd HH:mm:ss
* @return
*/
public static Date stringToDate(String source, String pattern) {
return stringToDate(source, pattern, null);
}
/**
* 字符串转换为指定时区时间
*
* @param value
* @param pattern 如yyyy-MM-dd HH:mm:ss
* @param timeZone 如东八区GMT +8
* @return
*/
public static Date stringToDate(String value, String pattern, String timeZone) {
SimpleDateFormat format = new SimpleDateFormat(pattern);
Date date = null;
if (value == null) {
return date;
}
if (timeZone != null && !timeZone.trim().equals("")) {
format.setTimeZone(TimeZone.getTimeZone(timeZone));
}
try {
date = format.parse(value);
} catch (java.text.ParseException e) {
log.error(e.getMessage(), e);
}
return date;
}
/**
* 获取当前日期是一个星期的第几天
* <br>星期天是0
*/
public static int getDayOfWeek(Date time) {
Calendar cal = Calendar.getInstance();
cal.setTime(time);
int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
if (w < 0) {
w = 0;
}
return w;
}
/**
* 获取当前日期是星期几
*
* @return
*/
public static String getWeekOfDate(Date time) {
if (time == null) {
return "";
}
String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
return weekDays[getDayOfWeek(time)];
}
/**
* 两个日期相差的天数,不算时分秒
*
* @param from 开始日期
* @param to 结束日期
* @return
*/
public static int daysBetween(Date from, Date to) {
Date fromL = stringToDate(format(from, "yyyy-MM-dd"), "yyyy-MM-dd");
Date toL = stringToDate(format(to, "yyyy-MM-dd"), "yyyy-MM-dd");
if (fromL != null && toL != null) {
return (int) (toL.getTime() / (1000 * 24 * 60 * 60) - fromL.getTime() / (1000 * 24 * 60 * 60));
} else {
return 0;
}
}
/**
* 两个日期相差的天数,不算时分秒
*
* @param from 开始日期
* @param to 结束日期
* @return
*/
public static int daysBetween(String from, String to) {
Date fromL = stringToDate(from, "yyyy-MM-dd");
Date toL = stringToDate(to, "yyyy-MM-dd");
if (fromL != null && toL != null) {
return (int) (toL.getTime() / (1000 * 24 * 60 * 60) - fromL.getTime() / (1000 * 24 * 60 * 60));
} else {
return 0;
}
}
/**
* 得到两日期相差几个月
*/
public static int getMonthDiff(String startDate, String endDate) {
Date startDate1 = stringToDate(startDate, "yyyy-MM-dd");
Date endDate1 = stringToDate(endDate, "yyyy-MM-dd");
return getMonthDiff(startDate1, endDate1);
}
/**
* 得到两日期相差几个月
*/
public static int getMonthDiff(Date startDate, Date endDate) {
Calendar starCal = Calendar.getInstance();
starCal.setTime(startDate);
int sYear = starCal.get(Calendar.YEAR);
int sMonth = starCal.get(Calendar.MONTH);
Calendar endCal = Calendar.getInstance();
endCal.setTime(endDate);
int eYear = endCal.get(Calendar.YEAR);
int eMonth = endCal.get(Calendar.MONTH);
return ((eYear - sYear) * 12 + (eMonth - sMonth));
}
/**
* 获取当月最后一天
*
* @param date
* @return
*/
public static Date getLastDayOfMonth(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.DATE, 1);// 把日期设置为当月第一天
calendar.roll(Calendar.DATE, -1);// 日期回滚一天,也就是最后一天
return calendar.getTime();
}
/**
* 获取某年某月的最后一天
*
* @param year
* @param month
* @param format
* @return
*/
public static String getLastDayOfMonth(int year, int month, String format) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
return format(calendar.getTime(), format);
}
/**
* 获取某年某月的第一天
*
* @param year
* @param month
* @param format
* @return
*/
public static String getFirstDayOfMonth(int year, int month, String format) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MONTH, month - 1);
cal.set(Calendar.YEAR, year);
cal.set(Calendar.DAY_OF_MONTH, 1);
return format(cal.getTime(), format);
}
/**
* 获取时间所在的日期。
*
* @param date
* @return
*/
public static Date getDateByStart(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
/**
* 获取当月第一天
*
* @param date
* @return
*/
public static Date getFirstDay(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, 0);
calendar.set(Calendar.DAY_OF_MONTH, 1);
return calendar.getTime();
}
/**
* 获取下月第一天
*
* @param date
* @return
*/
public static Date getFirstDayOfLastMonth(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, 1);
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
return calendar.getTime();
}
/**
* 获取一天中最后的时刻(23点59分59秒)
*
* @param date 某一天
* @return
*/
public static Date getLastDateOfDate(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
return calendar.getTime();
}
/**
* 时间转换
*
* @param timeString 时间格式,必须为 HH:mm 或 HH:mm:ss
* @return
*/
public static Time parseTime(String timeString) {
Matcher mWithSecond = P_WITH_SECOND.matcher(timeString);
Matcher mWithoutSecond = P_WITHOUT_SECOND.matcher(timeString);
if (mWithSecond.matches()) {
return Time.valueOf(timeString);
} else if (mWithoutSecond.matches()) {
return Time.valueOf(timeString + ":00");
} else {
throw new RuntimeException("时间格式有误");
}
}
/**
* 格式换Time
*
* @param time
* @param needSecondFlag 是否需要秒
* @return
*/
public static String formatTime(Time time, Boolean needSecondFlag) {
if (time == null) {
return null;
}
if (needSecondFlag) {
return time.toString();
} else {
return time.toString().substring(0, time.toString().lastIndexOf(":"));
}
}
/**
* 截取时分秒
*
* @param date
* @return
*/
public static Time splitTime(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
return Time.valueOf(String.format("%s:%s:%s", hour, minute, second));
}
/**
* 是否3天前
*
* @param date 时间 yyyy-MM-dd
* @return boolean
*/
public static boolean isBefore3Day(String date) {
LocalDate dateTime = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
LocalDate minusDays = LocalDate.now().minusDays(2);
return dateTime.isBefore(minusDays);
}
public static Date getDate(Date time) {
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(time);
gc.set(Calendar.YEAR, DateUtil.thisYear());
gc.set(Calendar.MONTH, DateUtil.thisMonth());
gc.set(Calendar.DAY_OF_MONTH, DateUtil.thisDayOfMonth());
return gc.getTime();
}
}
总结
转换过程总结起来相对简单,Date类和JDK1.8推出的LocalDate、LocalDateTime、LocalTime类之间的转换可以通过ZonedDateTime做桥梁,至于JDK1.8新出的类就可以通过API自己转换。