1. 引言

在Java中处理日期时,我们有时需要将它们四舍五入到特定的单位,如小时、天或月。这可以用于数据分析和报告,以及控制显示信息的详细程度。

在这篇教程中,我们将学习如何使用java.util.DateLocalDateTimeZonedDateTime类来对日期进行四舍五入。

2. 基本四舍五入

基本的四舍五入方法是,在Java中截断日期的时间部分。具体来说,这意味着将所有时间元素设置为零。以下是实现方式:

Date roundToDay(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();
}

roundToDay()方法接受一个Date对象作为参数。然后,我们使用Calendar类的setTime()方法,并通过set()方法将小时、分钟、秒和毫秒四舍五入到当天的开始。

3. 四舍五入到最近单位

我们可以选择让四舍五入方法固定,使日期在整个单位(如小时、天或月)之间匹配,如下所示:

Date roundToNearestUnit(Date date, int unit) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);

    switch (unit) {
        case Calendar.HOUR:
            int minute = calendar.get(Calendar.MINUTE);
            if (minute >= 0 && minute < 15) {
                calendar.set(Calendar.MINUTE, 0);
            } else if (minute >= 15 && minute < 45) {
                calendar.set(Calendar.MINUTE, 30);
            } else {
                calendar.set(Calendar.MINUTE, 0);
                calendar.add(Calendar.HOUR_OF_DAY, 1);
            }
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            break;

        case Calendar.DAY_OF_MONTH:
            int hour = calendar.get(Calendar.HOUR_OF_DAY);
            if (hour >= 12) {
                calendar.add(Calendar.DAY_OF_MONTH, 1);
            }
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            break;

        case Calendar.MONTH:
            int day = calendar.get(Calendar.DAY_OF_MONTH);
            if (day >= 15) {
                calendar.add(Calendar.MONTH, 1);
            }
            calendar.set(Calendar.DAY_OF_MONTH, 1);
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            break;
    }

    return calendar.getTime();
}

这里有一个roundToNearestUnit()方法,它期望接收一个Date和一个整数单位作为输入,目的是将输入日期四舍五入到指定的时间单位。

为此,代码创建了一个Calendar实例,并将其设置为输入日期。根据所选的单位(Calendar.HOURCalendar.DAY_OF_MONTHCalendar.MONTH),我们将分钟、小时或日期组件调整到允许日期四舍五入到最近的30分钟间隔、天或月。同时,我们确保秒和毫秒也设置为零。按照定义的时间单位进行上述调整后,代码返回生成的Calendar实体对应的新Date

4. 使用LocalDateTime进行四舍五入

LocalDateTime是Java中的新类,它使我们能够以不同的级别对日期进行四舍五入,从而为处理日期和时间提供更现代的解决方案。以下是两种使用LocalDateTime进行日期四舍五入的方法:

LocalDateTime roundToStartOfMonth(LocalDateTime dateTime) {
    return dateTime.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
}

roundToStartOfMonth()方法中,我们将月份的天数设置为1,以便将给定的LocalDateTime四舍五入到月份的开始,并在午夜(0小时、0分钟、0秒和0纳秒)重置所有时间组件。

我们还可以使用LocalDateTime将给定的日期四舍五入到周末,如下所示:

LocalDateTime roundToEndOfWeek(LocalDateTime dateTime) {
    return dateTime.with(TemporalAdjusters.next(DayOfWeek.SATURDAY))
      .withHour(23)
      .withMinute(59)
      .withSecond(59)
      .withNano(999);
}

RoundToEndOfWeek()方法利用TemporalAdjusters.next()函数找到下一个周六,并设置时间组件(如23小时、59分钟、59秒和999纳秒),从而将LocalDateTime四舍五入到下个周六的末尾。

5. 使用ZonedDateTime进行四舍五入

对于存在时区的情况,ZonedDateTime类使得日期四舍五入成为可能。以下是两种使用ZonedDateTime进行日期四舍五入的方法:

ZonedDateTime roundToStartOfMonth(ZonedDateTime dateTime) {
    return dateTime.withDayOfMonth(1)
      .withHour(0)
      .withMinute(0)
      .withSecond(0)
      .with(ChronoField.MILLI_OF_SECOND, 0)
      .with(ChronoField.MICRO_OF_SECOND, 0)
      .with(ChronoField.NANO_OF_SECOND, 0);
}

roundToStartOfMonth()方法接受一个ZonedDateTime作为输入并返回一个新的ZonedDateTime具体来说,它返回相同的日期和时间,但时间部分设置为月初的午夜(00:00:00.000)。

要将输入的Date四舍五入到当前周的末尾,请参阅以下示例:

public static ZonedDateTime roundToEndOfWeek(ZonedDateTime dateTime) {
    return dateTime.with(TemporalAdjusters.next(DayOfWeek.SATURDAY))
      .withHour(23)
      .withMinute(59)
      .withSecond(59)
      .with(ChronoField.MILLI_OF_SECOND, 999)
      .with(ChronoField.MICRO_OF_SECOND, 999)
      .with(ChronoField.NANO_OF_SECOND, 999);
}

roundToEndOfWeek()方法中,我们首先使用TemporalAdjusters找到下一个周六,然后将时间设置为(23:59:59.999999999)。

6. 总结

总的来说,处理基于时间的数据,特别是对日期进行四舍五入是至关重要的任务。在这篇教程中,我们了解了一些将日期四舍五入到不同单位和精度级别的方法,如截断和自定义四舍五入。

请记住,对于国际应用,要考虑时间的变化。

如往常一样,本文的完整代码示例可在GitHub上找到:core-java-modules/core-java-8-datetime-2