将 UTC/GMT 时间转换为本地时间

本文关键字:时间 转换 GMT UTC | 更新日期: 2023-09-27 17:47:21

我们正在为Web服务客户端开发一个C#应用程序。这将在Windows XP PC上运行。

Web 服务返回的字段之一是"日期时间"字段。服务器返回一个 GMT 格式的字段,即末尾带有"Z"。

但是,我们发现 .NET 似乎进行了某种隐式转换,并且时间始终为 12 小时。

下面的代码示例在某种程度上解决了这个问题,因为 12 小时的差异已经消失,但它不允许新西兰夏令时。

CultureInfo ci = new CultureInfo("en-NZ");
string date = "Web service date".ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            

根据此日期网站:

UTC/GMT 偏移量

标准时区:UTC/GMT +12 小时
夏令时:+1 小时
当前时区偏移量:UTC/GMT +13 小时

我们如何调整额外的小时数?这可以通过编程方式完成,还是这是PC上的某种设置?

将 UTC/GMT 时间转换为本地时间

对于像 2012-09-19 01:27:30.000 这样的字符串,DateTime.Parse无法知道日期和时间来自哪个时区。

DateTime有一个 Kind 属性,该属性可以具有以下三个时区选项之一:

  • 未指定
  • 当地
  • 世界标准时间

注意如果您希望表示 UTC 或本地时区以外的日期/时间,则应使用 DateTimeOffset


因此,对于您问题中的代码:

DateTime convertedDate = DateTime.Parse(dateStr);
var kind = convertedDate.Kind; // will equal DateTimeKind.Unspecified

你说你知道它是什么类型,所以告诉它。

DateTime convertedDate = DateTime.SpecifyKind(
    DateTime.Parse(dateStr),
    DateTimeKind.Utc);
var kind = convertedDate.Kind; // will equal DateTimeKind.Utc

现在,一旦系统知道它的UTC时间,你只需调用ToLocalTime

DateTime dt = convertedDate.ToLocalTime();

这将为您提供所需的结果。

如果你

在.NET 3.5中,我会考虑使用System.TimeZoneInfo类。请参阅 http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx。这应该正确考虑夏令时的变化。

// Coordinated Universal Time string from 
// DateTime.Now.ToUniversalTime().ToString("u");
string date = "2009-02-25 16:13:00Z"; 
// Local .NET timeZone.
DateTime localDateTime = DateTime.Parse(date); 
DateTime utcDateTime = localDateTime.ToUniversalTime();
// ID from: 
// "HKEY_LOCAL_MACHINE'Software'Microsoft'Windows NT'CurrentVersion'Time Zone"
// See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.id.aspx
string nzTimeZoneKey = "New Zealand Standard Time";
TimeZoneInfo nzTimeZone = TimeZoneInfo.FindSystemTimeZoneById(nzTimeZoneKey);
DateTime nzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, nzTimeZone);
TimeZone.CurrentTimeZone.ToLocalTime(date);

默认情况下DateTime对象具有Unspecified Kind,出于ToLocalTime的目的,假定UTC

因此,要获取Unspecified DateTime对象的本地时间,您只需执行以下操作:

convertedDate.ToLocalTime();
不需要将

DateTimeKindUnspecified更改为UTCUnspecified被假定为UTC ToLocalTime:http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx

我知道

这是一个较老的问题,但我遇到了类似的情况,我想为未来的搜索者分享我的发现,可能包括我自己:)。

DateTime.Parse()可能很棘手 - 例如,请参阅此处。

如果DateTime来自 Web 服务或具有已知格式的其他源,则可能需要考虑类似

DateTime.ParseExact(dateString, 
                   "MM/dd/yyyy HH:mm:ss", 
                   CultureInfo.InvariantCulture, 
                   DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)

或者,更好的是,

DateTime.TryParseExact(...)

AssumeUniversal 标志告诉解析器日期/时间已经是 UTC;AssumeUniversalAdjustToUniversal 的组合告诉它不要将结果转换为"本地"时间,默认情况下它会尝试这样做。 (无论如何,我个人都尝试在业务/应用程序/服务层中专门处理UTC。 但是绕过到本地时间的转换也会加快速度 - 在我的测试中增加了 50% 或更多,见下文。

以下是我们之前所做的:

DateTime.Parse(dateString, new CultureInfo("en-US"))

我们已经分析了该应用程序,发现DateTime.Parse代表了CPU使用率的很大一部分。 (顺便说一下,CultureInfo构造函数并不是 CPU 使用率的重要贡献者。

因此,我设置了一个控制台应用程序,以多种方式解析日期/时间字符串 10000 次。 底线:
Parse() 10 秒
ParseExact()(转换为本地) 20-45 ms
ParseExact()(不转换为本地) 10-15 ms
...是的,Parse()的结果以为单位,而其他结果以毫秒为单位。

我只想补充一个一般的注意事项。

如果您所做的只是从计算机的内部时钟中获取当前时间以将日期/时间放在显示屏或报告上,那么一切都很好。 但是,如果您要保存日期/时间信息以供以后参考或正在计算日期/时间,请注意!

假设您确定一艘游轮于 2007 年 12 月 20 日 15:00 UTC 抵达檀香山。 你想知道那是当地时间。
1. 可能至少有三个"当地人"参与其中。 本地可能意味着檀香山,也可能意味着您的计算机所在的位置,或者可能意味着您的客户所在的位置。
2.如果使用内置函数进行转换,则可能是错误的。 这是因为夏令时目前(可能)在您的计算机上生效,但在 12 月未生效。 但是Windows不知道这一点...它只有一个标志来确定夏令时当前是否有效。 如果它目前有效,那么它甚至会很乐意在 12 月的日期增加一个小时。
3. 夏令时在各个政治分区的实施方式不同(或根本不实施)。 不要以为仅仅因为您所在的国家/地区在特定日期发生变化,其他国家/地区也会发生变化。

@TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)

不要忘记,如果您已经有一个 DateTime 对象并且不确定它是 UTC 还是本地,那么直接在对象上使用方法很容易:

DateTime convertedDate = DateTime.Parse(date);
DateTime localDate = convertedDate.ToLocalTime();

我们如何调整额外的小时数?

除非指定,否则 .net 将使用本地电脑设置。我会读到:http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx

从外观上看,代码可能如下所示:

DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year );

如上所述,请仔细检查您的服务器所在的时区设置。网上有关于如何安全地影响 IIS 中的更改的文章。

在回答Dana的建议时:

代码示例现在如下所示:

string date = "Web service date"..ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate);

最初的日期是20/08/08;那种是UTC。

"

convertedDate"和"dt"都是相同的:

08-08-21 10:00:26;那种是本地的

我遇到了一个问题,它位于一个被推送到线路(网络服务到客户端)的数据集中,它会自动更改,因为 DataColumn 的 DateType 字段设置为本地。确保检查 DateType 是什么,如果你推送数据集。

如果不希望更改,请将其设置为"未指定"

我遇到了这个问题,因为我通过推特 API(状态上的created_at字段)返回的 UTC 日期有问题;我需要将它们转换为日期时间。此页面上答案中的任何答案/代码示例都不足以阻止我收到"字符串未被识别为有效的日期时间"错误(但这是我最接近在 SO 上找到正确答案)

在这里发布此链接以防这对其他人有帮助 - 我需要的答案在这篇博文中找到:http://www.wduffy.co.uk/blog/parsing-date-when-aspnets-datetimeparse-don't-work/- 基本上使用DateTime.ParseExact 与格式字符串而不是DateTime.Parse

此代码块使用通用时间转换当前 DateTime 对象,然后将其转换回本地 DateTime。对我来说效果很好,希望它有所帮助!

CreatedDate.ToUniversalTime().ToLocalTime();

如果您的日期时间字符串被视为在 UTC 时区之外,则可以使用 DateTimeOffset 来仅设置时区信息,而无需更改字符串中的时间,如下所示:

// Input datetime string
string datetimeString = "2023-07-11 12:42:56";
// Specify the timezone as "Europe/Prague"
TimeZoneInfo pragueTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Prague");
// Parse the datetime string with the specified format
DateTime datetimeObj = DateTime.ParseExact(datetimeString, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
// Create a DateTimeOffset with the parsed datetime and the "Europe/Prague" timezone
DateTimeOffset pragueDateTimeOffset = new DateTimeOffset(datetimeObj, pragueTimeZone.GetUtcOffset(datetimeObj));

您还可以像通常使用 DateTime 实例一样设置日期时间偏移量字符串的格式。

// Print the resulting DateTimeOffset object
Console.WriteLine(pragueDateTimeOffset.ToString("O"));

我在ASP.NET Core 7.0 Web API中遇到了类似的情况,我按如下方式解决了它,

  1. 安装 NodaTime Nuget。
  2. 使用 CultureInfo.CurrentCulture.Name[^2..] 从用户请求中获取国家/地区代码。就我而言,这是IN,这将返回Asia/Kolkatta.
  3. 如果它not null则使用其他ZoneId获取TimeZoneInfo DateTime将导出为UTC
  4. 现在我们可以使用TimeZoneInfo.ConvertTimeFromUtc(UTCDateTime, TimeZoneInfo)UTC DateTime转换为用户的Local DateTime
var timeZone = TzdbDateTimeZoneSource.Default.ZoneLocations!.FirstOrDefault(x => x.CountryCode == CultureInfo.CurrentCulture.Name[^2..]);
if (timeZone is not null) 
{
    var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone.ZoneId);
    foreach (var order in orders)
    {
        order.OrderDate = TimeZoneInfo.ConvertTimeFromUtc(order.OrderDate.DateTime, timeZoneInfo);
    }
}

请注意,我采用的是FirstOrDefault()这意味着对于拥有单一timezone的国家来说,这就足够了。但是对于具有多个时区的国家/地区,我们需要将用户timezone详细信息存储在数据库中以进行转换。