【问题标题】:Are adding weeks, months or years to a date/time independent from time zone?是否将几周、几个月或几年添加到独立于时区的日期/时间?
【发布时间】:2013-08-05 04:23:54
【问题描述】:

我的软件使用本地时间显示日期/时间,然后以 UTC 将其发送到服务器。在服务器端,我想在这个日期/时间添加月、年、周、天等。但是,问题是,如果我将此类方法与 UTC 日期/时间一起使用,然后将其转换回本地时间,结果是否总是相同的,就好像我直接将这些方法与本地时间一起使用?

这是一个 C# 示例:

// #1
var utc = DateTime.Now.ToUtcTime();
utc = utc.AddWeeks(2); // or AddDays, AddYears, AddMonths...
var localtime = utc.ToLocalTime();

// #2
var localtime = DateTime.Now;
localtime = localtime.AddWeeks(2); // or AddDays, AddYears, AddMonths...

#1 和#2 中的结果是否总是相同的?还是时区会影响结果?

【问题讨论】:

  • 你是如何发送到服务器的?哪种格式?您希望达到secondsmsticks 的精度是多少?
  • 查看Noda Time。减轻很多约会时间的痛苦。

标签: c# date time timezone


【解决方案1】:

答案可能会让您感到惊讶,但它是 NO。您不能添加天、周、月或年而不影响结果的时区。

原因是并非所有当地日子都有 24 小时。根据时区、该区域的规则以及 DST 是否在相关期间转换,某些“天”可能有 23、23.5、24、24.5 或 25 小时。 (如果您想精确,那么请改用“标准天”一词来表示您的意思是 24 小时。)

例如,首先将您的计算机设置为美国夏令时更改的时区之一,例如太平洋时间或东部时间。然后运行这些示例:

这一篇涵盖了 2013 年的“春天”过渡:

DateTime local1 = new DateTime(2013, 3, 10, 0, 0, 0, DateTimeKind.Local);
DateTime local2 = local1.AddDays(1);

DateTime utc1 = local1.ToUniversalTime();
DateTime utc2 = utc1.AddDays(1);
DateTime local3 = utc2.ToLocalTime();

Debug.WriteLine(local2); //  3/11/2013 12:00:00 AM
Debug.WriteLine(local3); //  3/11/2013 1:00:00 AM

这一篇涵盖了 2013 年的“后备”过渡:

DateTime local1 = new DateTime(2013, 11, 3, 0, 0, 0, DateTimeKind.Local);
DateTime local2 = local1.AddDays(1);

DateTime utc1 = local1.ToUniversalTime();
DateTime utc2 = utc1.AddDays(1);
DateTime local3 = utc2.ToLocalTime();

Debug.WriteLine(local2); //  11/4/2013 12:00:00 AM
Debug.WriteLine(local3); //  11/3/2013 11:00:00 PM

正如您在两个示例中看到的那样 - 结果是一个小时的休息时间,一个方向或另一个方向。

其他几点:

  • 没有AddWeeks 方法。乘以 7 并添加天数。
  • 没有ToUtcTime 方法。我想你在找ToUniversalTime
  • 不要打电话给DateTime.Now.ToUniversalTime()。这是多余的,因为在.Now 内部它必须采用 UTC 时间并转换为本地时间。请改用DateTime.UtcNow
  • 如果此代码在服务器上运行,则不应调用 .Now.ToLocalTime 或使用具有 Local 类型的 DateTime。如果这样做,那么您将介绍 服务器 的时区 - 而不是用户的时区。如果您的用户不在同一个时区,或者您曾经将应用程序部署到其他地方,您就会遇到问题。
  • 如果您想避免此类问题,请查看NodaTime。它的 API 可以防止你犯常见的错误。

你应该这样做:

// on the client
DateTime local = new DateTime(2013, 3, 10, 0, 0, 0, DateTimeKind.Local);
DateTime utc = local.ToUniversalTime();
string zoneId = TimeZoneInfo.Local.Id;

// send both utc time and zone to the server
// ...

// on the server
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(zoneId);
DateTime theirTime = TimeZoneInfo.ConvertTimeFromUtc(utc, tzi);
DateTime newDate = theirTime.AddDays(1);
Debug.WriteLine(newDate); //   3/11/2013 12:00:00 AM

为了更好地衡量,如果您改用 Noda Time,它的外观如下:

// on the client
LocalDateTime local = new LocalDateTime(2013, 3, 10, 0, 0, 0);
DateTimeZone zone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
ZonedDateTime zdt = local.InZoneStrictly(zone);

// send zdt to server
// ...

// on the server
LocalDateTime newDate = zdt.LocalDateTime.PlusDays(1);
Debug.WriteLine(newDate); // 3/11/2013 12:00:00 AM

【讨论】:

【解决方案2】:

#1 和#2 中的结果会始终相同吗?

我的钱是为了YES。我看不出它们有什么不同的原因。

来自DateTime.ToLocalTime

本地时间等于协调世界时 (UTC) 时间 加上 UTC 偏移量。

转换返回的值是一个 DateTime,其 Kind 属性 总是返回本地。因此,即使返回有效结果 ToLocalTime 被重复应用于同一个 DateTime

来自DateTime.ToUniversalTime

协调世界时 (UTC) 等于当地时间 减去 UTC 偏移量。

由于AddDaysAddYearsAddMonths不会根据时区变化,所以不会有问题。

【讨论】:

  • 如果您跨越 DST 边界,它们将不一样。并非所有当地日子都是 24 小时。看我的回答。
猜你喜欢
  • 2017-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-31
  • 2011-09-06
相关资源
最近更新 更多