摘要:主要功能包括解析不同格式的时间字符串、生成默认时间值以及处理时间参数。它能识别"yyyy-MM-dd"或"yyyy-MM-dd HH:mm:ss"两种格式的时间输入,对于仅日期的输入会自动补全时间部分(起始时间补全为00:00:00,结束时间补全为23:59:59),并对非法格式抛出明确异常。该类提供了生成默认时间的方法,包括返回一个月前00:00:00的默认起始时间和返回当天23:59:59的默认结束时间,同时提供了处理时间参数的方法,可以自动处理空值情况并应用默认值。

关键词:Java;时间处理;工具类;日期时间解析

一、引言

在Java开发中,对时间的处理是一个常见需求。不同业务场景可能需要对时间进行不同格式的解析、设置默认时间值以及处理用户输入的时间参数等操作。TimeUtils类旨在提供一套统一且便捷的方法来满足这些需求,提高代码的复用性和开发效率。

二、TimeUtils类设计

2.1 类结构与成员变量

TimeUtils类包含两个私有静态常量DATE_FORMATTERDATETIME_FORMATTER,分别定义了日期格式"yyyy - MM - dd"和日期时间格式"yyyy - MM - dd HH:mm:ss",用于时间的格式化和解析。

// 日期格式: yyyy-MM-dd
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// 日期时间格式: yyyy-MM-dd HH:mm:ss
private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

2.2 时间解析方法

parseTime方法用于解析输入的时间字符串,并根据isStartTime标志设置具体时间。它首先尝试按完整日期时间格式解析,如果失败则尝试按日期格式解析,并根据isStartTime决定设置为当天的起始时间(00:00:00)或结束时间(23:59:59)。如果两种解析都失败,则抛出RuntimeException

public static String parseTime(String timeStr, boolean isStartTime) {
    try {
        // 尝试按完整日期时间格式解析
        LocalDateTime dateTime = LocalDateTime.parse(timeStr, DATETIME_FORMATTER);
        return dateTime.format(DATETIME_FORMATTER);
    } catch (Exception e1) {
        try {
            // 尝试按日期格式解析
            LocalDate date = LocalDate.parse(timeStr, DATE_FORMATTER);
            if (isStartTime) {
                // 起始时间设置为当天的00:00:00
                return date.atStartOfDay().format(DATETIME_FORMATTER);
            } else {
                // 结束时间设置为当天的23:59:59
                return date.atTime(23, 59, 59).format(DATETIME_FORMATTER);
            }
        } catch (Exception e2) {
            throw new RuntimeException("时间格式错误,支持格式:yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss", e2);
        }
    }
}

2.3 获取默认时间方法

getDefaultStartTime方法返回一个月前的起始时间(00:00:00),getDefaultEndTime方法返回当天的结束时间(23:59:59)。(看标题四,选择更多款式时间设置,比如获取当天时间)

public static String getDefaultStartTime() {
    return LocalDate.now()
           .minus(1, ChronoUnit.MONTHS)
           .atStartOfDay()
           .format(DATETIME_FORMATTER);
}
public static String getDefaultEndTime() {
    return LocalDate.now()
           .atTime(23, 59, 59)
           .format(DATETIME_FORMATTER);
}

现在是2025-07-01
.一个月前就是2025-06-01.

2.4 时间参数处理方法

processStartTimeprocessEndTime方法分别用于处理起始时间和结束时间参数。如果输入的时间字符串为空或空白,将返回默认的起始或结束时间;否则,调用parseTime方法进行解析。

public static String processStartTime(String startTime) {
    if (StringUtils.isBlank(startTime)) {
        return getDefaultStartTime();
    }
    return parseTime(startTime, true);
}
public static String processEndTime(String endTime) {
    if (StringUtils.isBlank(endTime)) {
        return getDefaultEndTime();
    }
    return parseTime(endTime, false);
}

三、TimeUtilsDemo类调用

TimeUtilsDemo类通过多个示例展示了TimeUtils类的使用方法。

  1. 解析完整日期时间格式:输入完整的日期时间字符串"2023 - 01 - 15 14:30:00",输出保持相同格式。
// 示例1: 解析完整日期时间格式
String time1 = "2023-01-15 14:30:00";
String parsed1 = TimeUtils.parseTime(time1, true);
System.out.println("解析完整日期时间格式:");
System.out.println("输入: " + time1);
System.out.println("输出: " + parsed1);
System.out.println();
  1. 解析仅日期格式(起始时间):输入仅包含日期的字符串"2023 - 01 - 15",输出为当天起始时间"2023 - 01 - 15 00:00:00"
// 示例2: 解析仅日期格式(起始时间)
String time2 = "2023-01-15";
String parsed2 = TimeUtils.parseTime(time2, true);
System.out.println("解析仅日期格式(起始时间):");
System.out.println("输入: " + time2);
System.out.println("输出: " + parsed2);
System.out.println();
  1. 解析仅日期格式(结束时间):输入仅包含日期的字符串"2023 - 01 - 15",输出为当天结束时间"2023 - 01 - 15 23:59:59"
// 示例3: 解析仅日期格式(结束时间)
String time3 = "2023-01-15";
String parsed3 = TimeUtils.parseTime(time3, false);
System.out.println("解析仅日期格式(结束时间):");
System.out.println("输入: " + time3);
System.out.println("输出: " + parsed3);
System.out.println();
  1. 处理空白起始时间:输入空白字符串,输出默认起始时间。
// 示例4: 处理空白起始时间(应返回默认值)
String time4 = "";
String parsed4 = TimeUtils.processStartTime(time4);
System.out.println("处理空白起始时间:");
System.out.println("输入: \"" + time4 + "\"");
System.out.println("输出: " + parsed4);
System.out.println();
  1. 处理空白结束时间:输入空白字符串,输出默认结束时间。
// 示例5: 处理空白结束时间(应返回默认值)
String time5 = "";
String parsed5 = TimeUtils.processEndTime(time5);
System.out.println("处理空白结束时间:");
System.out.println("输入: \"" + time5 + "\"");
System.out.println("输出: " + parsed5);
System.out.println();
  1. 处理有效起始时间:输入有效日期字符串,输出解析后的起始时间。
// 示例6: 处理有效起始时间
String time6 = "2023-01-15";
String parsed6 = TimeUtils.processStartTime(time6);
System.out.println("处理有效起始时间:");
System.out.println("输入: " + time6);
System.out.println("输出: " + parsed6);
System.out.println();
  1. 处理有效结束时间:输入有效日期字符串,输出解析后的结束时间。
// 示例7: 处理有效结束时间
String time7 = "2023-01-15";
String parsed7 = TimeUtils.processEndTime(time7);
System.out.println("处理有效结束时间:");
System.out.println("输入: " + time7);
System.out.println("输出: " + parsed7);
解析完整日期时间格式:
输入: 2023-01-15 14:30:00
输出: 2023-01-15 14:30:00

解析仅日期格式(起始时间):
输入: 2023-01-15
输出: 2023-01-15 00:00:00

解析仅日期格式(结束时间):
输入: 2023-01-15
输出: 2023-01-15 23:59:59

处理空白起始时间:
输入: ""
输出: 2025-06-09 00:00:00

处理空白结束时间:
输入: ""
输出: 2025-07-09 23:59:59

处理有效起始时间:
输入: 2023-01-15
输出: 2023-01-15 00:00:00

处理有效结束时间:
输入: 2023-01-15
输出: 2023-01-15 23:59:59

Process finished with exit code 0

如果你想修改 getDefaultStartTime() 方法,使其返回不同的默认起始时间(而不是固定的"一个月前的00:00:00"),以下是几种常见的调整方式及对应的代码实现:


四、更改初始时间设置

1. 修改为固定日期(如2025-01-01 00:00:00)

public static String getDefaultStartTime() {
    return LocalDate.parse("2025-01-01")  // 直接指定固定日期
           .atStartOfDay()               // 设置为当天的00:00:00
           .format(DATETIME_FORMATTER);  // 格式化为字符串
}

2. 修改为当前日期的00:00:00(当天起始时间)

public static String getDefaultStartTime() {
    return LocalDate.now()                // 当前日期
           .atStartOfDay()               // 设置为当天的00:00:00
           .format(DATETIME_FORMATTER);  // 格式化为字符串
}

3. 修改为N天前的00:00:00(如7天前)

public static String getDefaultStartTime() {
    return LocalDate.now()
           .minus(7, ChronoUnit.DAYS)    // 减去7天(可调整天数)
           .atStartOfDay()               // 设置为当天的00:00:00
           .format(DATETIME_FORMATTER);  // 格式化为字符串
}

4. 修改为自定义日期(通过参数动态传入)

如果希望更灵活地指定日期,可以改为带参数的方法:

/**
 * 获取自定义起始时间(默认00:00:00)
 * @param daysOffset 相对于当前日期的偏移天数(正数为未来,负数为过去)
 * @return 格式化后的起始时间字符串
 */
public static String getDefaultStartTime(int daysOffset) {
    return LocalDate.now()
           .minus(daysOffset, ChronoUnit.DAYS)  // 按偏移天数计算日期
           .atStartOfDay()                    // 设置为00:00:00
           .format(DATETIME_FORMATTER);       // 格式化为字符串
}

// 调用示例:获取3天前的起始时间
String startTime = getDefaultStartTime(3);  // 3天前
String startTime = getDefaultStartTime(-5); // 5天后(未来)

5. 修改为指定月份的1号00:00:00(如每月1号)

public static String getDefaultStartTime() {
    return LocalDate.now()
           .withDayOfMonth(1)             // 设置为当月1号
           .atStartOfDay()                // 设置为00:00:00
           .format(DATETIME_FORMATTER);   // 格式化为字符串
}

6. 完全自定义日期时间(精确到小时分钟秒)

如果需要更精确的控制(如固定时间点):

public static String getDefaultStartTime() {
    return LocalDateTime.of(2023, 1, 1, 8, 30, 0)  // 指定2023-01-01 08:30:00
           .format(DATETIME_FORMATTER);  // 复用已定义的格式化器
}

如何选择?

场景

推荐方法

固定历史日期

方法1(直接指定日期)

当天起始时间

方法2

过去N天

方法3(调整天数)

动态调整日期

方法4(带参数)

每月1号统计

方法5

精确时间点

方法6

五、完整代码

package com.example.utils;
import org.apache.commons.lang3.StringUtils;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

/**
 * 时间处理工具类
 */
public class TimeUtils {

    // 日期格式: yyyy-MM-dd
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    // 日期时间格式: yyyy-MM-dd HH:mm:ss
    private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 解析时间字符串,根据是否为起始时间设置具体时间
     * @param timeStr 时间字符串,格式可为 yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss
     * @param isStartTime 是否为起始时间
     * @return 格式化后的时间字符串,格式为 yyyy-MM-dd HH:mm:ss
     * @throws RuntimeException 如果时间格式错误
     */
    public static String parseTime(String timeStr, boolean isStartTime) {
        try {
            // 尝试按完整日期时间格式解析
            LocalDateTime dateTime = LocalDateTime.parse(timeStr, DATETIME_FORMATTER);
            return dateTime.format(DATETIME_FORMATTER);
        } catch (Exception e1) {
            try {
                // 尝试按日期格式解析
                LocalDate date = LocalDate.parse(timeStr, DATE_FORMATTER);
                if (isStartTime) {
                    // 起始时间设置为当天的00:00:00
                    return date.atStartOfDay().format(DATETIME_FORMATTER);
                } else {
                    // 结束时间设置为当天的23:59:59
                    return date.atTime(23, 59, 59).format(DATETIME_FORMATTER);
                }
            } catch (Exception e2) {
                throw new RuntimeException("时间格式错误,支持格式:yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss", e2);
            }
        }
    }

    /**
     * 获取默认起始时间(一个月前的00:00:00)
     * @return 格式化后的时间字符串
     */
    public static String getDefaultStartTime() {
        return LocalDate.now()
                .minus(1, ChronoUnit.MONTHS)
                .atStartOfDay()
                .format(DATETIME_FORMATTER);
    }

    /**
     * 获取默认结束时间(当天的23:59:59)
     * @return 格式化后的时间字符串
     */
    public static String getDefaultEndTime() {
        return LocalDate.now()
                .atTime(23, 59, 59)
                .format(DATETIME_FORMATTER);
    }

    /**
     * 处理起始时间参数
     * @param startTime 输入的起始时间字符串(可为null或空)
     * @return 处理后的起始时间字符串
     */
    public static String processStartTime(String startTime) {
        if (StringUtils.isBlank(startTime)) {
            return getDefaultStartTime();
        }
        return parseTime(startTime, true);
    }

    /**
     * 处理结束时间参数
     * @param endTime 输入的结束时间字符串(可为null或空)
     * @return 处理后的结束时间字符串
     */
    public static String processEndTime(String endTime) {
        if (StringUtils.isBlank(endTime)) {
            return getDefaultEndTime();
        }
        return parseTime(endTime, false);
    }
}