【问题标题】:Convert time data to local time in C#在 C# 中将时间数据转换为本地时间
【发布时间】:2018-04-26 10:49:22
【问题描述】:

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

  • 星期几(0-6 之间的数字)
  • 时间(从用户当地时间午夜开始的毫秒数)
  • UTC 偏移量(与 UTC 不同的小时数)
  • DST Observed(布尔值表示是否在该时区观察到 DST)

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

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

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

【问题讨论】:

  • 你已经尝试过 Quartz.NET 了吗?它有 ChronExpressions,可以帮助您计算开放时间。另外,看看这个链接:cronmaker.com
  • 我看到的主要问题是您只有关于时区是否使用 DST 的信息,但没有关于它何时处于活动状态的信息。因此,您将无法知道是否应该使用 DST。
  • @Conyc:如果 OP 在配置了客户端本地时区的操作系统上运行其系统,他可以轻松正确地将 UTC 时间转换为本地时间。
  • @Conyc 如果我假设“星期五”的时间是从现在起的下一个星期五,那么我有一个日期,我可以肯定知道 DST 是否会在该日期生效
  • 问题不在于您不知道夏令时是否会在您希望显示时间的当地时间生效。问题是原始时间数据存储在本地没有关于 DST 何时生效的信息。您必须记住,不同的国家/地区在不同的时间更改和更改夏令时。

标签: c# datetime datetimeoffset


【解决方案1】:

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

【讨论】:

  • 如果我将 C# TimeZone ID 存储到数据库中,那么这个解决方案可以完美运行。感谢您的帮助和耐心:)
【解决方案2】:

试试Quartz.NET

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

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

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

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

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

【讨论】:

  • 您能否解释一下在给定时间触发事件与 OP 问题有何关联?
  • @Jazimov:我在解释图书馆的潜力。绝对值得一个机会,我已经用这个漂亮的库实现了一个可靠的系统,它与 OP 的要求相似。
  • @Jazimov:本地时间偏移只是 OP 的日期时间问题之一。
【解决方案3】:

对另一个答案的评论使问题更加清晰。

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

【讨论】:

    【解决方案4】:

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

    如果您只想存储周一到周日的营业时间,没问题。有一个简单的数据表来描述每天的时间(0 = 周日到 7 = 周六——这是.Net 的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 可用于当天关闭 - 即没有开放时间。)

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

    Conyc 提供了一种方法。我的方法使用简单的日期/时间字符串。要使用我的方法,只需将每天的时间值存储在您的数据库中。然后,您可以查找任何一天的开放时间。要在任何语言环境中为其他用户表示该时间,请使用此代码将您的时间转换为 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()

    【讨论】:

    • 但这不起作用,不是吗?我首先需要在数据库中存储的时间的时区构造一个 DateTime 对象。我怎样才能从某个时间和一个偏移量中做到这一点,该偏移量可能会或可能不会在特定日期观察 DST?
    • 这是我的系统,是的,所以可以更改,但我无法存储日期,因为它们是一周中每一天的时间。完整的约会没有意义。例如,我在星期一的上午 9 点到下午 5 点工作。无论夏令时如何,我在本地工作的时间相同,但其他用户对我的时间的看法会有所不同,具体取决于他们是否遵守夏令时
    • 我明白了,但是星期一的时间对于所有星期一都是相同的,所以当我需要显示下一个星期一的时间时,日期只在客户端相关。另一种方法是我在数据库中存储一年中每一天的时间。
    • @Jazimov 当您需要表示相同的本地时间而与 DST 是否有效无关时,存储 UTC 时间不起作用(这将导致两个不同的 UTC 时间)。英国上午 9 点在 DST 未生效时为 09:00 UTC,但英国上午 9 点在 DST is 生效时为 10:00 UTC。您不能存储一个 UTC 时间并让它同时代表两者。
    • @Jazimov 但是您的回答说“关键是将所有时间存储为 UTC 时间”,这使得您似乎在建议作者应该将时间存储为 UTC 时间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-07
    • 2013-10-29
    • 2016-01-14
    • 2013-01-01
    • 2018-07-27
    • 2021-07-28
    相关资源
    最近更新 更多