您的位置:

Java时间计算:加减日期实现方法详解

在日常开发中,无论是数据统计、活动倒计时、定时任务等都需要进行时间操作,其中一项常见的操作是加减日期,例如:将今天的日期往前或往后推几天、几个月、几年,以及具体到某一天、某一时刻等。

一、Java 时间类

Java提供了一个处理日期和时间的类——Date,尽管它在Java SE 8中已经可以弃用使用,但是对于基础知识的学习,介绍起来还是有必要的。

public class Date {
    /**
     * 分配给 Date 对象的唯一标识符。
     */
    private transient long fastTime;
    //...其他成员变量和方法
}

由上述代码分析可知,Date 类利用一个“long型”的变量存储日期时间信息,毫秒级别的时间戳可以完美地用long类型来表示,但是该类并没有提供很好的时间计算方法。

二、Java Calendar 类

Java中的Calendar类是一个抽象类,提供了丰富的日期时间计算和转换接口,由此也可以得出另一条结论:Java 预算日期操作最好使用 Calendar 类。

1. Calendar 类方法

Calendar 类提供的方法如下:

  • 根据日期设置年、月、日等时间信息。
  • 提供了获取日期时间各部分信息的接口,如获取年、月、日、小时、分钟、秒等。
  • 提供了时间增减计算接口,如 add、roll 等方法。
  • Calendar 类提供了格式化日期的接口,例如,getDisplayName等。
  • Calendar 类提供了时区的操作接口。

其中 add 和 roll 方法都是时间增量计算方法,两者的区别在于对于跨年的加减日期处理方式不同。

2. Calendar 类代码示例

/**
 * 获取某一天的开始时间
 *
 * @param calendar 时间对象
 * @return 当天起始时间
 */
public static Date getStartTimeOfDay(Calendar calendar) {
    Date date = calendar.getTime();
    try {
        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();
    } catch (Exception e) {
        log.error("", e);
    }
    return null;
}

上述代码是获取某一天的开始时间方法的实现,利用 Calendar 类的 getTime() 方法获取当前时间,在设置成员变量中各部分时间为 0,即可得到当天开始时间。

三、时间计算区间工具类

为方便日后使用,我们通常会封装一个工具类,来计算时间的加减和计算区间等。

1. 时间计算工具类代码示例

/**
 * 时间计算工具类
 * 

* 包含获取某一时间之前或之后的时间,以及计算时间区间等方法。 */ public class DateUtil { /** * 指定时间加减年份 * * @param date 日期 * @param yearAmount 年数,正数代表往后推,负数代表往前推 * @return 计算完后的日期 */ public static Date addYear(Date date, int yearAmount) { Calendar c = Calendar.getInstance(); c.setTime(date); c.add(Calendar.YEAR, yearAmount); return c.getTime(); } /** * 指定时间加减月份 * * @param date 日期 * @param monthAmount 月数,正数代表往后推,负数代表往前推 * @return 计算完后的日期 */ public static Date addMonth(Date date, int monthAmount) { Calendar c = Calendar.getInstance(); c.setTime(date); c.add(Calendar.MONTH, monthAmount); return c.getTime(); } /** * 指定时间加减天数 * * @param date 日期 * @param dayAmount 日数,正数代表往后推,负数代表往前推 * @return 计算完后的日期 */ public static Date addDay(Date date, int dayAmount) { Calendar c = Calendar.getInstance(); c.setTime(date); c.add(Calendar.DAY_OF_MONTH, dayAmount); return c.getTime(); } /** * 指定时间加减小时数 * * @param date 日期 * @param hourOfdayAmount 小时数,正数代表往后推,负数代表往前推 * @return 计算完后的日期 */ public static Date addHourOfDay(Date date, int hourOfdayAmount) { Calendar c = Calendar.getInstance(); c.setTime(date); c.add(Calendar.HOUR_OF_DAY, hourOfdayAmount); return c.getTime(); } /** * 指定时间加减分钟数 * * @param date 日期 * @param minuteAmount 分钟数,正数代表往后推,负数代表往前推 * @return 计算完后的日期 */ public static Date addMinute(Date date, int minuteAmount) { Calendar c = Calendar.getInstance(); c.setTime(date); c.add(Calendar.MINUTE, minuteAmount); return c.getTime(); }

上述代码通过 Calendar 类提供的 add 方法,对时间的年、月、日、时、分等信息进行加减,从而实现时间操作的目的。

2. 时间区间计算代码示例

/**
 * 时间区间计算工具类
 * 

* 如果 startTime 为null,endTime 不为null,则返回endTime到当前的区间列表。 * 如果 endTime 为null,startTime 不为null,则返回startTime到当前的区间列表。 * 如果 startTime 和 endTime 都为null,则返回全年或全月或全日的区间列表。 * * @param startTime 开始日期 * @param endTime 结束日期 * @return 时间区间列表 */ public static List getTimeInterval(String startTime, String endTime) throws Exception { if ((startTime == null || startTime.trim().equals("")) && (endTime == null || endTime.trim().equals(""))) { throw new Exception("开始时间和结束时间不能同时为空!"); } List result = new ArrayList (); SimpleDateFormat sdf = null; DateTimeFormatter formatter = null; String datePattern = ""; LocalDateTime nowLdt = LocalDateTime.now(); LocalDateTime startLdt = null; LocalDateTime endLdt = null; Integer year = null; Integer month = null; if (!StringUtils.isEmpty(startTime)) { datePattern = getPattern(startTime); if (datePattern == null || datePattern.equals("")) { throw new Exception("开始时间格式错误!(yyyy-MM-dd或yyyy-MM-dd HH:mm:ss)"); } //解决java.time.DateTimeParseException异常问题 formatter = DateTimeFormatter.ofPattern(datePattern); startLdt = LocalDateTime.parse(startTime, formatter); year = startLdt.getYear(); month = startLdt.getMonth().getValue(); } if (!StringUtils.isEmpty(endTime)) { datePattern = getPattern(endTime); if (datePattern == null || datePattern.equals("")) { throw new Exception("结束时间格式错误!(yyyy-MM-dd或yyyy-MM-dd HH:mm:ss)"); } //解决java.time.DateTimeParseException异常问题 formatter = DateTimeFormatter.ofPattern(datePattern); endLdt = LocalDateTime.parse(endTime, formatter); year = endLdt.getYear(); month = endLdt.getMonth().getValue(); } int startYear = year == null ? nowLdt.getYear() : year; int endYear = year == null ? nowLdt.getYear() : year; int startMon = month == null ? nowLdt.getMonthValue() : month; int endMon = month == null ? nowLdt.getMonthValue() : month; if (startLdt != null) { startYear = startLdt.getYear(); startMon = startLdt.getMonthValue(); } if (endLdt != null) { endYear = endLdt.getYear(); endMon = endLdt.getMonthValue(); } if (startLdt != null && endLdt != null && startLdt.isBefore(nowLdt) && endLdt.isAfter(nowLdt)) { startYear = nowLdt.getYear(); startMon = nowLdt.getMonthValue(); } for (int yearIndex = startYear; yearIndex <= endYear; yearIndex++) { if ((startYear == endYear && startMon > endMon) || (startYear < endYear && yearIndex == startYear && startMon > nowLdt.getMonthValue())) { break; } int startMonIndex = 1; int endMonIndex = 12; if (yearIndex == startYear && yearIndex == endYear) { startMonIndex = startMon; endMonIndex = endMon; } else if (yearIndex == startYear) { startMonIndex = startMon; } else if (yearIndex == endYear) { endMonIndex = endMon; } for (int monIndex = startMonIndex; monIndex <= endMonIndex; monIndex++) { LocalDate localDate = LocalDate.of(yearIndex, monIndex, 1); int maxDaysOfMonth = localDate.lengthOfMonth(); String startTimeStr = ""; String endTimeStr = ""; if (startLdt != null && startLdt.getYear() == yearIndex && startLdt.getMonthValue() == monIndex) { startTimeStr = startTime; } else { startTimeStr = yearIndex + "-" + formatNumber(monIndex) + "-01 00:00:00"; } if (endLdt != null && endLdt.getYear() == yearIndex && endLdt.getMonthValue() == monIndex) { endTimeStr = endTime; } else { endTimeStr = yearIndex + "-" + formatNumber(monIndex) + "-" + maxDaysOfMonth + " 23:59:59"; } result.add(startTimeStr + "|" + endTimeStr); } } return result; } private static String getPattern(String date) { if (date.contains("-")) { return date.contains(":") ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd"; } else if (date.contains("/")) { return date.contains(":") ? "yyyy/MM/dd HH:mm:ss" : "yyyy/MM/dd"; } return ""; } private static String formatNumber(int value) { return value < 10 ? "0" + value : String.valueOf(value); }

上述代码中演示了时间区间计算的示例。根据输入的开始时间和结束时间,计算出时间区间列表。例如,在输入 2019-06-01 和 2019-07-01 时,输出“2019-06-01 00:00:00|2019-06-30 23:59:59”、“2019-07-01 00:00:00|2019-07-01 23:59:59”两个时间区间。

四、总结

时间计算在日常开发中是经常遇到的任务,Java 中提供了多种计算日期的方法和类。其中,我们建议使用 Calendar 类,实现增减日期,包含了较为丰富的接口。此外,为方便使用和复用,我们可以封装一个时间计算工具类,增加代码复用性。