在C#中将时间数据转换为本地时间

时间:2018-04-26 10:49:22

标签: c# datetime datetimeoffset

我已经阅读了一些类似主题的帖子,但似乎没有回答这个问题。我的数据库有以下有关时间的信息

  • 星期几(0-6之间的数字)
  • 时间(用户当地时间午夜以来的毫秒数)
  • UTC偏移量(与UTC不同的小时数)
  • 观察到DST(布尔说明是否在该时区内观察到DST)

此数据代表营业时间。所以每天都有时间。我想在用户当地时间显示该时间,假设每天都在将来

int dayOffset = availability.Day - (int)now.DayOfWeek
if (dayOffset < 0)
    dayOffset += 7;

当一个时区可能正在观察DST时,我真的很难绕过时区和处理,而另一个时区可能会观察DST但尚未观察到。 我目前的主要问题是我我需要为非本地时间创建一个DateTimeOffset对象,但我不知道该怎么做,因为我不知道是否DST是否生效。 我希望我能说清楚自己。使用日期和时间真的是一种令人费解的体验!

4 个答案:

答案 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进行显示。