WinRT日历日期是否发生在同一天?

huangapple go评论67阅读模式
英文:

Do WinRT Calendar dates occur on the same day?

问题

我有2个winrt::Calendar,我知道我可以Compare它们,但这会将日期比较到(可能是)纳秒。

如何可靠地确定2个日期是否发生在相同的日期、相同的小时等历史事件中?我担心将小时/分钟/时间等归零是危险的,参考程序员对时间的错误观念

例如,2023年4月28日下午3:12 PDT2023年4月28日上午11:31 PDT应该比较相等,但2021年5月12日下午1:24 PDT2023年5月12日下午1:24 PDT不应该比较相等。

英文:

I have 2 winrt::Calendars, and I know I can Compare them, but that compares the dates down to (presumably) the nanosecond.

How can I robustly tell if 2 dates occur on the same day, or same hour, etc in history? I'm worried that zeroing out the hour/minute/time/etc is dangerous, after reading Falsehoods programmers believe about time.

eg April 28, 2023 at 3:12PM PDT and April 28, 2023 at 11:31AM PDT should compare equal, but May 12, 2021 at 1:24PM PDT and May 12, 2023 at 1:24PM PDT should not.

答案1

得分: 1

以下是翻译的内容:

虚假信息构成了一个很好的列表。我喜欢“我可以轻松地自己维护时区列表”的那一条。LOL。

尽管我为先前的回答做出了贡献,但我想分享一些额外的想法,而不是过于随机地回答。

我关键的想法是“上下文和情境很重要,需要充分理解”。特别是涉及所选解决方案的潜在边缘情况和限制。

我想要补充的一点是处理位置可能会产生影响。例如:如果日期/时间在本地处理还是在服务器上处理。对一些用户来说,我的服务器的午夜批处理可能是工作日的中间。

我倾向于推荐使用协调世界时(UTC),但你必须小心。如果你只使用时间的“日期”部分,然后将其转换为UTC,日期可能会发生变化!

你可能会问的一个问题是,你是否正在编程到一个明确的时间点,一个实际的时间戳。还是你打算以更为概念性的方式使用,比如日历日期。例如:美国于1776年7月4日宣布独立。人们会根据自己的参考框架来考虑那个日期。实际上,在英帝国的某些地方,那天下午的时间可能已经是7月5日了,但他们可能仍然会在历史考试中回答7月4日。

此外,要注意边缘情况。如果你有来自东部标准时间(EST)和纽芬兰标准时间(NST)的事件,“相同的小时”将是一个相对无意义的问题。

作为程序员,我们总是希望有人说“使用+运算符将两个数字相加”并且知道我们做得很好(当然,事情并不那么简单,它们是实数还是整数或者太大了)。

无论如何,日期和时间并不是我们可以说“按照这种方式做”就能完成的事情。开发者需要理解目标以及他们方法的限制。“谁最后写入了这个文件?”比“我的维基页面7月4日发生了什么?”要容易得多。

英文:

The falsehoods are a good list. I love the "I can easily maintain a timezone list myself" one. LOL.

Though I contributed to the previous answer, I wanted to share some additional thoughts without randomizeing that answer too much.

My key thoughts are "the context and scenario are important and need to be fully understood." Particularly with regard to potential edge cases and limitations of the chosen solution.

One item I'd add is that the processing location can have an impact. Eg: if the date/times are processed locally or on a server. My server's midnight batch process might be in the middle of the working day for some users.

I tend to recommend UTC, but you have to be careful. If you're only using the "date" part of a time, and then switch it to UTC, the date might change!

One question you might ask is if you're programming to an explicit moment in time, an actual timestamp. Or if you're intending a more conceptual use, like a calendar day. Eg: The US declared independence on July 4, 1776. People think of that date in their own frame of reference. It was in the afternoon sometime, so in actuality it was July 5th in some parts of the British Empire, yet they'd likely still answer July 4th on a history exam.

Also, be aware of the edge cases. "Same hour" is going to be a relatively meaningless question if you have events from EST and NST.

As programmers we always want someone to say "use the + operator to add two numbers" and know we're good (of course, that isn't even that simple, are they real or integers or too big).

Anyway, dates & times aren't one of those things where we can say "do it this way" and be done with it. The developer needs to understand what the goals are, and the limitations of their approach. "Who wrote to this file last?" is way easier than "what happened on July 4th for my wiki page?"

答案2

得分: 0

这是一个复杂的问题。

警告

正如问题中所提到的,时间很令人困惑。还有一些其他适用于所有这些解决方案的问题:

  • 您的用户是否期望“同一天”意味着“同一日历日期”?或者他们期望“前24小时”?

    • 例如,待办事项应用的用户可能希望“同一天”的任务意味着“同一日历日期”,但如果您的用户主要在夜班使用该应用会怎样呢?基于“同一日历日期”进行拆分可能会非常令人沮丧,因为任务可能在他们的夜班中途到期,即在午夜时分。
  • 您的日期是否依赖于时区?

    • 例如,待办事项应用的用户可能期望任务是基于明显的日历日期到期的(例如,如果这项任务“明天到期”,而我穿越国际日期变更线,该任务仍应“明天到期”)。但他们可能希望根据具体的“时刻”来安排任务(例如,穿越日期线会使任务今天到期/2天后到期)。
  • 时间中的瞬间很令人困惑(UTC仅追溯几十年,闰秒会增加更多混淆)。

  • 正在旅行的用户对“天”有不同的感知。

因此,这个问题的答案本质上充满了边缘情况。尽管如此,以下是我同事提出的一些解决方案:

选项1:使用 std::chrono 比较两个时间(仅适用于C++20)

bool IsSameDay(winrt::Windows::Globalization::Calendar const& a,
    winrt::Windows::Globalization::Calendar const& b)
{
    const aSys = winrt::clock::to_sys(a.GetDateTime());
    const bSys = winrt::clock::to_sys(b.GetDateTime());

    const auto ymdA = std::chrono::year_month_day(std::chrono::time_point_cast<std::chrono::days>(aSys));
    const auto ymdB = std::chrono::year_month_day(std::chrono::time_point_cast<std::chrono::days>(bSys));

    return ymdA == ymdB;
}

选项2:将时间点置零

void SetCalendarToDay(winrt::Windows::Globalization::Calendar& c)
{
    // 假定纳秒是日历中的最小刻度。
    c.Period(c.FirstPeriodInThisDay());
    c.Hour(c.FirstHourInThisPeriod());
    c.Minute(c.FirstMinuteInThisHour());
    c.Second(c.FirstSecondInThisMinute());
    c.Nanosecond(0);
}

bool IsSameDay(winrt::Windows::Globalization::Calendar const& a,
    winrt::Windows::Globalization::Calendar const& b)
{
    const a2 = a.Clone();
    const b2 = b.Clone();
    SetCalendarToDay(a2);
    SetCalendarToDay(b2);

    return a2.Compare(b2) == 0;
}

选项3:手动比较年、月和日

bool IsSameDay(winrt::Windows::Globalization::Calendar const& a,
    winrt::Windows::Globalization::Calendar const& b)
{
    auto dt1_tt = winrt::clock::to_time_t(a.GetDateTime());
    auto dt2_tt = winrt::clock::to_time_t(b.GetDateTime());

    // 转换为UTC时间。如果需要在本地时区 - 使用 *localtime(&dt*_tt);
    auto dt1_tt_tm = *gmtime(&dt1_tt);
    auto dt2_tt_tm = *gmtime(&dt2_tt);

    return dt1_tt_tm.tm_year == dt2_tt_tm.tm_year && dt1_tt_tm.tm_mday == dt2_tt_tm.tm_mday;
}

结论

“日历日期是否发生在同一天”是一个复杂的问题,因此很难完全回答。但这些答案应该有助于获得一个不错的结果。

英文:

This is a complicated question.

Warning

As mentioned in the question, time is confusing. Here are a couple other concerns that apply to all these solutions:

  • Do your users expect "same day" to mean "same calendar date"? Or do they expect "previous 24h"?

    • For example, users of a to-do app might expect tasks due on the "same day" to mean "same calendar date", but what if your users primarily use the app on a night shift? Splitting based on "same calendar date" might be really frustrating, as tasks might become overdue midway through their shift, at midnight.
  • Are your dates time-zone--dependent?

    • For example, users of a to-do app might expect tasks to be due on apparent calendar date (eg if this task is "due tomorrow" and I cross the international date line, the task should still be "due tomorrow"). But they might instead expect the task to be due based on a concrete "moment" (eg crossing the line would make the task due today/2 days from now).
  • Moments in time are confusing (UTC only goes back a few decades, and leap-seconds add further confusion).

  • Users who are traveling might have a different perception of "days" than the calendar offers.

So the answer to this question is inherently laden with edge-cases. That said, here are some solutions suggested by my colleagues:

Option 1: compare both times using std::chrono (C++20 only)

bool IsSameDay(winrt::Windows::Globalization::Calendar const& a,
    winrt::Windows::Globalization::Calendar const& b)
{
    const aSys = winrt::clock::to_sys(a.GetDateTime());
    const bSys = winrt::clock::to_sys(b.GetDateTime());

    const auto ymdA = std::chrono::year_month_day(std::chrono::time_point_cast<std::chrono::days>(aSys));
    const auto ymdB = std::chrono::year_month_day(std::chrono::time_point_cast<std::chrono::days>(bSys));

    return ymdA == ymdB;
}

Option 2: zero-out the timepoints, yup

void SetCalendarToDay(winrt::Windows::Globalization::Calendar& c)
{
    // Assumes nanoseconds are the smallest tick in the calendar.
    c.Period(c.FirstPeriodInThisDay());
    c.Hour(c.FirstHourInThisPeriod());
    c.Minute(c.FirstMinuteInThisHour());
    c.Second(c.FirstSecondInThisMinute());
    c.Nanosecond(0);
}

bool IsSameDay(winrt::Windows::Globalization::Calendar const& a,
    winrt::Windows::Globalization::Calendar const& b)
{
    const a2 = a.Clone();
    const b2 = b.Clone();
    SetCalendarToDay(a2);
    SetCalendarToDay(b2);

    return a2.Compare(b2) == 0;
}

Option 3: compare year, month, and day manually

bool IsSameDay(winrt::Windows::Globalization::Calendar const& a,
    winrt::Windows::Globalization::Calendar const& b)
{
    auto dt1_tt = winrt::clock::to_time_t(a.GetDateTime());
    auto dt2_tt = winrt::clock::to_time_t(b.GetDateTime());

    // Conversion done to UTC time. If needed in Local time zone - use *localtime(&dt*_tt);
    auto dt1_tt_tm = *gmtime(&dt1_tt);
    auto dt2_tt_tm = *gmtime(&dt2_tt);

    return dt1_tt_tm.tm_year == dt2_tt_tm.tm_year && dt1_tt_tm.tm_mday == dt2_tt_tm.tm_mday;
}

Conclusion

"Do calendar dates occur on the same day" is a complicated question, so it's hard to answer completely. But these answers should help get a decent result.

huangapple
  • 本文由 发表于 2023年4月11日 03:03:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/75979909.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定