【问题标题】:Checking a date is in an interval given by partial/periodic dates检查日期是在部分/定期日期给定的间隔内
【发布时间】:2022-11-11 01:52:58
【问题描述】:

我可以用start 和end 以YYMMDD 的格式定义一个间隔,但它们也可以是部分/周期性的——这意味着某些元素(日、月或年)可以留空。

例如,start = " 1115" 和end = " 0115" 的时间间隔是每年 11 月 15 日到 1 月 15 日。

我想检查非部分日期是否在间隔内。

int compareParial(const char* first, const char* second)
{
    for (int i = 0; i < 6; ++i)
    {
        if (first[i] != ' ' && second[i] != ' ' && first[i] != second[i])
            return first[i] > second[i] ? 1 : -1;
    }
    return 0;
}

bool isDateInInterval(const char* start, const char* end, const char* searchDate)
{
    int firstCompare = compareParial(start, searchDate);
    int endCompare = compareParial(end, searchDate);

    if (firstCompare <= 0 && endCompare >= 0)
        return true;

    // the date can still be in the interval if the start of the interval is in one year, but end in the next year
    bool switched = 0 < compareParial(start, end);
    if (switched && (firstCompare <= 0) != (endCompare >= 0))
       return true;

    return false;
}

int main()
{
    cout << boolalpha << isDateInInterval("  1115", "  0115", "251110") << endl;
    return 0;
}

更新:如果日期颠倒,请再次检查 searchDate 是否在。

我注意到的一个问题是,如果 start 和 end 颠倒了,但提供了年份。例如:isDateInInterval("200105", "190601", "251110") 将是 true

【问题讨论】:

  • 调试有什么你完毕?您的函数可以以两种不同的方式返回 true。你确定它的发生方式了吗?这将是调试此问题的第一步。
  • @pcarter 是的,第二种方式。开始 > 结束,但这还不足以说明它在
  • 然后下一步是查看compareParial 的for 循环,看看当它返回1 时i 是什么,然后在first 和second 中对应于索引@987654339 的值是什么@的值。查看代码,似乎是i 为 2 时。
  • 您的代码假定如果缺少年份,则 start 和 end 的年份相同。
  • @pcarter你是对的,但我不这样做

标签: c++ algorithm


【解决方案1】:

C++20 包含可以表示部分日期的类型:year、month、day、year_month、month_day 等。2

例如:

auto start = November/15;
auto end = January/15;

通过使用实际的日历类型,而不是字符串,您必须处理的逻辑可以大大简化。一个完整的year_month_day 可以与一对month_day 定义的区间进行比较,如下所示:

bool
compare_partial(std::chrono::month_day start, std::chrono::month_day end,
                std::chrono::year_month_day searchDate)
{
    using namespace std::chrono;

    // Guess that both start and end fall in the same year
    auto trial_start = start/searchDate.year();
    auto trial_end = end/searchDate.year();
    if (trial_start <= trial_end)
    {
        return trial_start <= searchDate && searchDate <= trial_end;
    }

    // start/y > end/y
    // Otherwise guess that searchDate comes after trail_start:
    if (trial_start <= searchDate)
    {
        // trial_end must be in the next year
        trial_end += years{1};
        return trial_start <= searchDate && searchDate <= trial_end;
    }
    // Otherwise searchDate < start/y  && start/y > end/y
    // trial_start must be in the previous year
    trial_start -= years{1};
    return trial_start <= searchDate && searchDate <= trial_end;
}

预先警告,即使这个答案也有些错误1.但是,通过使用实际的日历类型来执行诸如加/减一年之类的操作并进行比较,可以使代码更清晰,更易于阅读,因此不太可能包含错误。

该答案也仅针对 month_day 部分日期。您可能还拥有year_month 部分日期,或者month_day 和year_month 的混合。

std::chrono 没有 year_day 类型,我也不确定这意味着什么。如果您知道这意味着什么,我毫不怀疑 C++20 chrono 可以帮助您对其进行建模。

在任何情况下:

cout << boolalpha << compare_partial(November/15, January/15, 2025y/November/10) << endl;

将输出:

false

即使您不使用 C++20 chrono(或者它是免费预览版),强烈建议使用日历类型(可能是您自己制作)而不是字符串来建模,以创建一个健壮、无错误的解决方案。


1trial_end += years{1}; 等表达式不能保证是有效日期。例如,如果 trial_end 的值是 2020-02-29。再加上一年会给你 2021-02-29。为了使这一点正确,您必须决定如何处理这种情况(例如将其映射到 2021-02-28?)。


2还有一个免费的、开源的、仅包含标头的 C++20 部分预览版,它适用于 C++11/14/17:https://github.com/HowardHinnant/date

【讨论】:

    【解决方案2】:

    如果设置了年份,并且切换了日期,则必须返回 false,因为它是一个空间隔。

    bool switched = 0 < compareParial(start, end);
    if (first[0]==' ' && switched && (firstCompare <= 0) != (endCompare >= 0))
       return true;
    
    return false;
    

    【讨论】:

      猜你喜欢
      • 2018-07-26
      • 1970-01-01
      • 2012-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多