You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java按工作小时/工作日计算到期时间实现求助

解决工作小时到期时间计算问题

原代码核心问题分析

  1. 工作时段定义错误:需求明确是UTC周一至周五8:00-17:00,但代码中workHourStartUtc=6、workHourEndUtc=15,完全不符合要求。
  2. 逻辑方向颠倒:要计算8小时后的到期时间,代码却全程使用minusHours做减法,结果只会是过去的时间,与需求完全相悖。
  3. 跨工作日/周末处理粗糙:仅通过判断周一就减72小时,其他情况减24小时,既没考虑周五之后需跳过周末的场景,也未处理当前时间在工作时段外的情况(比如例1的UTC19:00已过当日工作结束时间)。
  4. 忽略分钟精度:仅按小时维度计算,未考虑分钟级别的时间精确性。
  5. 重复获取当前时间:多次调用LocalDateTime.now(ZoneOffset.UTC),可能因跨分钟导致前后时间不一致。

正确实现方案

使用Java 8+标准的java.time API实现,精准处理工作时段、跨工作日及时区转换逻辑:

import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;

public class WorkHourCalculator {
    // UTC工作时段:周一至周五 8:00-17:00
    private static final int WORK_START_UTC = 8;
    private static final int WORK_END_UTC = 17;
    private static final int WORK_HOURS_PER_DAY = WORK_END_UTC - WORK_START_UTC;

    /**
     * 计算指定工作小时后的到期时间
     * @param inputTime 输入时间(本地时间)
     * @param inputZone 输入时间所在时区
     * @param workHoursToAdd 需要添加的工作小时数
     * @return 到期时间(转换回输入时区的本地时间)
     */
    public static LocalDateTime calculateExpiryTime(LocalDateTime inputTime, ZoneId inputZone, int workHoursToAdd) {
        // 将输入时间转换为UTC时间统一处理
        ZonedDateTime inputUtc = inputTime.atZone(inputZone).withZoneSameInstant(ZoneOffset.UTC);
        LocalDateTime currentUtc = inputUtc.toLocalDateTime();
        int remainingHours = workHoursToAdd;

        while (remainingHours > 0) {
            DayOfWeek day = currentUtc.getDayOfWeek();
            // 跳过周末,直接跳到下周一工作开始时间
            if (day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY) {
                currentUtc = currentUtc.with(DayOfWeek.MONDAY)
                        .withHour(WORK_START_UTC)
                        .withMinute(0)
                        .withSecond(0);
                continue;
            }

            int currentHour = currentUtc.getHour();
            // 当前时间未到当日工作开始时间,跳到工作开始点
            if (currentHour < WORK_START_UTC) {
                currentUtc = currentUtc.withHour(WORK_START_UTC)
                        .withMinute(0)
                        .withSecond(0);
            }
            // 当前时间已过当日工作结束时间,跳到下一个工作日开始点
            else if (currentHour >= WORK_END_UTC) {
                currentUtc = currentUtc.plusDays(1)
                        .withHour(WORK_START_UTC)
                        .withMinute(0)
                        .withSecond(0);
                continue;
            }

            // 计算当日剩余可使用的工作小时
            int remainingToday = WORK_END_UTC - currentHour;
            // 取本次可添加的小时数(不超过剩余需求,也不超过当日剩余工作时间)
            int addHours = Math.min(remainingHours, remainingToday);
            currentUtc = currentUtc.plusHours(addHours);
            remainingHours -= addHours;
        }

        // 将UTC到期时间转换回输入时区的本地时间
        return currentUtc.atZone(ZoneOffset.UTC)
                .withZoneSameInstant(inputZone)
                .toLocalDateTime();
    }

    // 测试示例
    public static void main(String[] args) {
        // 例1:UTC19:00创建,到期时间应为次日UTC16:00
        LocalDateTime example1Input = LocalDateTime.of(2024, 5, 20, 19, 0);
        LocalDateTime example1Expiry = calculateExpiryTime(example1Input, ZoneOffset.UTC, 8);
        System.out.println("例1到期时间(UTC):" + example1Expiry); // 输出:2024-05-21T16:00

        // 例2:SA时间16:00广播,到期时间为次日SA时间15:00(SA时区为Africa/Johannesburg,UTC+2)
        LocalDateTime example2Input = LocalDateTime.of(2024, 5, 20, 16, 0);
        LocalDateTime example2Expiry = calculateExpiryTime(example2Input, ZoneId.of("Africa/Johannesburg"), 8);
        System.out.println("例2到期时间(SA):" + example2Expiry); // 输出:2024-05-21T15:00
    }
}

代码关键说明

  • 统一转换为UTC时间处理,避免时区差异导致的计算错误;
  • 循环处理剩余工作小时,逐个工作日计算,自动跳过周末;
  • 精准处理工作时段边界:未到工作时间则跳至当日开始,已过工作时间则跳至下一个工作日开始;
  • 支持任意输入时区,最终结果可转换回原时区,满足多时区场景需求。

内容的提问来源于stack exchange,提问作者Saurabh Kohade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 22:00:54