一. Date 类
java.util.Date
是 Java 编程语言中最早提供的时间和日期处理类,用于表示一个特定的时间点,精确到毫秒。其时间表示方式是:自 1970 年 1 月 1 日 00:00:00 GMT 起的毫秒数。
1. Date 类的优点
1. 使用简单: 初学者可快速上手,例如 new Date() 获取当前时间。
2. 可与时间戳互转: 内部以毫秒时间戳表示,便于与系统时间、数据库等交互。
3. 向后兼容性强: 是 Java 平台核心类库的一部分,许多旧接口仍依赖 Date 类型。
4. 可与 JDBC 等框架配合使用: 可与 java.sql.Date、Timestamp 等数据库相关类直接转换。
2.Date 类的缺点
1. 线程不安全: 可变性导致线程不安全 Date 对象内部状态可变,在多线程环境中共享使用容易引发并发问题。
2. 设计不直观、语义混乱: 如 getYear() 返回的是“年份 - 1900”,getMonth() 从 0 开始;多数方法含糊不清。
3. 缺乏时区支持: Date 无法直接表示时区信息,只能与 TimeZone、Calendar 组合使用。
4. 功能有限: 不支持日期加减、时间截断、字段调整等常见操作,需借助其他类(如 Calendar)实现。
5. 大量方法已过时: 自 Java 1.1 开始,多数 Date 方法被标记为过时,不推荐继续使用。
6. 格式化依赖非线程安全的类: 格式化需配合 SimpleDateFormat,但该类本身也不是线程安全的,需小心使用。
3.Date 常用时间操作
3.1 获取当前格式化时间
private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 获取当前时间
* @return
*/
public static String currentDatetime() {
return dateFormat.format(new Date());
}
注: yyyy-MM-dd HH:mm:ss HH大写时为24小时制,hh小写时为12小时制
或者:
String date = String.format("%1$tF %1$tT", new Date());
3.2 格式化指定时间
private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 格式化日期
* @param date
* @return
*/
public static String formatDate(Date date) {
return dateFormat.format(date);
}
3.3 时间戳转时间字符串
private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 时间戳转为时间字符串
*
* @param timeStamp
* @return
*/
public static String formatTimeStamp(long timeStamp) {
// 时间戳转换成时间
return dateFormat.format(new Date(timeStamp));
}
3.4 将字符串解析成时间
private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 将字符串解析成时间对象
*
* @param date
* @return
*/
public static Date parseDatetime(String date) throws Exception{
return dateFormat.parse(date);
}
3.5 计算两个日期时间差
/**
* 计算两个日期时间差,单位 秒
* @param start
* @param end
* @return
*/
public static long interval(Date start, Date end) {
// 获取结束时间的毫秒表示(自1970年1月1日00:00:00 UTC以来的毫秒数)
long endTime = end.getTime();
// 获取起始时间的毫秒表示
long startTime = start.getTime();
// 计算两个时间的差值(单位为毫秒),然后转换为秒(1秒 = 1000毫秒)
return (endTime - startTime) / 1000;
}
二. Calendar 类
java.util.Calendar
是 Java 在 1.1 版本中引入的时间处理类,旨在取代 java.util.Date
中已过时的方法。Calendar
提供了更为全面的日期字段访问与运算功能,如获取年、月、日、小时等字段,以及对时间进行加减操作。
1. Calendar 类的优点
1. 提供字段访问: 支持通过常量(如 Calendar.YEAR、Calendar.MONTH)读取和设置时间字段。
2. 支持日期计算: 内置 add() 和 roll() 方法实现日期加减操作。
3. 支持时区操作: 可设置和处理 TimeZone,用于国际化场景。
4. 与 Date 无缝转换: 提供 getTime() / setTime() 方法,可与 Date 相互转换。
2. Calendar 类的缺点
1.API 使用复杂: 方法设计繁琐、不直观,字段操作依赖常量,降低代码可读性。
2.月份从 0 开始: 与人类习惯不符,易导致逻辑错误。
3.线程不安全: 可变对象,多线程环境中必须手动同步。
4.不能链式调用: 设置多个字段需多次调用 set(),代码不简洁。
5.设计臃肿: 实现过于重量级,缺乏现代时间处理类的灵活性与表现力。
3.Calendar 常用时间操作
3.1 获取日期
3.1.1 获取当天开始时间
private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 获取当天的开始时间
* @return
*/
public static String getDayBegin() {
// 创建一个基于当前系统时间的 GregorianCalendar 实例(默认时区、地区)
Calendar calendar = new GregorianCalendar();
// 将小时字段设置为 0(24 小时制中的午夜)
calendar.set(Calendar.HOUR_OF_DAY, 0);
// 将分钟字段设置为 0
calendar.set(Calendar.MINUTE, 0);
// 将秒字段设置为 0
calendar.set(Calendar.SECOND, 0);
// 将毫秒字段设置为 0,确保时间精确到 00:00:00.000
calendar.set(Calendar.MILLISECOND, 0);
// 将 Calendar 对象转换为 Date 类型,并返回
return dateFormat.format(calendar.getTime());
}
3.1.2 获取昨天此刻的时间
private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 获取昨天时间
* @return
*/
public static String getBeginDayOfYesterday() {
// 获取当前时间的 Calendar 实例
Calendar calendar = Calendar.getInstance();
// 日期减 1 天
calendar.add(Calendar.DAY_OF_MONTH, -1);
// 获取 Date 对象(昨天的时间)
Date yesterday = calendar.getTime();
return dateFormat.format(yesterday.getTime());
}
3.1.3 获取上个月的今天
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取上个月当前日期
* @return
*/
public static String getLastMonthToday() {
// 获取当前时间的 Calendar 实例
Calendar cl = Calendar.getInstance();
// 将月份减一,切换到上个月同一天
cl.add(Calendar.MONTH, -1);
// 获取上个月同一天的 Date 对象
Date dateFrom = cl.getTime();
// 将 Date 格式化成字符串返回
return format.format(dateFrom);
}
3.1.4 获取此刻前多少分钟的时间
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 获取前多少分钟的时间
* @param beforeMinutes
* @return
*/
public static String checkPointMinutes(int beforeMinutes){
// 获取当前时间
Calendar calendar = Calendar.getInstance();
// 向前推指定分钟数
calendar.add(Calendar.MINUTE, -beforeMinutes);
// 获取 Date 对象
Date daysAgo = calendar.getTime();
return dateFormat.format(daysAgo);
}
3.1.5 获取指定日期前多少天的日期
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取前多少天的日期
* @param beforeDay
* @return
*/
public static String checkPointDay(int beforeDay){
// 获取当前时间
Calendar calendar = Calendar.getInstance();
// 向前推指定天
calendar.add(Calendar.DAY_OF_MONTH, 1 - beforeDay);
// 获取 Date 对象
Date daysAgo = calendar.getTime();
return format.format(daysAgo);
}
3.1.6 获取指定日期的下一天
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取指定日期的下一天
* @param date
* @return
* @throws Exception
*/
public static String getNextDate(String date) throws Exception{
// 用于保存解析后的 Date 对象,初始为 null
Date dateDay = format.parse(date);
// 创建 Calendar 实例,准备操作日期
Calendar rightNow = Calendar.getInstance();
// 把 Calendar 时间设置为解析后的日期
rightNow.setTime(dateDay);
// 日期加 1 天
rightNow.add(Calendar.DATE, 1);
// 获取加一天后的 Date 对象
Date nestDay = rightNow.getTime();
// 格式化加一天后的日期为字符串
String nestDayStr = format.format(nestDay);
// 返回结果字符串
return nestDayStr;
}
3.1.7 获取指定日期后多少天的日期
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取指定日期加上天数后的日期
* @param num
* @param date
* @return
*/
public static String plusDay(int num, String date) throws Exception{
// 初始化 Date 对象为 null,用于后续接收解析结果
Date thisDay = format.parse(date);
// 创建 Calendar 实例,用于进行日期的加减运算
Calendar rightNow = Calendar.getInstance();
// 将 Calendar 设置为目标日期
rightNow.setTime(thisDay);
// 在目标日期的基础上加(或减)指定的天数
// 正数为向后推,负数为向前推
rightNow.add(Calendar.DAY_OF_YEAR, num);
// 获取加减后的日期对象
Date plusDay = rightNow.getTime();
// 将新日期格式化为字符串
String plusDayStr = format.format(plusDay);
// 返回结果日期字符串
return plusDayStr;
}
public static void main(String[] args) {
try {
System.out.println(plusDay(2, "2025-07-17"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
3.1.8 获取指定日期后几个月的日期
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 指定日期加上月数后的日期
* @param num
* @param date
* @return
*/
public static String plusMonth(int num, String date) throws Exception{
// 初始化 Date 类型变量,用于存放解析后的日期
Date dt = format.parse(date);
// 获取一个 Calendar 实例,用于进行日期计算
Calendar rightNow = Calendar.getInstance();
// 将 Calendar 的时间设置为解析后的日期
rightNow.setTime(dt);
// 在当前日期基础上增加(或减少)指定的月份数
// num 为正数表示向后推移,为负数表示向前推移
rightNow.add(Calendar.MONTH, num); // 例如 num=3 表示加3个月
// 获取加减操作后的日期对象
Date plusDay = rightNow.getTime();
// 将计算后的日期格式化为字符串
String plusDayStr = format.format(plusDay);
// 返回最终的日期字符串
return plusDayStr;
}
public static void main(String[] args) {
try {
System.out.println(plusMonth(2, "2025-07-17"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
3.1.9 获取某日去年的时间
如果是闰年二月29号,前一年自动变成28号
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取传入日期在去年的同一天对应的日期
* @param date
* @return
*/
public static String getDateDayLastYear(String date) throws Exception{
// 初始化 Date 类型变量用于接收解析结果
Date rightDate = format.parse(date);
// 获取 Calendar 实例,用于日期计算
Calendar cal = Calendar.getInstance();
// 将 Calendar 的时间设置为解析后的日期
cal.setTime(rightDate);
// 将年份减去 1,即得到去年的同一天
cal.add(Calendar.YEAR, -1);
// 将计算结果格式化为字符串
String lastDay = format.format(cal.getTime());
// 返回最终字符串结果
return lastDay;
}
public static void main(String[] args) {
try {
System.out.println(getDateDayLastYear("2024-02-28"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
3.2 获取日期范围
3.2.1.获取指定日期的某周的第一天和最后一天
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取某个时间的当周的第一天和最后一天
* @param date
* @return
*/
public static String[] getWeekDayScope(String date) {
// 用于存储解析后的 Date 对象
Date time = null;
try {
// 将传入的日期字符串解析为 Date 类型
time = format.parse(date);
} catch (ParseException e) {
// 如果解析失败,打印异常信息
e.printStackTrace();
}
// 创建一个 Calendar 实例,并设置为解析后的时间
Calendar calendar = Calendar.getInstance();
calendar.setTime(time);
// 获取当前是星期几(1 表示周日,2 表示周一,依此类推)
int dayWeek = calendar.get(Calendar.DAY_OF_WEEK);
// 如果是周日(1),手动减一天,归到上一周,便于统一处理
if (1 == dayWeek) {
calendar.add(Calendar.DAY_OF_MONTH, -1);
}
// 设置一周的第一天为“星期一”(默认是周日)
calendar.setFirstDayOfWeek(Calendar.MONDAY);
// 获取当前是本周的第几天
int day = calendar.get(Calendar.DAY_OF_WEEK);
// 调整日期:从当前日期向前回退,回到“本周一”
calendar.add(Calendar.DATE, calendar.getFirstDayOfWeek() - day);
// 格式化为字符串,表示本周的周一
String timeBegin = format.format(calendar.getTime());
// 在当前基础上加 6 天,即得到本周周日
calendar.add(Calendar.DATE, 6);
// 格式化为字符串,表示本周的周日
String timeEnd = format.format(calendar.getTime());
return new String[]{timeBegin, timeEnd};
}
public static void main(String[] args) {
System.out.println(Arrays.toString(getWeekDayScope("2025-07-21")));
}
3.2.2 获取当月的第一天和最后一天
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取当前月份的第一天和最后一天的字符串表示
* @return
*/
public static String[] getMonthScope() {
// 获取当前时间的 Calendar 实例
Calendar calendar = Calendar.getInstance();
// 当前月份加 0,等价于不变,保留结构用于未来可能支持月份偏移
calendar.add(Calendar.MONTH, 0);
// 将日期设置为当前月份的第一天
calendar.set(Calendar.DAY_OF_MONTH, 1);
// 格式化第一天为字符串
String first = format.format(calendar.getTime());
// 再获取一个新的 Calendar 实例,依然是当前时间
Calendar ca = Calendar.getInstance();
// 将日期设置为当前月份的最后一天
ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
// 格式化最后一天为字符串
String last = format.format(ca.getTime());
return new String[]{first, last};
}
public static void main(String[] args) {
System.out.println(Arrays.toString(getMonthScope()));
}
3.2.3 获取上个月的第一天和最后一天
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取上个月的第一天和最后一天
* @return
*/
public static String[] getLastMonthScope() {
// 获取当前时间对应的 Calendar 实例
Calendar calendar = Calendar.getInstance();
// 将当前月份减去 1,移动到上个月
calendar.add(Calendar.MONTH, -1);
// 设置为上个月的第一天
calendar.set(Calendar.DAY_OF_MONTH, 1);
// 此时 calendar 已指向上个月的第一天
// 创建另一个 Calendar 实例,用于表示上个月的最后一天
Calendar calendar1 = Calendar.getInstance();
// 获取当前月份(0~11),并减去 1,定位到上个月
int month = calendar1.get(Calendar.MONTH);
calendar1.set(Calendar.MONTH, month - 1);
// 设置为上个月的最后一天
calendar1.set(Calendar.DAY_OF_MONTH,
calendar1.getActualMaximum(Calendar.DAY_OF_MONTH));
// 格式化上个月的第一天为字符串
String firstDay = format.format(calendar.getTime());
// 格式化上个月的最后一天为字符串
String lastDay = format.format(calendar1.getTime());
// 返回格式为 "yyyy-MM-dd and yyyy-MM-dd" 的结果
return new String[]{firstDay, lastDay};
}
public static void main(String[] args) {
System.out.println(Arrays.toString(getLastMonthScope()));
}
3.2.4 获取去年当月的第一天和最后一天
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取“去年本月”的第一天与最后一天
* @return
*/
public static String[] getMonthLastYearScope() {
// 创建 Calendar 实例,代表“去年本月第一天”
Calendar lastYearStart = Calendar.getInstance();
// 年份减一,即跳转到去年
lastYearStart.add(Calendar.YEAR, -1);
// 月份保持不变(add 0 实际无效,但结构清晰)
lastYearStart.add(Calendar.MONTH, 0);
// 设置为当月的第一天(1号)
lastYearStart.set(Calendar.DAY_OF_MONTH, 1);
// 将“去年本月的第一天”格式化为字符串
String firstDayLastYear = format.format(lastYearStart.getTime());
// 创建另一个 Calendar 实例,代表“去年本月最后一天”
Calendar lastYearEnd = Calendar.getInstance();
// 同样年份减一,回到去年
lastYearEnd.add(Calendar.YEAR, -1);
// 月份保持不变
lastYearEnd.add(Calendar.MONTH, 0);
// 设置为该月的最后一天(如:30 或 31)
lastYearEnd.set(Calendar.DAY_OF_MONTH,
lastYearEnd.getActualMaximum(Calendar.DAY_OF_MONTH));
// 将“去年本月的最后一天”格式化为字符串
String endDayLastYear = format.format(lastYearEnd.getTime());
// 返回格式为 "yyyyMMdd and yyyyMMdd" 的字符串结果
return new String[]{firstDayLastYear, endDayLastYear};
}
3.2.5 获取指定年月的第一天和最后一天
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取指定月份所在的整月起止日期
* @param date
* @return
*/
public static String[] getMonthScope(String date) {
// 创建用于解析输入月份的格式器(只含年和月)
SimpleDateFormat monthFormat = new SimpleDateFormat("yyyy-MM");
// 用于接收转换后的 Date 对象
Date monthDate = null;
// 将传入的字符串(如 "2024-07")转换为 Date 对象
try {
monthDate = monthFormat.parse(date);
} catch (ParseException e) {
// 如果输入格式不符合预期,打印异常信息
e.printStackTrace();
}
// 创建一个 Calendar 实例并设为指定月份
Calendar lastYearStart = Calendar.getInstance();
lastYearStart.setTime(monthDate);
// 设置为该月的第一天(如 1 号)
lastYearStart.set(Calendar.DAY_OF_MONTH, 1);
// 格式化该月的第一天为字符串(如 "2024-07-01")
String firstDayLastYear = format.format(lastYearStart.getTime());
// 创建另一个 Calendar 实例,并设为同一月份
Calendar lastYearEnd = Calendar.getInstance();
lastYearEnd.setTime(monthDate);
// 设置为该月的最后一天(如 "2024-07-31")
lastYearEnd.set(Calendar.DAY_OF_MONTH,
lastYearEnd.getActualMaximum(Calendar.DAY_OF_MONTH));
// 格式化该月的最后一天为字符串
String endDayLastYear = format.format(lastYearEnd.getTime());
// 返回格式为 "yyyy-MM-dd and yyyy-MM-dd" 的字符串结果
return new String[]{firstDayLastYear, endDayLastYear};
}
public static void main(String[] args) {
System.out.println(Arrays.toString(getMonthScope("2025-07")));
}
3.2.6 获取两个日期范围的所有日期
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 获取两个日期之间的所有日期(含起止)
* @param startDateStr 起始日期字符串
* @param endDateStr 结束日期字符串
* @return 日期字符串列表
*/
public static List<String> getDateRange(String startDateStr, String endDateStr) throws Exception{
List<String> dateList = new ArrayList<>();
// 将字符串解析为 Date 对象
Date startDate = format.parse(startDateStr);
Date endDate = format.parse(endDateStr);
// 使用 Calendar 设置开始和结束日期
Calendar startCal = Calendar.getInstance();
startCal.setTime(startDate);
Calendar endCal = Calendar.getInstance();
endCal.setTime(endDate);
// 遍历日期范围,逐天添加
while (!startCal.after(endCal)) {
String result = format.format(startCal.getTime());
dateList.add(result);
// 日期加 1 天
startCal.add(Calendar.DAY_OF_MONTH, 1);
}
return dateList;
}
public static void main(String[] args) throws Exception{
List<String> result = getDateRange("2027-06-15", "2027-07-18");
System.out.println(result); // 输出:[2027-07-15, 2027-07-16, 2027-07-17, 2027-07-18]
}
3.2.7 获取两个日期范围的所有月份
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM");
/**
* 获取两个日期之间的所有月份(包含起止月份
* @param startDateStr 起始日期字符串
* @param endDateStr 结束日期字符串
* @return 所有月份组成的字符串列表
*/
public static List<String> getMonthRange(String startDateStr, String endDateStr) throws Exception{
List<String> monthList = new ArrayList<>();
// 将输入字符串解析为 Date 对象
Date startDate = format.parse(startDateStr);
Date endDate = format.parse(endDateStr);
// 设置起始日期
Calendar startCal = Calendar.getInstance();
startCal.setTime(startDate);
// 确保是月份的第一天
startCal.set(Calendar.DAY_OF_MONTH, 1);
// 设置结束日期
Calendar endCal = Calendar.getInstance();
endCal.setTime(endDate);
// 统一成月份起始日
endCal.set(Calendar.DAY_OF_MONTH, 1);
// 遍历月份范围
while (!startCal.after(endCal)) {
String result = format.format(startCal.getTime());
monthList.add(result);
// 月份加 1
startCal.add(Calendar.MONTH, 1);
}
return monthList;
}
public static void main(String[] args) throws Exception{
List<String> months = getMonthRange("2026-11", "2027-03");
System.out.println(months);
}
3.2.8 打印 00-23 的小时
/**
* 打印 00-23 的小时
* @return
*/
public static List<String> hours(){
return IntStream.range(0, 24)
.mapToObj(i -> String.format("%02d", i))
.collect(Collectors.toList());
}
3.3 时间计算
3.3.1 计算两个日期之间所包含的天数
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
/**
* 计算两个日期字符串之间相差的天数(包含当天,结果至少为 1)
* @param startDate
* @param endDate
* @return
*/
public static int differentDays(String startDate, String endDate) {
Date date2 = null;
Date date1 = null;
try {
// 将字符串 startDate 解析为 Date 类型
date1 = format.parse(startDate);
// 将字符串 endDate 解析为 Date 类型
date2 = format.parse(endDate);
} catch (ParseException e) {
// 解析失败时输出异常堆栈信息
e.printStackTrace();
}
// 创建第一个 Calendar 实例,并设置为 date1 时间
Calendar cal1 = Calendar.getInstance();
cal1.setTime(date1);
// 创建第二个 Calendar 实例,并设置为 date2 时间
Calendar cal2 = Calendar.getInstance();
cal2.setTime(date2);
// 获取两个日期在各自年份中的“第几天”(即从年初算起的天数)
int day1 = cal1.get(Calendar.DAY_OF_YEAR);
int day2 = cal2.get(Calendar.DAY_OF_YEAR);
// 获取两个日期的年份
int year1 = cal1.get(Calendar.YEAR);
int year2 = cal2.get(Calendar.YEAR);
// 如果不在同一年,需要考虑跨年累加天数
if (year1 != year2) {
int timeDistance = 0;
// 遍历中间年份,累加天数
for (int i = year1; i < year2; i++) {
// 判断是否为闰年
if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) {
timeDistance += 366;
} else {
timeDistance += 365;
}
}
// 返回跨年的总天数 + 年内天数差 + 1(包含当天)
return timeDistance + (day2 - day1) + 1;
} else {
// 同一年,直接用 day2 - day1 即可(再 +1 表示包含起始日)
return day2 - day1 + 1;
}
}
public static void main(String[] args) {
System.out.println(differentDays("2019-2-26","2019-2-28"));
}
3.3.2 计算时间是否过期
private static SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 校验开始时间是否早于或等于结束时间(是否过期)
* @param startTime 开始时间 可以为null
* @param endTime 结束时间 不能为null
* @return
* @throws Exception
*/
public static boolean checkExpired(String startTime, String endTime) throws Exception{
// 当开始或者结束时间为空时,不进行比较
if(StringUtils.isEmpty(endTime)){
return true;
}
// 转换为日期格式
Date start = format.parse(startTime);
Date end = format.parse(endTime);
// 进行过期判断
return checkExpired(start,end);
}
/**
* 校验开始时间是否早于或等于结束时间(是否过期)
* @param startTime 开始时间(允许为 null,若为 null 则默认为当前系统时间)
* @param endTime 结束时间(必须不为 null)
* @return 如果开始时间 ≤ 结束时间,则返回 true;否则返回 false
*/
public static boolean checkExpired(Date startTime, Date endTime) {
boolean flag = true; // 默认标志为 true(即认为时间顺序合法)
// 如果开始时间为空,则使用当前时间作为默认开始时间
if (null != startTime) {
// 使用系统当前时间
startTime = new Date();
}
// 获取开始时间的时间戳(毫秒表示)
long startTimestamp = startTime.getTime();
// 获取结束时间的时间戳(毫秒表示)
long endTimestamp = endTime.getTime();
// 判断开始时间是否晚于结束时间
if (startTimestamp > endTimestamp) {
// 如果开始时间在结束时间之后,则时间顺序非法
flag = false;
}
// 返回判断结果
return flag;
}
public static void main(String[] args) throws Exception{
boolean ifExpired = checkExpired("2025-07-18 12:13:14", "2025-07-17 12:13:14");
System.out.println(ifExpired);
}
3.3.3 获取当前时间到次日凌晨的剩余毫秒数
/**
* 获取当前时间到次日凌晨(00:00:00)的剩余毫秒数
* @return 距离第二天 00:00:00 的时间间隔(以毫秒为单位)
*/
public static long getExecuteDuration() {
// 获取当前系统时间
Calendar now = Calendar.getInstance();
// 克隆当前时间,用于设置第二天的凌晨时间点
Calendar midnight = (Calendar) now.clone();
// 将克隆的时间增加一天,表示“明天”的时间
midnight.add(Calendar.DAY_OF_YEAR, 1);
// 设置“明天”的时间为凌晨 00:00:00.000
midnight.set(Calendar.HOUR_OF_DAY, 0); // 设置小时为 0
midnight.set(Calendar.MINUTE, 0); // 设置分钟为 0
midnight.set(Calendar.SECOND, 0); // 设置秒为 0
midnight.set(Calendar.MILLISECOND, 0); // 设置毫秒为 0
// 计算从“当前时间”到“明天凌晨”的时间差(单位:毫秒)
long delay = midnight.getTimeInMillis() - now.getTimeInMillis();
// 返回时间差
return delay;
}
三.DateTime 类
在早期的 Java 开发中,时间和日期的处理主要依赖于 java.util.Date 和 java.util.Calendar 类。然而他们存在设计上的缺陷、操作繁琐以及线程不安全的缺点。
org.joda.time.DateTime 是 Joda-Time 中最核心的类之一,用于表示一个不变的时间点(包括日期和时间)。它具有丰富的 API 接口,用于时间的创建、解析、比较、偏移、格式化等操作。
1. DateTime 类的优点
1.设计清晰: 遵循不可变对象设计,更符合函数式风格。
2.API 丰富: 支持多种日期操作,如加减年月日、判断闰年、周数等。
3.线程安全: 所有核心类(如 DateTime、LocalDate 等)默认是线程安全的。
4.格式化/解析能力强: 提供 DateTimeFormatter 类,支持灵活的时间格式转换。
5.国际化支持: 支持时区、地区设置等国际化需求。
2. DateTime 类的缺点
1.非 Java 标准库: 需要额外引入第三方依赖,增加构建与兼容复杂度。
2.与 java.util.Date 不兼容: 使用时常需要手动进行类型转换。
3.被 Java 8 替代 : Java 8 引入的 java.time.*(JSR-310) 包吸收了 Joda-Time 的精华设计。
3. DateTime 常用时间操作
引入org.joda.time.DateTime
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.8</version>
</dependency>
3.1 初始化时间
DateTime time = new DateTime(2018,4,23,23, 7,18,888);
// 2018年4月23日23点7分18秒888毫秒
3.2 按格式输出时间(将DateTime格式转换为字符串)
String time = dateTime.toString("yyyy-MM-dd hh:mm:ss.SSSa");
小写hh是12小时制,大写HH是24小时制
3.3 将字符串转换为DateTime格式
DateTimeFormatter format = DateTimeFormat .forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dateTime = DateTime.parse("2018-4-23 23:12:16", format);
3.4 取得当前时间
DateTime time= new DateTime();
3.5 计算两个日期间隔的天数
LocalDate start=new LocalDate(2018,4,23);
LocalDate end=new LocalDate(2019, 06, 16);
int days = Days.daysBetween(start, end).getDays();
3.6 增加日期
DateTime dateTime = DateTime.parse("2018-04-23");
dateTime = dateTime1.plusDays(1);
dateTime = dateTime1.plusHours(2);
dateTime = dateTime1.plusMinutes(3);
dateTime = dateTime1.plusMonths(4);
dateTime = dateTime1.plusSeconds(5);
dateTime = dateTime1.plusWeeks(6);
dateTime = dateTime1.plusYears(7);
3.7 减少日期
DateTime dateTime = DateTime.parse("2018-04-23");
dateTime = dateTime1.minusMillis(1);
dateTime = dateTime1.minusHours(1);
dateTime = dateTime1.minusSeconds(1);;
3.8 判断是否闰月
DateTime time = new DateTime();
org.joda.time.DateTime.Property month = time.monthOfYear();
System.out.println("是否闰月:" + month.isLeap());
3.9 DateTime与Date转换
DateTime time = new DateTime(new Date());
Date date = time.toDate();
DateTime time2 = new DateTime(System.currentTimeMillis());
time2.getMillis();
3.10.DateTime与Calendar转换
Calendar calendar = Calendar.getInstance();
dateTime = new DateTime(calendar);
四. 设置时区
1. Calendar 设置时区
较为复杂, 如果要使用 SimpleDateFormat 格式化时间, SimpleDateFormat 要同时设置时区
/**
* 时区测试
*/
public static void testTimeZone() {
// UTC 时间格式化器
SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
utcFormat.setTimeZone(TimeZone.getTimeZone("UTC")); // 设置为 UTC
Calendar utcDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
System.out.println("UTC时间: " + utcFormat.format(utcDate.getTime()));
// 北京时间格式化器
SimpleDateFormat beijingFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
beijingFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置为北京时间
Calendar beijingDate = Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai"));
System.out.println("北京时间: " + beijingFormat.format(beijingDate.getTime()));
}
2. java.time 设置时区
public static void main(String[] args) {
// 获取当前北京时间(默认 JVM 时区如果是 UTC+8)
LocalDateTime nowInBeijing = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("北京时间: " + nowInBeijing);
// 设置当前时区为 UTC
LocalDateTime nowInUtc = LocalDateTime.now(ZoneId.of("UTC"));
System.out.println("UTC时间: " + nowInUtc);
}
3. springboot 设置全局 UTC 时区
可以在springboot 启动类中加入该方法:
@PostConstruct
public void init() {
// 全局设置为 UTC 时区
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
logger.info("当前默认时区已设置为: {}", TimeZone.getDefault().getID());
}
4. @Scheduled定时注解指定时区
@Scheduled(cron = "0 0 0 * * ?", zone = "Asia/Shanghai")
该方法触发时按照指定的时区, 方法内部的时间操作, 也是按照指定时区