在C#中将时间数据转换为本地时间
Convert time data to local time in C#
我已经阅读了一些关于类似主题的帖子,但似乎没有任何内容可以回答这个问题。我的数据库有关于某个时间的以下信息
- 星期几(0-6 之间的数字)
- 时间(用户当地时间午夜后的毫秒数)
- UTC 偏移量(与 UTC 不同的小时数)
- DST Observed(布尔值,说明是否在该时区观察到 DST)
此数据表示营业时间。所以每天都有时间。我想在用户当地时间显示那个时间,假设每一天都在未来
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);
我已经阅读了一些关于类似主题的帖子,但似乎没有任何内容可以回答这个问题。我的数据库有关于某个时间的以下信息
- 星期几(0-6 之间的数字)
- 时间(用户当地时间午夜后的毫秒数)
- UTC 偏移量(与 UTC 不同的小时数)
- DST Observed(布尔值,说明是否在该时区观察到 DST)
此数据表示营业时间。所以每天都有时间。我想在用户当地时间显示那个时间,假设每一天都在未来
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);