我已经阅读了一些类似主题的帖子,但似乎没有回答这个问题。我的数据库有以下有关时间的信息
此数据代表营业时间。所以每天都有时间。我想在用户当地时间显示该时间,假设每天都在将来
int dayOffset = availability.Day - (int)now.DayOfWeek
if (dayOffset < 0)
dayOffset += 7;
当一个时区可能正在观察DST时,我真的很难绕过时区和处理,而另一个时区可能会观察DST但尚未观察到。
我目前的主要问题是我想我需要为非本地时间创建一个DateTimeOffset
对象,但我不知道该怎么做,因为我不知道是否DST是否生效。
我希望我能说清楚自己。使用日期和时间真的是一种令人费解的体验!
答案 0 :(得分:2)
如其他答案所示,跨时区处理DateTime
的常用解决方案是存储UTC时间。
但是,考虑到您没有在特定日期引用绝对时间,而是指在特定时区内无限天数的时间;将时间存储为UTC时间不再有意义,因为由于DST,UTC时间(即使我们丢弃日期)将根据日期而有所不同。
存储时间的最佳方式与您已经完成的工作非常接近。
您的问题是,您当前存储的时区信息不明确,因为它不是指特定时区,而是指属性时区。
要解决此问题,只需存储时区标识符,而不是UTC偏移量和DST布尔值。
现在我们可以构建DateTime
对象并使用TimeZoneInfo class将其转换为任何时区:
int dayOffset = availability.Day - (int)DateTime.Today.DayOfWeek;
if (dayOffset < 0)
{
dayOffset += 7;
}
var openingHourStart = DateTime
.SpecifyKind(DateTime.Today, DateTimeKind.Unspecified)
.AddDays(dayOffset)
.AddMilliseconds(availability.Time);
var sourceTimeZone = TimeZoneInfo.FindSystemTimeZoneById(availability.TimeZoneId);
var userTimeZone = TimeZoneInfo.Local;
var convertedOpeningHourStart = TimeZoneInfo.ConvertTime(openingHourStart,
sourceTimeZone,
userTimeZone);
答案 1 :(得分:0)
您应该在C#中利用DateTime.ToLocalTime()
和TimeZoneInfo.ConvertTimeToUtc()
- 请参阅https://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime(v=vs.110).aspx。
如果您只想存储从周一到周日开放的时间,那很好。有一个简单的数据表来描述每天的时间(0 =星期日到7 =星期六 - 这是.Net&#39; s DayOfWeek
枚举)。您的查找表可能如下所示:
0 null
1 08:00:00
2 08:00:00
3 08:00:00
4 08:30:00
5 08:30:00
6 10:30:00
(使用适合您的任何数据类型 - 例如,SQL Server 2008+具有TIME数据类型.Null可以在当天用于Closed - 即,没有开放时间。)
当需要向任何其他用户显示您的时间时,使用必须在向本地用户显示信息时即时创建UTC时间。
Conyc提供了一种方法。我的方法使用简单的日期/时间字符串。要使用我的方法,只需在数据库中存储每天的时间值。然后你可以查看任何一天的开放时间。要在任何区域设置中为其他用户表达该时间,请使用此代码将您的时间转换为UTC(您可以使用在查找后填充的字符串变量替换&#34; 08:00:00 AM&#34;字符串值数据库中的开放时间):
var StoreOpenTimeInUtc = TimeZoneInfo.ConvertTimeToUtc(Convert.ToDateTime("08:00:00 AM"));
要在数据库中查找将来特定日期的开放时间,您需要将日期与时间值连接起来,如下所示:
var StoreOpenTimeInUtc = TimeZoneInfo.ConvertTimeToUtc(Convert.ToDateTime("04/28/2018 08:00:00 AM"));
一旦有了准确的StoreOpenTimeInUtc
变量,就可以将其作为UTC值用于其他地球上任何其他地方的机器上。要将该UTC值转换为其本地时间,请使用.NET ToLocalTime()方法:
var OpenTimeForLocalUser = StoreOpenTimeInUtc.ToLocalTime();
请注意,此方法要求您仅存储如上所示的打开时间。您不必担心日期,UTC的本地偏移或其他任何问题。只需使用ConvertTimeToUtc()
和ToLocalTime()
,如图所示。
答案 2 :(得分:0)
尝试Quartz.NET。
它实现了对CronExpressions的评估,甚至触发在给定时间触发事件。它可以在下次事件发生时进行评估。这可以帮助您计算开放时间。
另外,看看cronmaker website:你可以在那里了解CronExpressions的全部潜力。
CronExpressionDescriptor是一个DotNET库,用于将CronExpressions转换为人类可读的字符串。
我尚未尝试过的另一个图书馆是[HangFire]。(https://www.hangfire.io/)
在这个forum post中,您可以找到一些关于HangFire如何使用DST在本地时区实施RecurringJobs评估的讨论,我相信这是您正在寻找的解决方案。
答案 3 :(得分:0)
对另一个答案的评论使问题更加明确。
因此,首先,只在您的数据库中存储UTC。真。
现在,由于您对实际日期不感兴趣,因为您要存储每周重复的工作时间表,只有在您想要出现您的时间时,日期才会变得相关 - 当您放置它们时在你的数据库中。
因此,让我们首先看看如何正确地将时间投入数据库。我假设用户将在他们自己的语言环境中输入时间。
确保首先创建一个(本地化的)DateTime
,其中包含当前日期和给定时间(来自用户),并将其转换为UTC DateTime
(你可以保持当前日期,没关系):
var utcDateTime = DateTime.Now.Date
.AddHours(userHours)
.AddMinutes(userMinutes)
.ToUniversalTime();
现在,当您向用户展示这些时间时,只需走另一条路:
var userDateTime = DateTime.Now.Date
.AddHours(utcDateTime.Hour)
.AddMinutes(utcDateTime.Minute)
.ToLocalTime();
然后,您可以使用userDateTime.Hour
和.Minute
进行显示。