我们的ASP.NET MVC Web应用程序具有向用户发送定期通知电子邮件的功能。我们将添加一项功能,用户可以选择特定时间来接收他们的电子邮件(即“我希望每天下午5点收到一封电子邮件”)。
我计划使用SQL time
类型存储所选时间。我们将有一个每分钟运行一次的后台进程,并寻找需要通过电子邮件发送的用户。该过程使用LINQ-to-Entities查询来查找所有匹配的用户。我打算写一个查询,基本上找到每日电子邮件时间小于当前UTC时间的所有用户 - 这是我遇到问题的地方。每个用户都可以属于不同的时区 - 我们将他们的时区存储为数据库中的IANA时区标识符。我们在项目中使用NodaTime将存储在我们数据库中的任何日期转换为UTC到用户的时区。为了继续我尝试这样做的方式,我需要一些我可以放在LINQ-to-entities查询中的东西,所以我不能真正使用任何NodaTime函数来转换时间并弄清楚当前时间是日期是在用户指定的时间之后,在他们的时区。我不能只在UTC时间内存储他们指定的时间,如果我这样做,那么每当DST出现时它都会被关闭一小时。
我现在能想出的最佳解决方案是“缓存”我们数据库中每个时区的当前UTC偏移量。所以我可以使用一些代码来使用NodaTime来获取每个时区的当前偏移量,并将其保存到存储TimeZoneID和UTCHourOffset的数据库中的表中。我可以有一个常规的过程来运行此代码,并确保缓存的小时偏移保持最新。然后在我的查询中,我可以通过查找缓存的UTC偏移并将其添加到他们的时间来转换每个用户的指定时间,以确定是否应该向他们发送电子邮件。
有没有更简单的方法来做我想做的事情?
答案 0 :(得分:5)
很高兴看到你仔细思考。实际上,当您尝试在用户本地时区安排定期事件时,您不能只存储UTC时间。 (不要让任何人告诉你“总是UTC” - 这是一个常见的误解,你确实有一个当地时间的有效案例。)我已经写了几次,最全面的答案是{{3} }。
您应该考虑将下一个运行时间预先计算为精确的UTC时间戳(日期和时间),而不是缓存偏移量。然后,您可以查询该信息以了解何时发送电子邮件。或者,考虑使用已经处理此问题的时区感知作业调度程序。 here是一个很好的。
您是正确的,目前无法使用EF的Noda Time类型。这是由于EF Quartz.NET长期缺乏功能。好消息是EF Core 2.1最近完成了开发工作(仍处于开发阶段)。在那个版本之后,某人(可能是我)必须应用该功能来创建EF-NodaTime支持包。在此期间,将SQL time
类型视为.Net TimeSpan
,并使用tracked in this issue公开NodaTime类型。您必须针对TimeSpan
编写LINQ查询。如果你转移到EF Core 2.1,你可以稍后重构。