Java按工作小时/工作日计算到期时间实现求助
解决工作小时到期时间计算问题
原代码核心问题分析
- 工作时段定义错误:需求明确是UTC周一至周五8:00-17:00,但代码中
workHourStartUtc=6、workHourEndUtc=15,完全不符合要求。 - 逻辑方向颠倒:要计算8小时后的到期时间,代码却全程使用
minusHours做减法,结果只会是过去的时间,与需求完全相悖。 - 跨工作日/周末处理粗糙:仅通过判断周一就减72小时,其他情况减24小时,既没考虑周五之后需跳过周末的场景,也未处理当前时间在工作时段外的情况(比如例1的UTC19:00已过当日工作结束时间)。
- 忽略分钟精度:仅按小时维度计算,未考虑分钟级别的时间精确性。
- 重复获取当前时间:多次调用
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
相关产品推荐
相关产品推荐

