c# - 使用 TimePeriod.NET 的 CalendarPeriodCollector 计算 "working time"给出了意外的结果

标签 c# datetime

我正在尝试计算服务水平协议(protocol)的到期日,同时,我还需要从另一个方向回算服务水平协议(protocol)。

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

  • 开始DateTime和一个 TimeSpan , 你应该收到 DateTime服务水平协议(protocol)到期日期(到期日期)。
  • 开始DateTime结束DateTime , 你应该收到 TimeSpan该服务水平协议(protocol)需要多长时间。

所有源代码(测试项目在 GitHub 上)。我有一个 ServiceLevelManager完成所有工作的类。它需要一个列表 WorkDaysHolidayPeriods , 以便计算出可以工作的时间。 CalendarPeriodCollector类正在给出意想不到的结果。在根据时间跨度确定截止日期时确实起作用的期望值,在我回算它们时计算不正确。

任何人都可以看到我是否做错了什么,或者库是否有错误?

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);
        }
    }
}

失败的单元测试提取如下:

[TestFixture]
public class ServicePlannerManagerTest
{
        [Test, TestCaseSource("LocalSource")]
    public void GetRemainingWorkingTimeWithHolidayShouldOnlyEnumerateWorkingTime(DateTime startTime, TimeSpan workingHours, DateTime expectedDueDate, string expectation)
    {
        // Arrange
        var workDays = new List<WorkDay>
        { 
            new WorkDay(DayOfWeek.Monday, new DateTime(1, 1, 1, 9, 0, 0), new DateTime(1, 1, 1, 17, 0, 0)),
            new WorkDay(DayOfWeek.Tuesday, new DateTime(1, 1, 1, 9, 0, 0), new DateTime(1, 1, 1, 17, 0, 0)),
            new WorkDay(DayOfWeek.Wednesday, new DateTime(1, 1, 1, 9, 0, 0), new DateTime(1, 1, 1, 17, 0, 0)),
            new WorkDay(DayOfWeek.Thursday, new DateTime(1, 1, 1, 9, 0, 0), new DateTime(1, 1, 1, 17, 0, 0)),
            new WorkDay(DayOfWeek.Friday, new DateTime(1, 1, 1, 9, 0, 0), new DateTime(1, 1, 1, 17, 0, 0)),
        };
        var holidayPeriods = new List<HolidayPeriod>
        { 
            new HolidayPeriod(new DateTime(2015, 9, 15, 00, 0, 0), new DateTime(2015, 9, 16, 0, 0, 0))
        };
        var service = new ServicePlannerManager(workDays, holidayPeriods);

        // Act
        var result = service.GetRemainingWorkingTime(startTime, expectedDueDate);

        // Assert - 
        Assert.AreEqual(workingHours.TotalHours, result.TotalHours, expectation);
    }

    protected IEnumerable LocalSource()
    {
        yield return
            new TestCaseData(
                new DateTime(2015, 9, 14, 9, 0, 0),
                new TimeSpan(23, 0, 0),
                new DateTime(2015, 9, 17, 16, 0, 0),
                    "5. Expected 23 hours of working time to end on the 17/09/2015 16:00. Monday to Thursday evening. Just short of 3 full working days by one hour. Tuesday is holiday.");
    }
}

这个测试的输出是

5. Expected 23 hours of working time to end on the 17/09/2015 16:00. Monday to Thursday evening. Just short of 3 full working days by one hour. Tuesday is holiday.

Expected: 23.0d
But was:  15.999999999944444d

我想知道是我用错了收集器,还是收集器有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)

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

关于c# - 使用 TimePeriod.NET 的 CalendarPeriodCollector 计算 "working time"给出了意外的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32700398/

相关文章:

sql - 如何将日期时间值插入 SQLite 数据库?

c# - 从基类继承字段的正确方法?

javascript - 获取具有特定时间戳的今天日期

C# - .net core DateTime 转换为 UTC 分钟偏移量

c# - 为 IComparer<DictionaryEntry> 实现 IComparer<T>

android - android中图像/图片/jpg的日期问题(DATE_TAKEN)

python - 如何在python中获取当前时间并分解为年、月、日、小时、分钟?

c# - LINQ 根据 IList<int> 从 IList<T> 中删除某些元素

c# - 如何通过与数据源不同的文本过滤 ASPxGridView

c# - 是否可以验证 Windows 服务是否正在从另一台计算机运行?