Every Day Every Week Every 2 Weeks Every 3 Weeks Every Month Every 2 Months ...
这样做的正确方法是什么?它应该可以正常使用不同的时区和日节省时间设置.我想我应该在本地DateTime中添加几天/几周/月,然后将其转换为UTC.但我不确定这一点.如果我添加几天会发生什么,这将是我们需要向前调整我们的时钟一小时的时间.在这种情况下,此时间不存在.
下面是我写的代码,但我不确定它在每种情况下都能正常工作:
private static List<DateTime> FindOccurrences(DateTime localStart,Repeat repeat,int occurrences) { var result = new List<DateTime> { localStart }; switch (repeat) { case Repeat.Daily: for (int i = 1; i <= occurrences; i++) result.Add(localStart.AddDays(i)); break; case Repeat.Every2Weeks: for (int i = 1; i <= occurrences; i++) result.Add(localStart.AddDays((7 * 2) * i)); break; ... } return result; } public List<Event> CreateRepeating(string timeZone,int repeatEnds,DateTime localStart,int eventDuration) { var events = new List<Event>(); var occurrences = FindOccurrences(localStart,repeat,repeatEnds); foreach (var occurrence in occurrences) { var item = new Event(); item.Start = occurrence.ToUtcTime(timeZone); item.End = occurrence.ToUtcTime(timeZone).AddMinutes(eventDuration); events.Add(item); } return events; }
PS:
所有日期都以UTC格式存储在数据库中.
解决方法
我担心这个主题过于宽泛,无法为您提供准确的代码.详细信息将特定于您的应用程序.但这里有一些提示.
一般来说:
>仅将UTC用于发生单个事件实例的预计时刻.
>以当地时间存储实际活动.同时存储时区ID.
>不要存储时区偏移量.应该单独查找每次出现的情况.
>将事件即将发生的事件计划为UTC,以便您知道何时基于事件执行操作(或者对您的场景有意义的事情).
>决定夏令时,当一个事件发生在弹跳间隙或后退重叠时要做什么.您的需求可能会有所不同,但一个共同的策略是跳过春天的差距,并选择秋季的第一次出现.如果您不确定我的意思,请参阅the dst tag wiki.
>仔细考虑如何处理月末附近的日期.并非所有月份都有相同的天数,日历数学很难. dt.AddMonths(1).AddMonths(1)不一定与dt.AddMonths(2)相同.
>随时掌握时区数据更新.没人能预测未来,世界各国政府都喜欢改变一切!
>您需要保留计划的原始本地时间值,以便可以重新设置事件的UTC值.您应该定期执行此操作,或者应用时区更新时(如果您手动跟踪它们). The timezone tag wiki详细介绍了不同时区数据库及其更新方式.
>考虑使用Noda Time和IANA/TZDB时区.与Microsoft提供的内置类型和时区相比,它们更适合此类工作.
>小心避免使用本地时区.您不应该调用DateTime.Now,ToLocalTime或ToUniversalTime.请记住,本地时区基于运行代码的计算机,不应影响代码的行为.阅读更多The Case Against DateTime.Now.
>如果您正在完成所有这些工作,那么您应该查看一个预先解决的解决方案,例如Quartz.NET.它是免费的,开源的,功能强大的,并涵盖了很多边缘案例你可能没有想过.