Возникли проблемы с преобразованием моего DateTime в UTC - PullRequest
55 голосов
/ 30 августа 2009

Я храню все свои даты в формате 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"

Я не знаю, почему я получаю это.

Я пробовал 2 способа

 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 @ Джон Скит

Да, я просто думал о том, что мне, возможно, даже не нужно делать все это. Времена меня смущают, поэтому пост может быть не таким ясным, как следовало бы. Я никогда не знаю, какого черта DateTime.Now получает (я пытался изменить свой часовой пояс на другой, и он продолжал получать мое местное время).

Это то, что я хотел, чтобы мои вещи делали. Пользователь заходит на сайт, добавляет какое-то предупреждение, и теперь он сохраняется как utc (раньше это было DateTime. Теперь кто-то предложил хранить все в формате UTC).

Итак, до того, как пользователь зашел на мой сайт и, в зависимости от того, где был мой хостинг-сервер, он мог быть таким на следующий день. Поэтому, если говорят, что предупреждение показывается 30-го августа (их время), но с разницей во времени сервера они могут прийти 29-го августа, и предупреждение будет показано.

Так что я хотел с этим бороться. Так что теперь я не уверен, стоит ли мне просто хранить их местное время, а затем использовать это смещение? Или просто хранить время UTC. Простое сохранение времени UTC все еще может быть неправильным, поскольку пользователь, вероятно, будет думать по местному времени, и я не уверен, как на самом деле работает UTC, это все равно может привести к разнице во времени.

Edit3

 var info = TimeZoneInfo.FindSystemTimeZoneById(id)

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

Ответы [ 5 ]

94 голосов
/ 30 августа 2009

Вам необходимо установить Kind на Unspecified, например:

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

DateTimeKind.Local означает местный часовой пояс, а не любой другой часовой пояс. Вот почему вы получили ошибку.

38 голосов
/ 30 августа 2009

Структура DateTime поддерживает только два часовых пояса:

  • Часовой пояс local , в котором работает машина.
  • и 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
7 голосов
/ 21 декабря 2013

Ответ остальных кажется слишком сложным. У меня было определенное требование, и оно работало нормально для меня:

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), только запутали меня.

К вашему сведению - машина, на которой я работал, находилась в центральном часовом поясе, а летнее время не действовало.

7 голосов
/ 30 августа 2009

Как говорит dtb, вы должны использовать DateTimeOffset, если хотите сохранить дату / время с определенным часовым поясом.

Однако из вашего поста совсем не ясно, что вам действительно нужно. Вы только приводите примеры, используя DateTime.Now, и говорите, что угадываете , что вы используете серверное время. Во сколько ты на самом деле хочешь? Если вы просто хотите узнать текущее время в UTC, используйте DateTime.UtcNow или DateTimeOffset.UtcNow. Вам не нужно знать часовой пояс, чтобы узнать текущее время UTC, именно потому, что оно универсально.

Если вы получаете дату / время от пользователя каким-либо другим способом, пожалуйста, дайте больше информации - таким образом, мы сможем решить, что вам нужно сделать. В противном случае мы просто догадываемся.

2 голосов
/ 30 августа 2009

UTC - это просто часовой пояс, который все согласовали как стандартный часовой пояс. В частности, это часовой пояс, который содержит Лондон, Англия. РЕДАКТИРОВАТЬ : обратите внимание, что это не тот же часовой пояс; например, UTC не имеет летнего времени. (Спасибо, Джон Скит)

Единственная особенность UTC заключается в том, что использовать ее в .Net гораздо проще, чем в любом другом часовом поясе (DateTime.UtcNow, DateTime.ToUniversalTime и других членах).

Поэтому, как уже упоминали другие, лучше всего сохранить в базе данных все даты в формате UTC, а затем преобразовать в местное время пользователя (записав TimeZoneInfo.ConvertTime(time, usersTimeZone) перед отображением.


Если вы хотите быть более изобретательным, вы можете geolocate IP-адреса своих пользователей, чтобы автоматически угадывать их часовые пояса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...