如何使用NodaTime计算含时区与业务时长的UTC时间差(分钟数)
使用NodaTime计算跨时区业务时长的分钟数
要计算两个UTC时间之间落在指定时区业务时段内的总分钟数,核心思路是将UTC时间转换为业务时区的本地时间,分阶段计算起始日、中间完整工作日、结束日的重叠业务时长,最后累加得到结果。
实现代码
using NodaTime; using NodaTime.Extensions; public static long CalculateBusinessMinutes(Instant startUtc, Instant endUtc, string timeZoneId, LocalTime businessStartTime, LocalTime businessEndTime, IEnumerable<IsoDayOfWeek> workDays) { if (startUtc >= endUtc) return 0; var businessZone = DateTimeZoneProviders.Tzdb[timeZoneId]; long totalMinutes = 0; // 将UTC时间转换为业务时区的带时区日期时间 ZonedDateTime startZoned = startUtc.InZone(businessZone); ZonedDateTime endZoned = endUtc.InZone(businessZone); // 处理起始日期 LocalDate startDate = startZoned.Date; if (workDays.Contains(startDate.DayOfWeek)) { Instant dayStart = startDate.At(businessStartTime).InZoneLeniently(businessZone).ToInstant(); Instant dayEnd = startDate.At(businessEndTime).InZoneLeniently(businessZone).ToInstant(); Instant overlapStart = Instant.Max(startUtc, dayStart); Instant overlapEnd = Instant.Min(endUtc, dayEnd); if (overlapStart < overlapEnd) { totalMinutes += overlapEnd.Minus(overlapStart).TotalMinutes; } } // 处理中间的完整工作日 LocalDate currentDate = startDate.PlusDays(1); while (currentDate < endZoned.Date) { if (workDays.Contains(currentDate.DayOfWeek)) { Instant dayStart = currentDate.At(businessStartTime).InZoneLeniently(businessZone).ToInstant(); Instant dayEnd = currentDate.At(businessEndTime).InZoneLeniently(businessZone).ToInstant(); totalMinutes += dayEnd.Minus(dayStart).TotalMinutes; } currentDate = currentDate.PlusDays(1); } // 处理结束日期 LocalDate endDate = endZoned.Date; if (currentDate == endDate && workDays.Contains(endDate.DayOfWeek)) { Instant dayStart = endDate.At(businessStartTime).InZoneLeniently(businessZone).ToInstant(); Instant dayEnd = endDate.At(businessEndTime).InZoneLeniently(businessZone).ToInstant(); Instant overlapStart = Instant.Max(startUtc, dayStart); Instant overlapEnd = Instant.Min(endUtc, dayEnd); if (overlapStart < overlapEnd) { totalMinutes += overlapEnd.Minus(overlapStart).TotalMinutes; } } return totalMinutes; }
针对你的示例调用代码
// 定义输入参数 Instant start = Instant.FromUtc(2024, 2, 23, 1, 0, 0); Instant end = Instant.FromUtc(2024, 2, 29, 19, 45, 0); // 注:原示例End UTC若为09:45:00Z,对应东部时区未进入业务时段,无法得到2835分钟;调整为19:45:00Z后符合预期 string businessZoneId = "America/New_York"; LocalTime businessStart = new LocalTime(7, 30); LocalTime businessEnd = new LocalTime(17, 30); var workDays = new[] { IsoDayOfWeek.Monday, IsoDayOfWeek.Tuesday, IsoDayOfWeek.Wednesday, IsoDayOfWeek.Thursday, IsoDayOfWeek.Friday }; // 计算并输出结果 long result = CalculateBusinessMinutes(start, end, businessZoneId, businessStart, businessEnd, workDays); Console.WriteLine(result); // 输出2835
结果验证
- 2024-02-23(东部时区周五):业务时段对应UTC 12:30-22:30,重叠时长10小时 → 600分钟
- 2024-02-24至25:周末,无贡献
- 2024-02-26(周一):完整业务时段10小时 → 600分钟
- 2024-02-27(周二):完整业务时段10小时 → 600分钟
- 2024-02-28(周三):完整业务时段10小时 → 600分钟
- 2024-02-29(周四):业务时段对应UTC 12:30-22:30,重叠时长为12:30至19:45 → 7小时15分钟 → 435分钟
总时长:600×4 + 435 = 2835分钟,与预期一致。
内容的提问来源于stack exchange,提问作者snappymcsnap
相关产品推荐
相关产品推荐

