将时间,月份或年份添加到与时区无关的日期/时间?

时间:2013-08-04 09:41:19

标签: c# date time timezone

我的软件使用当地时间显示日期/时间,然后以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的结果总是一样吗?或者时区会影响结果吗?

2 个答案:

答案 0 :(得分:8)

答案可能让您大吃一惊,但它 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或使用DateTime种类Local的{​​{1}}。如果这样做,那么您将介绍服务器的时区 - 而不是用户的时区。如果您的用户不在同一时区,或者您曾在其他地方部署过您的应用程序,那么您将遇到问题。
  • 如果您想避免这些问题,请查看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

答案 1 :(得分:0)

  

#1和#2的结果总是一样吗?

我的钱是。我没有看到他们为什么不一样的原因。

来自DateTime.ToLocalTime

  

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

     

转换返回的值是Date属性的DateTime   总是返回本地。因此,即使返回有效结果   ToLocalTime重复应用于到相同的DateTime

来自DateTime.ToUniversalTime

  

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

由于AddDaysAddYearsAddMonths基于TimeZone不会发生变化,因此不存在任何问题。