Проблема не имеет ничего общего с базой данных. Если установить точку останова или войти выход где-то, вы должны быть в состоянии видеть смещение быть пристегивается вскоре после этого кода:
TestDateAndTime = testDateAndTime.DateTime.Date;
Давайте сломаем это вниз:
Вы сказали:
Конечной целью является просто иметь дату без времени или часового пояса смещения.
Ну, есть в настоящее время не родной C# типа данных, который просто дата без времени.Существует чистый Date тип в System.Time пакет в corefxlab , но это еще не совсем готово для типичного производственного приложения. Там LocalDate в библиотеке Noda Time , которую вы можете использовать сегодня, но перед сохранением в базу данных вам все равно придется преобразовать обратно в собственный тип. Таким образом, в то же время, самое лучшее, что вы можете сделать:
В целом, в то время как DateTimeOffset подходит большое количество сценариев (например, меток времени событий), она не подходит хорошо для даты только значения.
Я хочу текущую дату с нулевым смещением.
Если вы действительно хотите это как DateTimeOffset , вы могли бы сделать:
TestDateAndTime = new DateTimeOffset(testDateAndTime.Date, TimeSpan.Zero);
Однако я не рекомендую этого делать. Поступая таким образом, вы берете местную дату первоначального значения и утверждаете, что она находится в UTC. Если исходным смещением является ничего, кроме нуля, это будет ложное утверждение. Он впоследствии приведет к другим ошибкам, поскольку вы на самом деле говорите о другом моменте времени (с потенциально другой датой), чем тот, который вы создали.
Относительно дополнительного вопроса, заданного в вашем правлении. Указание DateTimeKind.Utc изменяет поведение неявного приведения. Вместо использования местного часового пояса используется время UTC, которое всегда имеет смещение нуля. Результат такой же, как и более явный вид, который я дал выше. Я по-прежнему рекомендую против этого по тем же причинам.
Рассмотрите пример, начинающийся с 2016-12-31T22:00:00-04:00 . По вашему подходу вы должны сохранить в базе данных 2016-12-31T00:00:00+00:00 . Однако это два разных момента времени. Первый, нормализованный к UTC, будет 2017-01-01T02:00:00+00:00 , а второй, преобразованный в другой часовой пояс, будет 2016-12-30T20:00:00-04:00 . Обратите внимание на изменение дат в конверсии. Вероятно, это не то поведение, которое вы хотели бы использовать в своем приложении.
DateTimeOffset testDateAndTime = new DateTimeOffset(2008, 5, 1, 8, 6, 32, new TimeSpan(1, 0, 0)); //CLEAN TIME AND DATE testDateAndTime = testDateAndTime.DateTime.Date; var datesTableEntry = db.DatesTable.First(dt => dt.Id == someTestId); datesTableEntry.test= testDateAndTime; db.SaveChangesAsync();
РЕЗУЛЬТАТ В БАЗА ДАННЫХ: 2008-05-01 00:00:00.0000000 -04:00
Как включить -4:00 в +00:00 (из кода перед сохранением)?
Я пытался:
Public Task
Он ничего не делает.
Конечная цель - просто иметь дату без смещения времени или часового пояса. Я НЕ хочу преобразовывать время в другой часовой пояс (т. 00:00:00.0000000 Я не хочу, чтобы он вычитал 4 часа из 00:00:00.0000000 времени и 00:00:00.0000000 смещение установленного времени на +00:00 , я просто хочу, чтобы он установил смещение к +00:00). Я хочу текущую дату с нулевым смещением.
Редактировать:
Вот что можно предложить в другом месте:
DateTimeOffset testDateAndTime = new DateTimeOffset(2008, 5, 1, 8, 6, 32, new TimeSpan(1, 0, 0)); testDateAndTime = testDateAndTime.DateTime.Date; //Zero out time portion testDateAndTime = DateTime.SpecifyKind(testDateAndTime.Date, DateTimeKind.Utc); //"Zero out" offset portion
Я был уверен, что SpecifyKind будет SpecifyKind моей dateTimeOffset, например, измените BOTH время и смещение часового пояса, но при тестировании, похоже, просто изменит смещение часового пояса, что и есть то, что я хочу. Есть ли проблема с этим?
1 ответ
Проблема не имеет ничего общего с базой данных. Если вы установили точку останова или зарегистрировали выход где-нибудь, вы должны увидеть, как смещение будет привязано вскоре после этого кода:
TestDateAndTime = testDateAndTime.DateTime.Date;
Позвольте сломать это:
Ты сказал:
Конечная цель - просто иметь дату без смещения времени или часового пояса.
Ну, в настоящее время нет собственного типа данных С#, который является просто датой без времени. Существует чистый тип Date в пакете System.Time в corefxlab , но это не совсем готово для типичного производственного приложения. Там LocalDate в библиотеке времени Noda, которую вы можете использовать сегодня, но вам все равно придется преобразовать обратно в родной тип перед сохранением в базе данных. Таким образом, в то же время, самое лучшее, что вы можете сделать, это:
В общем случае, в то время как DateTimeOffset подходит для большого количества сценариев (например, событий timestamping), он не подходит для значений только даты.
Я хочу текущую дату с нулевым смещением.
Если вы действительно хотите, чтобы это как DateTimeOffset , вы бы сделали:
TestDateAndTime = new DateTimeOffset(testDateAndTime.Date, TimeSpan.Zero);
Однако я советую против этого. Поступая таким образом, вы берете локальную дату исходного значения и утверждаете, что она находится в UTC. Если исходным смещением является ничего, кроме нуля, это будет ложное утверждение. Он впоследствии приведет к другим ошибкам, поскольку вы на самом деле говорите о другом моменте времени (с потенциально другой датой), чем тот, который вы создали.
Что касается дополнительного вопроса, заданного в вашем редактировании - указание DateTimeKind.Utc изменяет поведение неявного приведения. Вместо использования локального часового пояса используется время UTC, которое всегда имеет смещение нуля. Результат такой же, как и более явный вид, который я дал выше. Я по-прежнему рекомендую против этого по тем же причинам.
Рассмотрим пример начала с 2016-12-31T22:00:00-04:00 . По вашему подходу вы должны сохранить в базе данных 2016-12-31T00:00:00+00:00 . Однако это два разных момента времени. Первый, нормализованный к UTC, будет 2017-01-01T02:00:00+00:00 , а второй, преобразованный в другой часовой пояс, будет 2016-12-30T20:00:00-04:00 . Обратите внимание на изменение дат в конверсии. Вероятно, это не то поведение, которое вы хотели бы использовать в своем приложении.
Вариант гуманный ([необходимо зарегистрироваться для просмотра ссылки]).
Суть проблемы в чем.. Если Вы случайно развернули базу на сервере SQL со «смещением даты» 0 то возникла проблема, когда в базе встречается реквизит с типом ВРЕМЯ т.е.в этот реквизит ставится 01.01.0001 10:30:00 или дата записалась пустой 01.01.0001 00:00:00. При записи такого реквизита не происходит его запись.
В интернете предлагают создать новую базу со смещением 2000.
Но новую базу создавать не очень хотелось. И менять у всех пользователях путь к базе.
Тогда я пошел по пути, а где ж хранится это значение в SQL-е. Нашел и поменял на 2000 и все стало ок..
А теперь по шагово, где поменять.
Выгнать всех пользователей.
ВНИМАНИЕ!!!
1. Сначала сделайте резервную копию средствами 1С т.е. выгрузите ее в *.dt
Это нужно делать до того как поменяете «смещение»
Если этого не сделать то во всей вашей базе спр., док, и т.д.
где есть есть реквизит дата будет стоять допустим 02.10.0009
ЧТО НЕ ДОПУСТИМО….
Итак Вы сделали выгрузку в *.dt
2. Заходим в SQL Server Management Studio
Находим Вашу базу в списку нажимаем плюсик.
Находи там папочку «Таблицы» и раскрываем ее.
Откроется куча таблиц, идем в самый низ, находим таблицу
_YearOffset, становимся на нее и правой клавишей выбираем пункт «Открыть таблицу» см. рис.1
Меняем значение 0 на 2000
Закрываем SQL Server Management Studio
3. Заходим в конфигуратор и загружаем ранее сохраненную базу.
Если это не сделать, то все даты будут с годом 0009.
После того как база загрузилась… Можно зайти в 1С и удостоверится что даты нормальные.
Результат мы поменяли «смещение дата с 0 на 2000»
Иногда бывает так, что этот вариант использовать не получается по тем или иным причинам. Тогда есть более хардкорный вариант ([необходимо зарегистрироваться для просмотра ссылки]):
Declare TablesAndFields cursor for
SELECT
objects.name as
Tablename, columns.name as
columnname
FROM
dbo.sysobjects
as
objects
left
join
dbo.syscolumns
as
columns on
objects.id =
columns.id
where
objects.xtype =
"U"
and
columns.xtype =
61
open TablesAndFields
WHILE @@FETCH_STATUS =
0
BEGIN
Exec ("update
"
+
@TableName +
"
set " +
@ColumnName +
"
=
""2000-
01-
01 00:00:00"
"
where
" +
@ColumnName +
"
>
""3999-
12-
31 23:59:59"
"")
This is executed as
long as
the previous fetch succeeds.
FETCH NEXT FROM
TablesAndFields
into
@TableName, @ColumnName
END
close TablesAndFields
deallocate TablesAndFields
go
Перед любыми манипуляциями не забывайте делать копии баз данных!
Практически все проекты сталкиваются с проблемами, вызванными неправильной обработкой и хранением даты и времени. Даже если проект используется в одном часовом поясе, все равно после перехода на зимнее/летнее время можно получить неприятные неожиданности. При этом мало кто озадачивается реализацией правильного механизма со старта, потому что кажется, что с этим проблем быть не может, так как все тривиально. К сожалению, в последствии реальность показывает, что это не так.
Логически можно выделить следующие типы значений, относящиеся к дате и времени:
Рассмотрим каждый пункт по отдельности, не забывая об .
Рассмотрим одну из возможных цепочек действий, через которую проходят данные с клиента на сервер и обратно, позволяющую всегда корректно отображать дату/время согласно текущему часовому поясу клиента:
Правила перевода на летнее/зимнее время — вещь, строго говоря, переменная. Разные страны могут иногда менять свои правила, и эти изменения должны быть заблаговременно заложены в обновления системы. На практике неоднократно встречались ситуации некорректной работы этого механизма, которые по итогу решались установкой хотфиксов либо операционной системы, либо используемых сторонних библиотек. Вероятность повторения тех же проблем — не нулевая, поэтому лучше иметь способ гарантированно их избежать.
Учитывая описанные выше соображения, сформулируем как можно более надежный и простой подход к передаче и хранению времени: на сервере и в базе данных все значения должны быть приведены к часовому поясу UTC .
Рассмотрим, что нам дает такое правило:
.NET | DateTimeOffset |
Java | org.joda.time.DateTime, java.time.ZonedDateTime |
MS SQL | datetimeoffset |
Oracle, PostgreSQL | TIMESTAMP WITH TIME ZONE |
MySQL | — |
Как видно из вышесказанного, не существует единого подхода, покрывающего 100% случаев . Поэтому сперва нужно четко понять из требований, какие из упомянутых выше ситуаций будут в вашей системе. С большой вероятностью, все ограничится первым предложенным подходом с хранением в UTC. Ну а описанные исключительные ситуации его не отменяют, а просто добавляют другие решения для частных случаев.
Не во всех платформах, языках и СУБД есть типы, хранящие только дату. Например, в.NET есть только тип DateTime, отдельного «просто Date» нет. Даже если при создании такого объекта была указана только дата, время все равно присутствует, и оно равно 00:00:00. Если мы значение «2 февраля 2016 00:00:00» из пояса со смещением +2 переведем в +1, то получим «1 февраля 2016 23:00:00». Для указанного выше примера это будет равносильно тому, что в одном часовом поясе новый контракт начнет действовать 2 февраля, а в другом — 1 февраля. С юридической точки зрения это абсурд и так, конечно же, быть не должно. Общее правило для «чисто» дат предельно простое — такие значения не должны преобразовываться ни на одном шаге сохранения и чтения.
Есть несколько способов избежать преобразование для дат:
А вот вычисление интервала может иметь подводные камни. Предположим, у нас есть типовой код на C#, который считает интервал времени между двумя событиями:
DateTime start = DateTime.Now;
//...
DateTime end = DateTime.Now;
double hours = (end - start).TotalHours;
На первый взгляд, никаких проблем здесь нет, но это не так. Во-первых, могут возникнуть проблемы с юнит-тестированием такого кода, но об этом мы поговорим чуть позже. Во-вторых, давайте представим, что начальный момент времени пришелся на зимнее время, а конечный — на летнее (например, таким образом замеряется количество рабочих часов, а у работников есть ночная смена).
Предположим, код работает в часовом поясе, в котором переход на летнее время в 2016 году происходит в ночь 27 марта, и смоделируем описанную выше ситуацию:
DateTime start = DateTime.Parse("2016-03-26T20:00:15+02");
DateTime end = DateTime.Parse("2016-03-27T05:00:15+03");
double hours = (end - start).TotalHours;
Этот код даст в результате 9 часов, хотя фактически между этими моментами прошло 8 часов. В этом легко убедиться, изменив код вот таким образом:
DateTime start = DateTime.Parse("2016-03-26T20:00:15+02").ToUniversalTime();
DateTime end = DateTime.Parse("2016-03-27T05:00:15+03").ToUniversalTime();
double hours = (end - start).TotalHours;
Отсюда вывод — любые арифметические операции с датой и временем нужно делать, используя либо UTC значения, либо типы, хранящие информацию о часовом поясе
. А потом обратно переводить в локальные в случае надобности. С этой точки зрения, изначальный пример легко исправить, поменяв DateTime.Now на DateTime.UtcNow.
Этот нюанс не зависит от конкретной платформы или языка. Вот аналогичный код на Java, имеющий тот же недостаток:
LocalDateTime start = LocalDateTime.now();
//...
LocalDateTime end = LocalDateTime.now();
long hours = ChronoUnit.HOURS.between(start, end);
Исправляется он также легко — например, использованием ZonedDateTime вместо LocalDateTime.
В большинстве случаев писать свой планировщик не имеет смысла, так как существуют гибкие проверенные временем решения, но если по какой-то причине есть надобность в создании своего механизма, то как минимум формат расписания можно позаимствовать у cron.
Помимо описанных выше рекомендаций, посвященных хранению и обработке разнотипных значений времени, есть еще несколько других, о которых тоже хотелось бы сказать.Во-первых, по поводу использования статических членов класса для получения текущего времени — DateTime.UtcNow, ZonedDateTime.now() и т.д. Как и было сказано, использование их напрямую в коде может серьезно усложнить юнит-тестирование, так как без специальных мок фреймворков подменить текущее время не получится. Поэтому, если вы планируете писать юнит тесты, следует позаботиться о том, чтобы реализацию таких методов можно было подменить. Для решения этой задачи есть как минимум два способа:
Второй нюанс с получением текущего времени — это то, что клиенту доверять нельзя . Текущее время на компьютерах пользователей может очень сильно отличаться от реального, и если есть логика, завязанная на него, то эта разница может все испортить. Все места, где есть необходимость получать текущее время, должны по возможности выполняться на стороне сервера. И, как уже было сказано ранее, все арифметические операции с временем должны производиться либо в UTC значениях, либо с использованием типов, хранящих смещение часового пояса.
И еще одна вещь, которую хотелось упомянуть — это стандарт ISO 8601 , описывающий формат даты и времени для обмена информацией. В частности, строковое представление даты и времени, используемое при сериализации, должно соответствовать этому стандарту для предотвращения потенциальных проблем с совместимостью. На практике крайне редко приходится самому реализовывать форматирование, поэтому сам стандарт может быть полезен в основном в ознакомительных целях.
Теги: Добавить метки
pushup-store.ru - Интернет. Программы. Инструкции. Поломки. Папки и файлы