将DateTime转换为UTC时遇到问题

时间:2009-08-30 18:21:13

标签: c# .net asp.net asp.net-mvc

我将所有日期都以UTC格式存储在我的数据库中。我问用户他们的时区,我想使用他们的时区加上我猜测的服务器时间来计算他们的UTC。

有了这个,我想搜索一下,看看数据库中使用新转换的UTC日期的范围。

但我总是得到这个例外。

System.ArgumentException was unhandled by user code  
Message="The conversion could not be completed because the   
supplied DateTime did not have the Kind property set correctly.  
For example, when the Kind property is DateTimeKind.Local,   
the source time zone must be TimeZoneInfo.Local.  
Parameter name: sourceTimeZone"

我不知道为什么我会这样做。

我试过两种方式

 TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(id);
 // I also tried DateTime.UtcNow
 DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
 var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone );

这失败了所以我累了

 DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
 var utc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(now, 
                                           ZoneId, TimeZoneInfo.Utc.Id);

这也失败了同样的错误。我做错了什么?

编辑可以吗?

 DateTime localServerTime = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
 TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById(id);

 var usersTime = TimeZoneInfo.ConvertTime(localServerTime, info);

 var utc = TimeZoneInfo.ConvertTimeToUtc(usersTime, userInfo);

编辑2 @ Jon Skeet

雅我只是想着我甚至不需要做这一切。时间的东西现在让我困惑,所以这就是为什么帖子可能不如它应该的那样清晰。我永远都不知道DateTime.Now到底是什么(我试图将我的时区更改为另一个时区,并且它一直在我的当地时间。)

这就是我想要的东西。用户来到网站添加一些警报,它现在保存为utc(在它之前是DateTime.Now然后有人建议存储所有UTC)。

因此,在用户访问我的网站之前,根据我的托管服务器的位置,它可能就像第二天一样。因此,如果警报据说是在8月30日(他们的时间)显示,但是服务器的时差可能会在8月29日到来,并会显示警报。

所以我想打击那个。所以现在我不确定我是否应该存储他们的当地时间然后使用这个偏移的东西?或者只存储UTC时间。仅仅存储UTC时间仍然可能是错误的,因为用户仍然可能会在当地时间考虑并且我不确定UTC是如何工作的,它仍然可能最终会产生时间差异。

EDIT3

 var info = TimeZoneInfo.FindSystemTimeZoneById(id)

 DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(DataBaseUTCDate,
                                             TimeZoneInfo.Utc, info);

5 个答案:

答案 0 :(得分:94)

您需要将Kind设置为Unspecified,如下所示:

DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified);
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone);

DateTimeKind.Local表示当地时区,而不是任何其他时区。这就是你收到错误的原因。

答案 1 :(得分:38)

DateTime结构仅支持两个时区:

  • 计算机正在运行的本地时区。
  • 和UTC。

查看DateTimeOffset结构。

var info = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");

DateTimeOffset localServerTime = DateTimeOffset.Now;

DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(localServerTime, info);

DateTimeOffset utc = localServerTime.ToUniversalTime();

Console.WriteLine("Local Time:  {0}", localServerTime);
Console.WriteLine("User's Time: {0}", usersTime);
Console.WriteLine("UTC:         {0}", utc);

输出:

Local Time:  30.08.2009 20:48:17 +02:00
User's Time: 31.08.2009 03:48:17 +09:00
UTC:         30.08.2009 18:48:17 +00:00

答案 2 :(得分:7)

正如dtb所说,如果你想存储具有特定时区的日期/时间,你应该使用DateTimeOffset

然而,从你的帖子中你根本不清楚你真的需要。您只使用DateTime.Now提供示例,并且您说猜测您正在使用服务器时间。你几点想要什么时间?如果您只想要UTC中的当前时间,请使用DateTime.UtcNowDateTimeOffset.UtcNow。你不需要知道时区就知道当前的UTC时间,正是因为它是通用的。

如果您以其他方式从用户那里获得日期/时间,请提供更多信息 - 这样我们就能够找到您需要做的事情。否则我们只是在猜测。

答案 3 :(得分:7)

其他人的答案似乎过于复杂。我有一个特定的要求,这对我来说很好:

void Main()
{
    var startDate = DateTime.Today;
    var StartDateUtc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.SpecifyKind(startDate.Date, DateTimeKind.Unspecified), "Eastern Standard Time", "UTC");
    startDate.Dump();
    StartDateUtc.Dump();
}

哪些输出(来自linqpad)我的预期:

12/20/2013 12:00:00 AM

12/20/2013 5:00:00 AM

为未指明的类型提示道具。这就是我所缺少的。但所有关于只有两种日期(本地和UTC)的讨论都让我感到困惑。

仅供参考 - 我运行的机器在中部时区,DST没有生效。

答案 4 :(得分:2)

UTC只是每个人都同意作为标准时区的时区。具体来说,这是一个包含英国伦敦的时区。 编辑:请注意,它不是完全相同的时区;例如,UTC没有DST。 (谢谢,Jon Skeet)

关于UTC的唯一特殊之处在于它在.Net中的使用比任何其他时区(DateTime.UtcNowDateTime.ToUniversalTime和其他成员更容易。

因此,正如其他人所提到的,你要做的最好的事情是在数据库中存储UTC的所有日期,然后转换为用户的本地时间(在显示之前写TimeZoneInfo.ConvertTime(time, usersTimeZone)


如果您想成为发烧友,可以geolocate用户的IP地址自动猜测他们的时区。