对某些日期/时间计算感到困惑

时间:2009-12-09 07:50:30

标签: datetime

所以,我正在尝试计算符合某些标准的两个日期之间的时间(这里:工作/非工作),我对结果感到困惑,因为我无法找出错误原因。

但首先是一些代码;

**Input Date A:** 2009-01-01 2:00 pm
**Input Date B:** 2009-01-02 9:00 am

因此,正如您所看到的,总时间跨度(例如按DateB.Substract(DateA)计算) 19 小时。

我现在想要计算这个时间段内“非工作”小时的小时数,基于上午8点到下午5点的平均工作日 - 结果应该 15小时(所以,19 - 15 =总工作时间为4小时)(下午2:00至下午5:00加上午8:00至上午9:00)

但是,遵循我的代码

DateTime _Temp = new DateTime(2009, 1, 1, 14, 0, 0);
DateTime _End = new DateTime(2009, 1, 2, 9, 0, 0);

int _WorkDayStart = 8;
int _WorkDayEnd = 17;

int _Return = 0;

while (_End > _Temp)
{
    if (_Temp.Hour <= _WorkDayStart || _Temp.Hour >= _WorkDayEnd)
    _Return++;
    _Temp = _Temp.AddHours(1);
}

结果是 16小时(19 - 16 = 3小时的总工作时间) - 我没有看到错误在哪里,所以有一个小时失踪?!我在纸上重构它,它应该按预期工作......但不是: - /

有人看到了这个错误吗?

3 个答案:

答案 0 :(得分:3)

你计算两个结束为非工作,而不只是一个。这一行:

if (_Temp.Hour <= _WorkDayStart || _Temp.Hour >= _WorkDayEnd)

应该是:

if (_Temp.Hour < _WorkDayStart || _Temp.Hour >= _WorkDayEnd)

你有效地走过了“开始时间”。因此,8am本身应该算作工作时间,因为它是工作时间的 start ,但是下午5点不会,因为它是非工作时间的 start 。 / p>

我也强烈建议您在if语句的主体周围放置大括号,或者至少缩进以下行。 (我进一步建议你使用camelCase作为局部变量,但这只是一个惯例 - 我以前从未见过使用该约定为局部变量编写的C#代码。你可能想要阅读{{3 - 它没有指定局部变量,但它们;通常在驼峰的情况下。)

最后,我个人觉得更容易阅读左边的“正在改变的事情” - 所以我将你的循环条件改为:

while (_Temp < _End)

另一种方法是将其更改为for循环。通过所有这些更改,代码将是:

DateTime start = new DateTime(2009, 1, 1, 14, 0, 0);
DateTime end = new DateTime(2009, 1, 2, 9, 0, 0);

int workDayStart = 8;
int workDayEnd = 17;

int nonWorkHours = 0;

for (DateTime time = start; time < end; time = time.AddHours(1))
{
    if (time.Hour < workDayStart || time.Hour >= workDayEnd)
    {
        nonWorkHours++;
    }
}

最后,将其提取到一个方法中:

public static int CountNonWorkHours(DateTime start, DateTime end,
                                    int workDayStart, int workDayEnd)
{
    int nonWorkHours = 0;

    for (DateTime time = start; time < end; time = time.AddHours(1))
    {
        if (time.Hour < workDayStart || time.Hour >= workDayEnd)
        {
            nonWorkHours++;
        }
    }
    return nonWorkHours;
}
编辑:关于konamiman的建议......是的,每小时循环是非常低效的。但是,要做到这一点相对容易。除非你打算长时间使用 lot ,否则我会使用这个相当简单的代码。如果您尝试执行每日版本,那么在各种情况下最终会出现逐个错误。虽然我不喜欢效率低下的代码,但如果它没有伤害我,我不介意它:)

答案 1 :(得分:1)

如果您打算重用此代码,我会重构它以避免循环。您可以将整天的数量乘以每天的工时,然后将间隔的第一天和最后一天视为特殊情况。

答案 2 :(得分:0)

您也可以使用它来避免循环

DateTime startDate = new DateTime(2009, 1, 1, 14, 0, 0);
            DateTime endDate = new DateTime(2009, 1, 2, 9, 0, 0);
            int startTime = 8;
            int endTime = 17;
            int ret =   ((endDate.Subtract(startDate).Days - 1) * 8) 
                        + (startDate.Hour >= startTime && startDate.Hour < endTime ? endTime - startDate.Hour : 0)
                        + (endDate.Hour > startTime && endDate.Hour <= endTime ? endDate.Hour - startTime : 0);