Проблемы с преобразованием даты DateTime в UTC

Я сохраняю все свои даты в формате 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 @Джон Скит

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

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

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

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

Edit3

 var info = TimeZoneInfo.FindSystemTimeZoneById(id)

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

Ответ 1

Структура 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 Time: {0}", usersTime);
Console.WriteLine("UTC:         {0}", utc);

Вывод:

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

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

Реквизит для Slaks для подсказки Unspecified. Этого я не хватало. Но все разговоры о том, что существуют только два вида дат (локальные и UTC), просто путают проблему для меня.

FYI - машина, на которой я работал, была в Центральном часовом поясе, и DST не действовал.

Ответ 5

UTC - это всего лишь часовой пояс, который все согласовали как стандартный часовой пояс. В частности, это часовой пояс, который содержит Лондон, Англия. EDIT: обратите внимание, что это не тот же часовой пояс; например, UTC не имеет DST. (Спасибо, Джон Скит)

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

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


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