C# 计算;“工作时间”;使用TimePeriod.NET';Calendars PeriodCollector提供了意外的结果

C# 计算;“工作时间”;使用TimePeriod.NET';Calendars PeriodCollector提供了意外的结果,c#,datetime,C#,Datetime,我试图计算服务级别协议的到期日,同时,我还需要反向计算服务级别协议 我一直在努力计算“工作时间”(即在一组天内可以工作的时间),并决定使用第三方库来完成该任务。我需要能够做两件事: 给定开始日期时间和时间间隔,您应该会收到服务级别协议到期日期(到期日期)的日期时间 给定开始DateTime和结束DateTime,您应该会收到服务级别协议所需时间的TimeSpan 所有源代码(测试项目处于打开状态)。我有一个ServiceLevelManager类来完成所有工作。它需要列出工作日和假期,以确定

我试图计算服务级别协议的到期日,同时,我还需要反向计算服务级别协议

我一直在努力计算“工作时间”(即在一组天内可以工作的时间),并决定使用第三方库来完成该任务。我需要能够做两件事:

  • 给定开始日期时间和时间间隔,您应该会收到服务级别协议到期日期(到期日期)的日期时间
  • 给定开始
    DateTime
    和结束
    DateTime
    ,您应该会收到服务级别协议所需时间的
    TimeSpan
所有源代码(测试项目处于打开状态)。我有一个
ServiceLevelManager
类来完成所有工作。它需要列出
工作日
假期
,以确定哪些时间可以工作。
CalendarPeriodCollector
类给出了意外的结果。在从时间跨度确定到期日时,确实起作用的期望值在我反向计算时计算不正确

有人能看到我是否做错了什么,或者图书馆是否有bug吗

namespace ServicePlanner
{
    using System;
    using System.Collections.Generic;
    using Itenso.TimePeriod;

    public class ServicePlannerManager
    {
        public ServicePlannerManager(IEnumerable<WorkDay> workDays, IEnumerable<HolidayPeriod> holidays)
        {
            this.WorkDays = workDays;
            this.Holidays = holidays;
        }

        public IEnumerable<WorkDay> WorkDays { get; set; }

        public IEnumerable<HolidayPeriod> Holidays { get; set; }

        public TimeSpan GetRemainingWorkingTime(DateTime start, DateTime dueDate)
        {
            var filter = new CalendarPeriodCollectorFilter();
            foreach (var dayOfWeek in this.WorkDays)
            {
                filter.CollectingDayHours.Add(new DayHourRange(dayOfWeek.DayOfWeek, new Time(dayOfWeek.StartTime), new Time(dayOfWeek.EndTime)));
            }

            foreach (var holiday in this.Holidays)
            {
                filter.ExcludePeriods.Add(new TimeBlock(holiday.StartTime, holiday.EndTime));
            }

            var range = new CalendarTimeRange(start, dueDate);
            var collector = new CalendarPeriodCollector(filter, range);
            collector.CollectHours();

            var duration = collector.Periods.GetTotalDuration(new TimeZoneDurationProvider(TimeZoneInfo.FindSystemTimeZoneById("UTC")));
            return duration;
            //var rounded = Math.Round(duration.TotalMinutes, MidpointRounding.AwayFromZero);
            //return TimeSpan.FromMinutes(rounded);
        }
    }
}

我想知道我是否错误地使用了收集器,或者收集器是否有bug。

这看起来是一个解决常见问题的很好的库

最好的方法是输出句点集合中的句点,以帮助您调试问题。

我已经重写了您的测试,以使用示例文档中的基类型:

        [Test, TestCaseSource("LocalSource")]
    public void SO_GetRemainingWorkingTimeWithHolidayShouldOnlyEnumerateWorkingTime(DateTime startTime,
        TimeSpan workingHours, DateTime expectedDueDate, string expectation)
    {
        CalendarPeriodCollectorFilter filter = new CalendarPeriodCollectorFilter();
        filter.Months.Add(YearMonth.September); // only Januaries
        filter.WeekDays.Add(DayOfWeek.Monday); // 
        filter.WeekDays.Add(DayOfWeek.Tuesday); // 
        filter.WeekDays.Add(DayOfWeek.Wednesday); // 
        filter.WeekDays.Add(DayOfWeek.Thursday); // 
        filter.WeekDays.Add(DayOfWeek.Friday); // 
        filter.CollectingHours.Add(new HourRange(9, 17)); // working hours

        CalendarTimeRange testPeriod = new CalendarTimeRange(startTime, expectedDueDate);//new DateTime(2015, 9, 14, 9, 0, 0), new DateTime(2015, 9, 17, 18, 0, 0));
        Console.WriteLine("Calendar period collector of period: " + testPeriod);

        filter.ExcludePeriods.Add(new TimeBlock(new DateTime(2015, 9, 15, 00, 0, 0), new DateTime(2015, 9, 16, 0, 0, 0)));

        CalendarPeriodCollector collector = new CalendarPeriodCollector(filter, testPeriod);
        collector.CollectHours();

        foreach (ITimePeriod period in collector.Periods)
        {
            Console.WriteLine("Period: " + period); // THIS WILL HELP A LOT!
        }
        var result = collector.Periods.GetTotalDuration(new TimeZoneDurationProvider(TimeZoneInfo.FindSystemTimeZoneById("UTC")));

        Console.WriteLine(result);
            //
    }
这导致:

Calendar period collector of period: 14/09/2015 09:00:00 - 17/09/2015 15:59:59 | 3.06:59
Period: 14/09/2015 09:00:00 - 14/09/2015 16:59:59 | 0.07:59
Period: 16/09/2015 09:00:00 - 16/09/2015 16:59:59 | 0.07:59
15:59:59.9999998
因此,我注意到最后一个句点不见了。

如果你把课时的结束时间从下午4点改为下午6点(因此预计额外的一小时=24小时),那就差不多过去了。(您还需要对结果进行四舍五入)

因此,看起来总持续时间需要完全覆盖这些期间,不计算部分覆盖。您可以更改库的选项,也可以将工作日的每个小时添加为单独的收集小时(hacky)


希望这能让你更接近你需要的答案

请详细说明“计算不正确”的确切含义。给出输入、预期输出和实际输出的示例。谢谢。还要注意,这个特定的库在过去遇到过DST和时区问题。我在他们的变更日志中看到,v1.7.0对此做了一些调整,但在文档方面却很少。@MattJohnson源代码(Github)中的单元测试显示了预期。如果您运行单元测试,您将看到失败的单元测试。当然,我会在有时间时这样做。但总的来说,StackOverflow不赞成依靠外部站点来完成问题。请记住,StackOverflow的主要目的是创建对其他人有用的问题和答案的概要,而不仅仅是帮助您找到调试代码的人。如果问题既不简洁又不完整,那么它对其他人就没有多大帮助,也不会获得很多赞成票。对于StackOverflow,更好的做法是隔离一个失败的特定测试,并用尽可能少的代码重新生成它。请阅读:。然后有人可以回答你的代码有什么问题,或者库有什么问题。抱歉@Matt Johnson-看起来你已经在评论中找到了答案。我没有把它们全部展开去看。发布你的答案,Junto应该接受它而不是我的。那么它似乎是TimePeriodLibrary.NET中的一个bug?公共存储库中没有用于尝试发出请求的源代码。
        [Test, TestCaseSource("LocalSource")]
    public void SO_GetRemainingWorkingTimeWithHolidayShouldOnlyEnumerateWorkingTime(DateTime startTime,
        TimeSpan workingHours, DateTime expectedDueDate, string expectation)
    {
        CalendarPeriodCollectorFilter filter = new CalendarPeriodCollectorFilter();
        filter.Months.Add(YearMonth.September); // only Januaries
        filter.WeekDays.Add(DayOfWeek.Monday); // 
        filter.WeekDays.Add(DayOfWeek.Tuesday); // 
        filter.WeekDays.Add(DayOfWeek.Wednesday); // 
        filter.WeekDays.Add(DayOfWeek.Thursday); // 
        filter.WeekDays.Add(DayOfWeek.Friday); // 
        filter.CollectingHours.Add(new HourRange(9, 17)); // working hours

        CalendarTimeRange testPeriod = new CalendarTimeRange(startTime, expectedDueDate);//new DateTime(2015, 9, 14, 9, 0, 0), new DateTime(2015, 9, 17, 18, 0, 0));
        Console.WriteLine("Calendar period collector of period: " + testPeriod);

        filter.ExcludePeriods.Add(new TimeBlock(new DateTime(2015, 9, 15, 00, 0, 0), new DateTime(2015, 9, 16, 0, 0, 0)));

        CalendarPeriodCollector collector = new CalendarPeriodCollector(filter, testPeriod);
        collector.CollectHours();

        foreach (ITimePeriod period in collector.Periods)
        {
            Console.WriteLine("Period: " + period); // THIS WILL HELP A LOT!
        }
        var result = collector.Periods.GetTotalDuration(new TimeZoneDurationProvider(TimeZoneInfo.FindSystemTimeZoneById("UTC")));

        Console.WriteLine(result);
            //
    }
Calendar period collector of period: 14/09/2015 09:00:00 - 17/09/2015 15:59:59 | 3.06:59
Period: 14/09/2015 09:00:00 - 14/09/2015 16:59:59 | 0.07:59
Period: 16/09/2015 09:00:00 - 16/09/2015 16:59:59 | 0.07:59
15:59:59.9999998