Как SqlDateTime снижает точность - PullRequest
5 голосов
/ 19 октября 2011

Рассмотрим следующую программу:

DateTime dateTime = new DateTime(634546165144647370);
SqlDateTime sqlDateTime = new SqlDateTime(dateTime);

Console.WriteLine("dateTime.TimeOfDay    = " + dateTime.TimeOfDay);
Console.WriteLine("sqlDateTime.TimeOfDay = " + sqlDateTime.Value.TimeOfDay);
Console.ReadLine();

Это будет иметь следующий вывод:

dateTime.TimeOfDay    = 10:21:54.4647370  
sqlDateTime.TimeOfDay = 10:21:54.4630000

Что странно для меня, это то, что .464737 было округлено до .463. Разве это не должно быть округлено до .464?

Я предполагаю, что не нашел ошибку в коде .NET, поэтому вопрос:

Почему он округлился до того, что сделал? и как я могу получить на стороне клиента округления, которые будут делать то, что собирается делать SqlServer?

В качестве примечания я сохранил это время даты в базе данных SQL Server (в столбце DateTime) и извлек ее снова, и оно получилось как 10: 21: 54.4670000 . Так что я действительно в замешательстве. (Я думал, что SqlDateTime будет соответствовать тому, что собирался делать SQL Server.)

Примечание: Поскольку я использую OData, я не могу использовать DateTime2 в SQL Server.

Ответы [ 3 ]

8 голосов
/ 19 октября 2011

SQL Server DATETIME имеет точность 3,33 мс - следовательно, вы не можете получить все возможные значения, и есть вероятность, что .464 было именно таким значением.можно использовать типы данных DATETIME2 или TIME(x) с точностью до 100 нс - этого должно быть достаточно для "регулярного" использования

6 голосов
/ 19 октября 2011

Тип данных SQL Server datetype - это два 32-битных слова (целые числа). Старшее слово - это смещение в днях, начиная с эпохи (нулевой точки) внутреннего календаря, используемого SQL Server: этой эпохой является 1 января 1900 00: 00: 00.000. Слово младшего разряда - это смещение в миллисекундах с начала дня (00: 00: 00.000 / полночь).

По историческим причинам, в то время как точность младшего слова составляет 1 миллисекунду; точность составляет 1/300 секунды (!?). Это означает, что любой данный момент времени округляется с шагом 0, 3 или 7 миллисекунд.

Чтобы выполнить преобразование так, как это делает SQL Server, выполните следующие действия: возьмите долю в миллисекундах рассматриваемого фактического времени, значение от 0 до 999, по модулю 100. Это даст вам младшую цифру, значение от 0 до 9. Таким образом, если текущее время составляет 23: 57: 23.559, компонент в миллисекундах равен 559. По модулю 100 вы получите 9.

  1. Значения 0 и 1 округляются до 0.
  2. Значения 2, 3 и 4 «округлены» до 3.
  3. Значения 5, 6, 7 и 8 «округлены» до 7.
  4. Значение 9 округляется UP до 0. Это имеет довольно неприятный и неприятный побочный эффект: если доля времени в миллисекундах равна 999, она увеличивается на 1 миллисекунду. Это означает, что время 23: 59: 59,999 округляется до СЛЕДУЮЩИЙ ДЕНЬ . Таким образом, преобразование '31 Dec 2010 23: 59: 59.999 'дает значение даты и времени ... 1 января 2011 00: 00: 00.000.

Brilliant! Или что-то.

См. Раздел «Замечания» в разделе SQL Server 2005 BOL здесь: http://msdn.microsoft.com/en-us/library/ms187819(v=SQL.90).aspx

Результатом всего этого является то, что вы не можете выполнить очевидную проверку для диапазона дат / периода времени ... что-то вроде

where myDateColumn between '1 September 2011 00:00:00.000'
  and '30 September 2011 23:59:59.999'

, поскольку это потенциально может принести [небольшой] кусок данных за следующий период. И вы не можете сказать

where myDateColumn between '1 September 2011 00:00'
  and '30 September 2011 23:59:59'

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

  • where myDateColumn >= '1 September 2011 00:00:00.000' and myDateColumn < '1 October 2011 00:00:00.000' или

  • where myDateColumn >= '1 September 2011 00:00:00.000' and myDateColumn <= '30 September 2011 23:59:59.997'

Следует отметить, что smalldatetime с точностью до 1 минуты демонстрирует то же поддельное поведение: если составляющая секунд рассматриваемого времени составляет 29,998 секунд или меньше, она округляется до ближайшей минуты; если 29,999 или выше, округляется UP до следующей минуты, поэтому значение 31 Dec 2010 23:59:30.000' winds up as a smalldatetime value of 1 Jan 2011 00: 00: 00`.

Это имеет всевозможные последствия, особенно WRT для биллинговых систем и тому подобного.

Я бы сказал, что если точность важна для вас, сохраните значения даты / времени в SQL Server в виде строк в формате ISO 8601 , что-то вроде 2011-10-30T23:59:55.1234 (или эквивалентную «компактную» форму ( 20111030T235955.1234). Стандарт ISO 8601 сопоставляет и сравнивает должным образом, он легко преобразуется и удобочитаем для человека. Еще лучше разбить его на два столбца - один для даты (2011-10-30) и один для времени (23:59:55.1234). Затем добавьте третий вычисляемый столбец, чтобы сложить все вместе:

create table foo
(
  ...
  transaction_date char(10) not null ,
  transaction_time char(12) not null ,
  ...
  iso8601_transaction_datetime as transaction_date + 'T' + transaction_time ,
  ...
)

Довольно хорошее резюме ISO 8601 в http://www.cl.cam.ac.uk/~mgk25/iso-time.html. В Википедии также есть довольно хорошая информация: http://en.wikipedia.org/wiki/ISO_8601.

1 голос
/ 19 октября 2011

Структура SqlDateTime

Представляет данные даты и времени в диапазоне значений с 1 января 1753 года по 31 декабря 9999 года с точностью 3,33 миллисекунды для хранения илиизвлекается из базы данных.Структура SqlDateTime имеет базовую структуру данных, отличную от соответствующего типа платформы .NET Framework, DateTime, который может представлять любое время между 12:00:00 1 января 2001 года и 11:59:59 31 декабря 1999 года.точность 100 наносекунд.SqlDateTime фактически сохраняет относительную разницу до 00:00:00 1 января 1900 года.Поэтому преобразование из «00:00:00 AM 01.01.1900» в целое число вернет 0.

Я бы сказал, что sqlDateTime находится в пределах точности в 3.33 мс.

Тип значения DateTime представляет даты и время со значениями в диапазоне от 12:00:00 до полуночи, 1 января 0001 года от Anno Domini (Common Era) до 23:59:59 PM, 31 декабря 9999 года нашей эры

В качестве примечания я сохранил это время даты в базе данных SQL Server (в столбце DateTime) и снова извлек ее, и оно получилось как 10: 21: 54.4670000.Так что я действительно в замешательстве.(Я думал, что SqlDateTime будет соответствовать тому, что собирался делать SQL Server.)

Значения времени измеряются в единицах по 100 наносекунд, называемых тиками, и конкретной датой являетсяколичество тиков с 12:00 полуночи 1 января 0001 г. н.э. (CE) в календаре GregorianCalendar (исключая тики, которые будут добавлены за високосные секунды).Например, значение тиков 31241376000000000L представляет дату, пятница, 01 января, 0100 12:00:00 полночь.Значение DateTime всегда выражается в контексте явного календаря или календаря по умолчанию.

и

Внутри все значения DateTime представляются в виде числа тактов (число100-наносекундных интервалов), прошедших с 12:00:00 до полуночи 1 января 0001 г. Фактическое значение DateTime не зависит от способа отображения этого значения при отображении в элементе пользовательского интерфейса или при записи в файл.Появление значения DateTime является результатом операции форматирования.Форматирование - это процесс преобразования значения в его строковое представление.

Поскольку появление значений даты и времени зависит от таких факторов, как культура, международные стандарты, требования к приложениям и личные предпочтения, структура DateTime предлагаетбольшая гибкость в форматировании значений даты и времени благодаря перегрузкам его метода ToString.Метод DateTime.ToString () по умолчанию возвращает строковое представление значения даты и времени с использованием шаблона короткой даты и времени текущей культуры.В следующем примере используется метод DateTime.ToString () по умолчанию для отображения даты и времени с использованием шаблона короткой даты и времени для культуры en-US, текущей культуры на компьютере, на котором был запущен пример.

Похоже, что DateTime является точным в пределах 100 нс, что о разнице, с которой вы столкнулись.

В качестве примечания я сохранил это время даты в SQLБаза данных сервера (в столбце DateTime) снова вытащила ее, и она вышла 10: 21: 54.4670000Так что я действительно в замешательстве.(Я думал, что SqlDateTime будет соответствовать тому, что собирался делать SQL Server.)

Это также с точностью до 3,33 мс ... Если вам нужно что-то более точное, чем 3,33 мс, тогда у вас будетиспользовать SQL Server 2008

...