Как обрабатывается часовой пояс в жизненном цикле столбца ADO.NET + SQL Server DateTime? - PullRequest
4 голосов
/ 06 мая 2010

Использование SQL Server 2008. Это действительно младший вопрос, и я мог бы действительно использовать некоторую сложную информацию, но информация в Google, кажется, немного расходится по этой теме, и было бы неплохо, если бы была какая-то детальная проработка того, как это работает ...

Допустим, у меня есть столбец datetime, и в ADO.NET я установил его на DateTime.UtcNow.

1) Сохраняет ли SQL Server DateTime.UtcNow соответственно или смещает его снова в зависимости от часового пояса, в котором установлен сервер, и затем возвращает его со смещением при запросе? Я думаю, что знаю, что ответ «конечно, он хранит его, не компенсируя его снова», но хочу быть уверенным.

Итак, я запрашиваю его и преобразую из объекта в DateTime после получения его, скажем, из столбца IDataReader. Насколько я знаю, System.DateTime имеет метаданные, которые внутренне отслеживают, является ли это DateTime UTC или смещенным DateTime, что может вызывать или не вызывать .ToLocalTime () и .ToUniversalTime () иметь различное поведение в зависимости от этого состояния , Таким образом,

2) Знает ли этот приведенный объект System.DateTime, что это экземпляр UTC DateTime, или он предполагает, что он был смещен?


Теперь допустим, что я не использую UtcNow, я использую DateTime.Now при выполнении операции вставки или обновления ADO.NET.

3) Передает ли ADO.NET смещение в SQL Server и хранит ли SQL Server DateTime.Now с метаданными смещения?

Итак, я запрашиваю его и преобразую, скажем, из столбца IDataReader в DateTime.

4) Знает ли этот приведенный объект System.DateTime, что это время смещения, или предполагается, что это UTC?

Ответы [ 3 ]

8 голосов
/ 04 июня 2010

Провел несколько юнит-тестов, чтобы ответить на мой собственный вопрос во всех четырех частях.

1: SQL Server хранит DateTime.UtcNow соответственно или смещает его снова в зависимости от часового пояса, в котором установлен сервер, а затем возвращает его со смещением при запросе?

Выполнено это):

cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.UtcNow));
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT CAST(val as varchar) value FROM testtbl";
Console.WriteLine(cmd.ExecuteScalar());

Результат в 13:30 по местному времени (-7 или 20:30 по Гринвичу) был:

Jun  3 2010 8:30PM

Тогда я попробовал это:

cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.UtcNow));
cmd.ExecuteNonQuery();
Console.WriteLine("change time zone to utc");
Console.ReadLine();
cmd.CommandText = "SELECT CAST(val as varchar) value FROM testtbl";
Console.WriteLine(cmd.ExecuteScalar());
Console.WriteLine("change time zone back to local");

Выполнено в 21:25 UTC, возвращено

Jun  3 2010 9:25PM

Сравните это с DateTime.Now:

.
cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.Now));
cmd.ExecuteNonQuery();
Console.WriteLine("change time zone to utc");
Console.ReadLine();
cmd.CommandText = "SELECT CAST(val as varchar) value FROM testtbl";
Console.WriteLine(cmd.ExecuteScalar());
Console.WriteLine("change time zone back to local");

Выполнено в 15:55 (местное; -7ч), возвращено:

Jun  3 2010  3:55PM

2: Итак, я запрашиваю его и преобразую из объекта в DateTime после получения его, скажем, из столбца IDataReader. Знает ли этот приведенный объект System.DateTime, что это экземпляр UTC DateTime, или он предполагает, что он был смещен?

Ни.

cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.UtcNow));
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT val value FROM testtbl";
var retval = (DateTime)cmd.ExecuteScalar();
Console.WriteLine("Kind: " + retval.Kind);
Console.WriteLine("UTC: " + retval.ToUniversalTime().ToString());
Console.WriteLine("Local: " + retval.ToLocalTime().ToString());

Результат этого (выполненный в 13:58 по местному времени) был:

Kind: Unspecified
UTC: 6/4/2010 3:58:42 AM
Local: 6/3/2010 1:58:42 PM

То есть .ToUniversalTime() закончил смещение от местного времени до времени UTC не один раз, а дважды (??), а .ToLocalTime() закончилось тем, что вообще не сместилось.

3: передает ли ADO.NET смещение в SQL Server и хранит ли SQL Server DateTime.Now с метаданными смещения?

Без выполнения каких-либо модульных тестов ответ уже известен как тип SQL «только с DateTimeOffset». SQL datetime не делает смещения.

4: этот приведенный объект System.DateTime уже знает, что это время смещения, или он предполагает, что это UTC?

Ни. Тип DateTimeOffset в SQL возвращается в виде структуры .NET DateTimeOffset.

Следующее выполнение выполняется в 15:31 по местному времени, где столбец offval является типом SQL datetimeoffset,

cmd.CommandText = "INSERT INTO testtbl (offval) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.Now));
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT offval value FROM testtbl";
object retvalobj = cmd.ExecuteScalar();
Console.WriteLine("Type: " + retvalobj.GetType().Name);
var retval = (DateTimeOffset)retvalobj;
Console.WriteLine("ToString(): " + retval.ToString());
Console.WriteLine("UTC: " + retval.ToUniversalTime().ToString());
Console.WriteLine("Local: " + retval.ToLocalTime().ToString());

Это привело к:

Type: DateTimeOffset
ToString(): 6/3/2010 3:31:47 PM +00:00
UTC: 6/3/2010 3:31:47 PM +00:00
Local: 6/3/2010 8:31:47 AM -07:00

Удивительное неравенство.


Возвращаясь и выполняя тест для вопроса № 1 выше, используя DateTime.Now вместо DateTime.UtcNow, я проверил, что ADO.NET НЕ конвертируется в универсальное время перед сохранением в базе данных.

То есть, это выполняется в 15:27 по местному времени (-7 ч):

 cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
 cmd.Parameters.Add(new SqlParameter("@newval", DateTime.Now));
 cmd.ExecuteNonQuery();
 Console.WriteLine("change time zone to utc");
 Console.ReadLine();
 cmd.CommandText = "SELECT CAST(val as varchar) value FROM testtbl";
 Console.WriteLine(cmd.ExecuteScalar());
 Console.WriteLine("change time zone back to local");

.. вернулся ..

Jun  3 2010  3:27PM

Выполнение этого в 15:17 по местному времени:

cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.UtcNow));
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT val FROM testtbl";
var result = (DateTime)cmd.ExecuteScalar();
Console.WriteLine("Kind: " + result.Kind);
Console.WriteLine("ToString(): " + result.ToString());
Console.WriteLine("Add 1 minute, is greater than UtcNow? "
 + (result.AddMinutes(1) > DateTime.UtcNow).ToString());
Console.WriteLine("Add 1 minute, is greater than Now? "
 + (result.AddMinutes(1) > DateTime.Now).ToString());
Console.WriteLine("Add 1 minute, is less than UtcNow? "
 + (result.AddMinutes(1) < DateTime.UtcNow).ToString());
Console.WriteLine("Add 1 minute, is less than Now? "
 + (result.AddMinutes(1) < DateTime.Now).ToString());
Console.WriteLine("Subtract 1 minute, is greater than UtcNow? "
 + (result.AddMinutes(-1) > DateTime.UtcNow).ToString());
Console.WriteLine("Subtract 1 minute, is greater than Now? "
 + (result.AddMinutes(-1) > DateTime.Now).ToString());
Console.WriteLine("Subtract 1 minute, is less than UtcNow? "
 + (result.AddMinutes(-1) < DateTime.UtcNow).ToString());
Console.WriteLine("Subtract 1 minute, is less than Now? "
 + (result.AddMinutes(-1) < DateTime.Now).ToString());

Результат:

Kind: Unspecified
ToString(): 6/3/2010 10:17:05 PM
Add 1 minute, is greater than UtcNow? True
Add 1 minute, is greater than Now? True
Add 1 minute, is less than UtcNow? False
Add 1 minute, is less than Now? False
Subtract 1 minute, is greater than UtcNow? False
Subtract 1 minute, is greater than Now? True
Subtract 1 minute, is less than UtcNow? True
Subtract 1 minute, is less than Now? False

Сравните это с DateTime.Now:

cmd.CommandText = "INSERT INTO testtbl (val) VALUES (@newval)";
cmd.Parameters.Add(new SqlParameter("@newval", DateTime.Now));
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT val FROM testtbl";
var result = (DateTime)cmd.ExecuteScalar();
Console.WriteLine("Kind: " + result.Kind);
Console.WriteLine("ToString(): " + result.ToString());
Console.WriteLine("Add 1 minute, is greater than UtcNow? "
 + (result.AddMinutes(1) > DateTime.UtcNow).ToString());
Console.WriteLine("Add 1 minute, is greater than Now? "
 + (result.AddMinutes(1) > DateTime.Now).ToString());
Console.WriteLine("Add 1 minute, is less than UtcNow? "
 + (result.AddMinutes(1) < DateTime.UtcNow).ToString());
Console.WriteLine("Add 1 minute, is less than Now? "
 + (result.AddMinutes(1) < DateTime.Now).ToString());
Console.WriteLine("Subtract 1 minute, is greater than UtcNow? "
 + (result.AddMinutes(-1) > DateTime.UtcNow).ToString());
Console.WriteLine("Subtract 1 minute, is greater than Now? "
 + (result.AddMinutes(-1) > DateTime.Now).ToString());
Console.WriteLine("Subtract 1 minute, is less than UtcNow? "
 + (result.AddMinutes(-1) < DateTime.UtcNow).ToString());
Console.WriteLine("Subtract 1 minute, is less than Now? "
 + (result.AddMinutes(-1) < DateTime.Now).ToString());

Выполнено в 15:58 (местное время, -7ч):

Kind: Unspecified
ToString(): 6/3/2010 3:59:26 PM
Add 1 minute, is greater than UtcNow? False
Add 1 minute, is greater than Now? True
Add 1 minute, is less than UtcNow? True
Add 1 minute, is less than Now? False
Subtract 1 minute, is greater than UtcNow? False
Subtract 1 minute, is greater than Now? False
Subtract 1 minute, is less than UtcNow? True
Subtract 1 minute, is less than Now? True
2 голосов
/ 06 мая 2010

SQL Server не хранит никакой информации о часовом поясе с информацией о дате / времени, помещенной в базу данных, а также не производит никаких корректировок этих значений (кроме использования функции GetUTCDate ()).

Таким образом, интерпретация этих данных и их часовых поясов полностью зависит от вашего приложения. Если вам нужно отслеживать информацию о часовом поясе, вы должны делать это вне столбцов DateTime в SQL Server.

Одна из наших лучших рекомендаций - всегда хранить ТОЛЬКО даты UTC в SQL Server и при необходимости вносить любые корректировки в местное время.

1 голос
/ 06 мая 2010

DateTime не хранит никакой информации о смещении - он только сохраняет значение datetime. DateTimeOffset в .Net сохранит смещение часового пояса. Если вы передадите DateTimeOffset в SQL Server, смещение будет сохранено в базе данных и получено обратно для вас при запросе таблицы.

В типе DateTimeOffset вы можете проверить свойство Offset, а также использовать методы ToLocalTime, ToUniversalTime и ToOffset.

...