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

Convert time data to local time in C#

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

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

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

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

您应该在 C# 中利用 DateTime.ToLocalTime()TimeZoneInfo.ConvertTimeToUtc() - 请参阅 https://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime(v=vs.110).aspx

如果您只想存储周一至周日的营业时间,没问题。有一个简单的数据 table 来仅描述每天的时间(0 = 星期日到 7 = 星期六——这是 .Net 的 DayOfWeek 枚举)。您的查找 table 可能如下所示:

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 可用于当天关闭——即没有开放时间。)

当需要向任何其他用户显示您的时间时,用户必须在您向本地用户显示信息时即时创建您的 UTC 时间。

Conyc 提供了一种方法。我的方法使用简单的 date/time 字符串。要使用我的方法,只需将每天的时间值存储在您的数据库中。然后您可以查看任何给定日期的开放时间。要在任何区域设置中为另一个用户表达该时间,请使用此代码将您的时间转换为 UTC(您可以将“08:00:00 AM”字符串值替换为您在查找开放时间后填充的字符串变量数据库):

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()

试试Quartz.NET

它实现了 CronExpressions 的评估,甚至触发在给定时间触发事件。它可以评估下一次事件发生的时间。这可能会帮助您计算开放时间。

另外,看看 cronmaker website:您可以在那里了解 CronExpressions 的全部潜力。

CronExpressionDescriptor 是一个 DotNET 库,用于将 CronExpressions 转换为人类可读的字符串。

我还没有尝试过的另一个库是 [HangFire]。(https://www.hangfire.io/)

在此 forum post 中,您可以找到一些关于 HangFire 如何使用夏令时在本地时区实现 RecurringJobs 评估的讨论,我相信这是您正在寻找的解决方案。

对另一个答案的评论使问题更清楚了。

因此,首先也是最重要的是,请在您的数据库中仅存储 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 进行显示。

如其他答案所示,跨时区处理 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);