Отместване на нулева дата sql сървър. Премахване на отместването на часовата зона от DateTimeOffset

Отместване на нулева дата sql сървър. Премахване на отместването на часовата зона от DateTimeOffset

Хуманният вариант ([трябва да се регистрирате, за да видите връзката]).

Същността на проблема е следната. Ако случайно сте разположили база данни на SQL сървър с „отместване на дата“ от 0, тогава възниква проблем, когато базата данни съдържа атрибут с тип TIME, т.е. този атрибут е зададен на 01 /01/0001 10:30:00 или дата на регистрация празна 01.01.0001 00:00:00. При запис на такива подробности не се записва.
В интернет предлагат да създават нова базас компенсация от 2000.
Но всъщност не исках да създавам нова база. И променете пътя до базата данни за всички потребители.
След това последвах пътя къде се съхранява тази стойност в SQL. Намерих го и го смених на 2000 и всичко беше наред..
А сега стъпка по стъпка къде да се промени.

Изритайте всички потребители.

ВНИМАНИЕ!!!

1. Направете го първи резервно копиечрез 1C т.е. качете го в *.dt
Това трябва да се направи преди промяна на „отместването“
Ако това не е направено, тогава в цялата ви база данни ще има препратки, документи и т.н.
където има реквизит датата ще бъде да речем 02.10.0009
КАКВО Е НЕПОЗВОЛЕНО...
Така че сте качили в *.dt

2. Отидете на SQL Server Management Studio
Намерете вашата база в списъка и натиснете знака плюс.
Намерете там папката „Таблици“ и я отворете.
Ще се отворят куп маси, отидете до дъното, намерете масата
_YearOffset, застанете върху него и изберете елемента „Отвори маса“ с десния бутон, вижте Фиг. 1
Променете стойността 0 на 2000
Затворете SQL Server Management Studio

3. Отидете в конфигуратора и заредете предварително запазената база данни.

Ако това не е направено, тогава всички дати ще бъдат с година 0009.
След като базата данни се зареди... Можете да отидете в 1C и да се уверите, че датите са нормални.
Резултатът е, че променихме „отместването на датата от 0 до 2000“

Понякога се случва тази опция да не може да се използва по една или друга причина. След това има по-хардкор опция ([трябва да се регистрирате, за да видите връзката]):

Декларирайте курсора TablesAndFields за

ИЗБЕРЕТЕ objects.name като име на таблица, columns.name като име на колона
ОТ dbo.sysobjects катообекти
left join dbo.syscolumns като колони на objects.id = columns.id
където objects.xtype = "U" и columns.xtype = 61

отворете TablesAndFields

WHILE @@FETCH_STATUS = 0
BEGIN Exec(" актуализация"+ @TableName + "
комплект " + @ColumnName + " = ""2000- 01- 01 00:00:00" "
където " + @ColumnName + " > ""3999- 12- 31 23:59:59" "")

Това се изпълнява, докато предишното извличане е успешно.
FETCH NEXT FROM TablesAndFields в @TableName, @ColumnName
КРАЙ

затворете TablesAndFields
deallocateTablesAndFields
отивам

Преди всякакви манипулации не забравяйте да направите копия на базите данни!

DateTimeOffset testDateAndTime = нов DateTimeOffset(2008, 5, 1, 8, 6, 32, нов TimeSpan(1, 0, 0)); //ИЗЧИСТВАНЕ НА ВРЕМЕ И ДАТА 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 (от код преди запазване)?

Опитах:

Обществена задача SetTimeZoneOffsetToZero(DateTimeOffset dateTimeOffSetObj) ( TimeSpan zeroOffsetTimeSpan = new TimeSpan(0, 0, 0, 0, 0); return dateTimeOffSetObj.ToOffset(zeroOffsetTimeSpan); )

Той не прави нищо.

Крайната цел е просто да имате дата без отместване на час или часова зона. НЕ искам да преобразувам часа в друга часова зона (т.е. 00:00:00.0000000 не искам да изважда 4 часа от времето 00:00:00.0000000 и 00:00:00.0000000 да компенсира зададеното време с +00 :00 , просто искам да зададе отместването на +00:00). Искам текущата дата с нулево отместване.

Редактиране:

Ето какво можете да предложите другаде:

DateTimeOffset testDateAndTime = нов DateTimeOffset(2008, 5, 1, 8, 6, 32, нов TimeSpan(1, 0, 0)); testDateAndTime = testDateAndTime.DateTime.Date; //Нулиране на част от времето testDateAndTime = DateTime.SpecifyKind(testDateAndTime.Date, DateTimeKind.Utc); // "Нулева" компенсирана част

Бях сигурен, че SpecifyKind ще SpecifyKind моя dateTimeOffset, като промяна на И ДВЕТЕ отместването на часа и часовата зона, но когато се тества, изглежда просто променя отместването на часовата зона, което е, което искам. Има ли проблем с това?

1 отговор

Проблемът няма нищо общо с базата данни. Ако зададете точка на прекъсване или регистрирате изход някъде, трябва да видите отместването да бъде обвързано малко след този код:

TestDateAndTime = testDateAndTime.DateTime.Date;

Нека го разбием:

  • Започнахте с DateTimeOffset 2008-05-01T08:06:32+01:00
  • След това извикахте .DateTime, което доведе до стойност на DateTime 2008-05-01T08:06:32 с DateTimeKind.Unspecified.
  • След това извикахте .Date, което доведе до стойност на DateTime 2008-05-01T00:00:00 с DateTimeKind.Unspecified.
  • Връщате резултата в testDateAndTime, който е от тип DateTimeOffset. Това причинява имплицитно преобразуване от DateTime в DateTimeOffset който прилага местната часова зона. Във вашия случай изглежда, че отместването за тази стойност във вашата местна часова зона е -04:00, така че получената стойност е DateTimeOffset 2008-05-01T00:00:00-04:00, както описахте,

Ти каза:

Крайната цел е просто да имате дата без отместване на времето или часовата зона.

Е, понастоящем няма собствен C# тип данни, който да е просто дата без час. Има чист тип Date в пакета System.Time в corefxlab, но той не е съвсем готов за типично производствено приложение. Има LocalDate в библиотеката за време на Noda, която можете да използвате днес, но все пак ще трябва да конвертирате обратно към естествен тип, преди да запазите в базата данни. Така че междувременно най-доброто, което можете да направите, е:

  • Променете вашия SQL Server, за да използвате тип дата в това поле.
  • Във вашия .NET код използвайте DateTime с час 00:00:00 и DateTimeKind.Unspecified. Трябва да запомните да игнорирате часовата част (тъй като наистина има дати без местна полунощ в определени часови зони).
  • Променете тестовия сигнал на DateTime, а не на DateTimeOffset.

Като цяло, въпреки че DateTimeOffset е подходящ за голям брой сценарии (като събития с клеймо за време), той не е подходящ за стойности само за дата.

Искам текущата дата с нулево отместване.

Ако наистина го искате като DateTimeOffset, бихте направили:

TestDateAndTime = нов 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. Моля, обърнете внимание на промяната в датите при преобразуването. Това вероятно не е поведение, което бихте искали във вашето приложение.

Проблемът няма нищо общо с базата данни. Ако зададете точка на прекъсване или въведете изход някъде, трябва да можете да видите отместването да се прихваща малко след този код:

TestDateAndTime = testDateAndTime.DateTime.Date;

Нека да го разбием:

  • Започнахте със стойност на DateTimeOffset 2008-05-01T08:06:32+01:00
  • След това извикахте .DateTime, което доведе до стойността DateTime 2008-05-01T08:06:32 с DateTimeKind.Unspecified.
  • След това извикахте .Date, което доведе до стойност на DateTime 2008-05-01T00:00:00 с DateTimeKind.Unspecified.
  • Присвоявате резултата на testDateAndTime, който е от тип DateTimeOffset. Това причинява имплицитно прехвърляне от DateTime към DateTimeOffset - , което се прилага местенЧасова зона. Във вашия случай изглежда, че отместването за тази стойност във вашата местна часова зона е -04:00, така че получената стойност е DateTimeOffset от 2008-05-01T00:00:00-04:00, както описахте.

Ти каза:

Крайната цел е просто да имате дата без отместване на час или часова зона.

Е, има понастоящемне е естествен тип данни на C#, който е просто дата без време. В него има чист тип Date Системно времепакет в corefxlab, но все още не е напълно готов за типично производствено приложение. Има LocalDate в библиотеката Noda Time, която можете да използвате днес, но все пак ще трябва да конвертирате обратно към собствен тип, преди да запазите в базата данни. Така че междувременно най-доброто, което можете да направите, е:

  • Променете вашия SQL Server, за да използвате тип дата в полето.
  • Във вашия .NET код използвайте DateTime с час 00:00:00 и DateTimeKind.Unspecified. Трябва да запомните да игнорирате часовата част (тъй като наистина има дати без местна полунощ в определени часови зони).
  • Променете тестовото предложение да бъде DateTime, а не DateTimeOffset.

Като цяло, докато DateTimeOffset е добре голям бройсценарии (напр. времеви отпечатъцисъбития), не се вписва добре за стойности само за дата.

Искам текущата дата с нулево отместване.

Ако ти наистина искамтова е като DateTimeOffset, можете да направите:

TestDateAndTime = нов 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. Моля, обърнете внимание на промяната в датите при преобразуването. Това вероятно не е поведение, което бихте искали във вашето приложение.

Почти всички проекти срещат проблеми, причинени от неправилно боравене и съхранение на дата и час. Дори ако проектът се използва в същата часова зона, пак можете да получите неприятни изненади след преминаване към зимно/лятно часово време. В същото време малко хора са озадачени от прилагането на правилния механизъм от самото начало, защото изглежда, че не може да има проблеми с това, тъй като всичко е тривиално. За съжаление по-късната реалност показва, че това не е така.

Логично могат да се разграничат следните видове стойности, свързани с дата и час:


Нека разгледаме всяка точка поотделно, без да забравяме.

дата и час

Да приемем, че лабораторията, която е събрала материала за анализ, се намира в +2 часова зона, а централния клон, който следи навременното завършване на анализите, е в +1 зона. Времената, дадени в примера, са отбелязани, когато материалът е събран от първата лаборатория. Възниква въпросът - каква часова цифра трябва да вижда централния офис? Очевидно софтуерът централен офистрябва да покаже 15 януари 2014 г. 12:17:15 - час по-малко, тъй като според часовника им събитието е станало точно в този момент.

Нека разгледаме една от възможните вериги от действия, през които данните преминават от клиента към сървъра и обратно, което ви позволява винаги да показвате правилно датата/часа според текущата часова зона на клиента:

  1. Стойността се създава на клиента, например 2 март 2016 г 15 :13:36, клиентът е в часова зона +2.
  2. Стойността се преобразува в представяне на низ за предаване към сървъра - „2016-03-02T 15 :13:36+02:00”.
  3. Сериализираните данни се изпращат на сървъра.
  4. Сървърът десериализира времето в обект за дата/час, привеждайки го в текущата му часова зона. Например, ако сървърът работи на +1, тогава обектът ще съдържа 2 март 2016 г 14 :13:36.
  5. Сървърът записва данните в базата данни, но не съдържа информация за часовата зона - най-често използваните типове дата/час просто не знаят нищо за това. Така 2 март 2016 г. ще бъде записан в базата данни 14 :13:36 в "неизвестна" часова зона.
  6. Сървърът чете данни от базата данни и създава съответен обект със стойност 2 март 2016 г 14 :13:36. И тъй като сървърът работи в часова зона +1, тази стойност също ще се интерпретира в рамките на същата часова зона.
  7. Стойността се преобразува в представяне на низ за предаване на клиента - „2016-03-02T 14 :13:36+01:00”.
  8. Сериализираните данни се изпращат на клиента.
  9. Клиентът десериализира получената стойност в обект за дата/час, като го преобразува в текущата му часова зона. Например, ако е -5, тогава показаната стойност трябва да бъде 2 март 2016 г 09 :13:36.
Всичко изглежда непокътнато, но нека помислим какво може да се обърка в този процес. Всъщност проблеми тук могат да възникнат на почти всяка стъпка.
  • Часът на клиента може да се генерира изобщо без часова зона - например типът DateTime в .NET с DateTimeKind.Unspecified.
  • Машината за сериализация може да използва формат, който не включва отместване на часовата зона.
  • Когато се десериализира в обект, отместването на часовата зона може да бъде игнорирано, особено в "домашно направени" десериализатори - както на сървъра, така и на клиента.
  • Когато се чете от база данни, обект за дата/час може да се генерира изобщо без часова зона - например типът DateTime в .NET с DateTimeKind.Unspecified. Освен това с DateTime в .NET на практика точно това се случва, ако не укажете изрично друг DateTimeKind веднага след корекцията.
  • Ако сървърите на приложения, работещи с обща база данни, са разположени в различни часови зони, ще има сериозно объркване във времевите отмествания. Стойността за дата/час, записана в базата данни от сървър A и прочетена от сървър B, ще бъде значително различна от същата оригинална стойност, записана от сървър B и прочетена от сървър A.
  • Прехвърлянето на сървъри на приложения от една зона в друга ще доведе до неправилна интерпретация на вече съхранени стойности за дата/час.
Но най-сериозният недостатък в описаната по-горе верига е използването на местна часова зона на сървъра. Ако няма преминаване към лятно/зимно часово време, значи не допълнителни проблеминяма да бъде. Но в противен случай можете да получите много неприятни изненади.

Правилата за преминаване към лятно/зимно часово време са, строго погледнато, променливи. Различните държави могат да променят правилата си от време на време и тези промени трябва да бъдат вградени в системните актуализации доста по-рано. На практика многократно са възниквали ситуации неправилна работана този механизъм, които в крайна сметка бяха разрешени чрез инсталиране на актуални корекции или операционна система, или използвани библиотеки на трети страни. Вероятността едни и същи проблеми да се повтарят не е нулева, така че е по-добре да имате начин да гарантирате, че ще бъдат избегнати.

Като вземем предвид описаните по-горе съображения, ще формулираме най-надеждния и прост подход за предаване и съхраняване на време: на сървъра и в базата данни всички стойности трябва да бъдат преобразувани в часовата зона UTC.

Нека да видим какво ни дава това правило:

  • Когато изпраща данни към сървъра, клиентът трябва да предаде отместването на часовата зона, така че сървърът да може правилно да конвертира времето в UTC. Алтернативен вариант е да принудите клиента да направи това преобразуване, но първият вариант е по-гъвкав. Когато получава данни обратно от сървъра, клиентът ще преобразува датата и часа в местната часова зона, като знае, че във всеки случай ще получи часа в UTC.
  • Няма преходи между лятно и зимно време в UTC, така че проблемите, свързани с това, няма да бъдат от значение.
  • На сървъра, когато четете от базата данни, не е необходимо да преобразувате времеви стойности, просто трябва изрично да посочите, че съответства на UTC. В .NET, например, това може да се постигне чрез задаване на DateTimeKind на часовия обект на DateTimeKind.Utc.
  • Разликата в часовите зони между сървърите, работещи с обща база данни, както и прехвърлянето на сървъри от една зона в друга, по никакъв начин няма да повлияе на коректността на получените данни.
За да приложите такова правило, е достатъчно да се погрижите за три неща:
  1. Направете механизма за сериализация и десериализация така, че стойностите за дата/час да се превеждат правилно от UTC в местната часова зона и обратно.
  2. Уверете се, че десериализаторът от страна на сървъра създава обекти за дата/час в UTC.
  3. Уверете се, че когато четете от базата данни, обектите за дата/час се създават в UTC. Този елемент понякога се предоставя без промени в кода - просто системната часова зона на всички сървъри е зададена на UTC.
Горните съображения и препоръки работят чудесно когато се комбинират две условия:
  • Системните изисквания не изискват местното време и/или отместването на часовата зона да се показват точно както са били съхранени. Например самолетните билети трябва да отпечатват часовете на заминаване и пристигане в часовата зона, съответстваща на местоположението на летището. Или ако сървърът изпраща фактури за отпечатване, създадени в различни страни, всеки трябва да завърши с местно време, а не да се преобразува в часовата зона на сървъра.
  • Всички стойности на дата и час в системата са "абсолютни" - т.е. описват момент от времето в бъдещето или миналото, който съответства на една стойност в UTC. Например „изстрелването на ракетата се проведе в 23:00 часа киевско време“ или „срещата ще се състои от 13:30 до 14:30 часа време в Минск“. Числата за тези събития ще бъдат различни в различните часови зони, но ще описват една и съща точка във времето. Но може да се случи изискванията за софтуерпредполагат "относително" местно време за някои случаи. Например „тази телевизионна програма ще се излъчва от 9:00 до 10:00 сутринта във всяка страна, където има филиал на телевизионен канал.“ Оказва се, че излъчването на една програма не е едно събитие, а няколко и потенциално всички те могат да се случат в различни периоди от време в „абсолютен“ мащаб.
За случаите, когато първото условие е нарушено, проблемът може да бъде решен чрез използване на типове данни, съдържащи часовата зона - както на сървъра, така и в базата данни. По-долу е даден малък списък с примери за различни платформи и СУБД.
.NET DateTimeOffset
Java org.joda.time.DateTime, java.time.ZonedDateTime
MS SQL datetimeoffset
Oracle, PostgreSQL ЧАСОВ ПЕЧАТ С ЧАСОВА ЗОНА
MySQL

Нарушаването на второто условие е по-сложен случай. Ако това „относително“ време трябва да се съхрани просто за показване и няма задача да се определи „абсолютният“ момент във времето, когато събитието е настъпило или ще се случи за дадена часова зона, достатъчно е просто да деактивирате преобразуването на времето. Например, потребителят е въвел старта на програмата за всички клонове на телевизионната компания на 25 март 2016 г. в 9:00 часа и тя ще бъде предавана, съхранявана и показвана в този вид. Но може да се случи някой планировчик да извърши автоматично специални действия един час преди началото на всяка програма (да изпрати известия или да провери наличието на някои данни в базата данни на телевизионната компания). Надеждното внедряване на такъв планировчик не е тривиална задача. Да приемем, че планировчикът знае в коя часова зона е всеки клон. И една от страните, където има клон, решава да промени часовата зона след известно време. Случаят не е толкова рядък, колкото може да изглежда - през тази и предходните две години преброих над 10 подобни събития (http://www.timeanddate.com/news/time/). Оказва се, че или потребителите трябва да поддържат обвързванията на часовите зони актуални, или плановият трябва автоматично да вземе тази информация от глобални източници, като API на Google Maps Time Zone. Не се наемам да предлагам универсално решение за подобни случаи, просто ще отбележа, че такива ситуации изискват сериозно проучване.

Както се вижда от горното, няма единен подход, който да покрива 100% от случаите. Следователно, първо трябва ясно да разберете от изискванията коя от горепосочените ситуации ще възникне във вашата система. Най-вероятно всичко ще бъде ограничено до първия предложен подход със съхранение в UTC. Е, описаните изключителни ситуации не го отменят, а просто добавят други решения за специални случаи.

Дата без време

Да кажем с правилен дисплейдатите и часовете, като се вземе предвид часовата зона на клиента, бяха сортирани. Да преминем към дати без време и примера, даден за случая в началото – „новият договор влиза в сила от 02.02.2016 г.“. Какво ще се случи, ако се използват същите типове и същият механизъм за такива стойности като за „обикновени“ дати и часове?

Не всички платформи, езици и СУБД имат типове само за дата. Например в .NET има само тип DateTime, няма отделен тип „само дата“. Дори и да е зададена само дата при създаването на такъв обект, часът все още присъства и е равен на 00:00:00. Ако прехвърлим стойността „2 февруари 2016 00:00:00“ от зона с отместване от +2 до +1, получаваме „1 февруари 2016 23:00:00“. За примера по-горе това би било еквивалентно на новия договор, който започва на 2 февруари в едната часова зона и на 1 февруари в другата. От правна гледна точка това е абсурдно и, разбира се, не трябва да е така. Общо правилоза „чистите“ дати е изключително просто - такива стойности не трябва да се преобразуват на нито една стъпка от записване и четене.

Има няколко начина да избегнете конвертирането за дати:

  • Ако платформата поддържа тип, който представлява дата без час, тогава това трябва да се използва.
  • Добавете специален атрибут към метаданните на обекта, който ще каже на сериализатора, че за дадена стойностчасовата зона трябва да се игнорира.
  • Предайте датата от клиента и обратно като низ и я запазете като дата. Този подход е неудобен, ако трябва не само да покажете датата на клиента, но и да извършите някои операции върху нея: сравнение, изваждане и др.
  • Предавайте и съхранявайте като низ и конвертирайте в дата само за форматиране въз основа на регионалните настройки на клиента. Той има още повече недостатъци от предишния вариант - например, ако части от датата в съхранения низ не са в реда "година, месец, ден", тогава ще бъде невъзможно да се направи ефективно индексирано търсене по диапазон от дати.
Можете, разбира се, да дадете контрапример и да кажете, че договорът има смисъл само в рамките на страната, в която е сключен; страната е в същата часова зона и следователно моментът, в който влиза в сила, може да бъде недвусмислено определен. Но дори и в този случай потребителите от други часови зони няма да се интересуват в кой момент от тяхното местно време ще се случи това събитие. И дори да е необходимо да се покаже този момент във времето, тогава той ще трябва да покаже не само датата, но и часа, което противоречи на първоначалното състояние.

Времеви интервал

Със съхранението и обработката на времеви интервали всичко е просто: тяхната стойност не зависи от часовата зона, така че тук няма специални препоръки. Те могат да се съхраняват и предават като брой времеви единици (цяло число или с плаваща запетая, в зависимост от изискваната точност). Ако втората точност е важна, тогава като брой секунди, ако е важна точността от милисекунди, тогава като брой милисекунди и т.н.

Но изчисляването на интервала може да има клопки. Да кажем, че имаме някакъв примерен C# код, който изчислява интервала от време между две събития:

DateTime start = DateTime.Now; //... DateTime край = DateTime.Now; двойни часове = (край - начало).TotalHours;
На пръв поглед тук няма проблеми, но това не е така. Първо, може да има проблеми с модулното тестване на такъв код, но ще говорим за това малко по-късно. Второ, нека си представим, че началният момент от времето падна на зимното часово време, а крайният момент падна на лятното часово време (например, така се измерва броят на работните часове, а работниците имат нощна смяна).

Да приемем, че кодът работи в часова зона, в която лятното часово време през 2016 г. настъпва в нощта на 27 март, и симулираме описаната по-горе ситуация:

DateTime start = DateTime.Parse("2016-03-26T20:00:15+02"); DateTime end = DateTime.Parse("2016-03-27T05:00:15+03"); двойни часове = (край - начало).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(); двойни часове = (край - начало).TotalHours;
Оттук и заключението - всякакви аритметични операцииТрябва да се справите с датата и часа, като използвате UTC стойности или типове, които съхраняват информация за часовата зона. И след това прехвърлете обратно към локалния, ако е необходимо. От тази гледна точка оригиналният пример може лесно да бъде коригиран чрез промяна на DateTime.Now на DateTime.UtcNow.

Този нюанс не зависи от конкретна платформа или език. Ето подобен код в Java, който има същия проблем:

Начало на LocalDateTime = LocalDateTime.now(); //... LocalDateTime end = LocalDateTime.now(); дълги часове = ChronoUnit.HOURS.between(начало, край);
Също така може лесно да се коригира - например чрез използване на ZonedDateTime вместо LocalDateTime.

График на планираните събития

Планирането на планирани събития е по-сложна ситуация. Няма универсален тип, който ви позволява да съхранявате графици в стандартни библиотеки. Но такава задача не възниква много рядко, така че готови решенияможе да се намери без проблеми. Добър пример е форматът на планировчика на cron, който се използва под една или друга форма от други решения, като например Quartz: http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html. Той покрива почти всички нужди за планиране, включително опции като „втори петък от месеца“.

В повечето случаи няма смисъл да пишете свой собствен планировчик, тъй като има гъвкави, изпитани във времето решения, но ако по някаква причина има нужда да създадете свой собствен механизъм, тогава поне форматът на графика може да бъде заимстван от cron.

В допълнение към препоръките, описани по-горе относно съхранението и обработката на различни видове времеви стойности, има няколко други, които също бих искал да спомена.

Първо, относно използването на статични членове на класа за получаване на текущия час - DateTime.UtcNow, ZonedDateTime.now() и т.н. Както беше казано, използването им директно в кода може сериозно да усложни модулното тестване, тъй като без специални подигравателни рамки можете да замените текущо временяма да работи. Следователно, ако планирате да пишете модулни тестове, трябва да се уверите, че внедряването на такива методи може да бъде заменено. Има поне два начина за решаване на този проблем:

  • Осигурете интерфейс IDateTimeProvider с един метод, който връща текущия час. След това добавете зависимост към този интерфейс във всички кодови единици, където трябва да получите текущото време. По време на нормално изпълнение на програмата, изпълнението „по подразбиране“ ще бъде инжектирано във всички тези места, което връща реалното текущо време, а при тестовете на единици - всяко друго необходимо изпълнение. Този метод е най-гъвкавият от гледна точка на тестване.
  • Направете свой собствен статичен клас с метод за получаване на текущото време и възможност за инсталиране на всяка реализация на този метод отвън. Например, в случай на C# код, този клас може да изложи свойството UtcNow и метода SetImplementation(Func) impl). Използване статично свойствоили метод за получаване на текущото време елиминира необходимостта от изрично указване на зависимост от допълнителен интерфейс навсякъде, но от гледна точка на принципите на ООП това не е идеалното решение. Въпреки това, ако по някаква причина предишната опция не е подходяща, тогава можете да използвате тази.
Допълнителен проблем, на който трябва да се обърне внимание при мигриране към текущата ви реализация на доставчик на време, е да се уверите, че никой не продължава да използва стандартни класове по „старомодния начин“. Тази задача е лесна за решаване в повечето системи за контрол на качеството на кода. По същество това се свежда до търсене на „нежелан“ подниз във всички файлове, с изключение на този, където е декларирано изпълнението „по подразбиране“.

Второто предупреждение за получаване на текущото време е това на клиента не може да се вярва. Текущото време на компютрите на потребителите може да бъде много различно от реалното и ако има логика, свързана с него, тогава тази разлика може да съсипе всичко. Всички места, където е необходимо да се получи текущото време, трябва, ако е възможно, да се извършват от страната на сървъра. И както бе споменато по-рано, всички аритметични операции с времето трябва да се извършват или в UTC стойности, или с помощта на типове, които съхраняват отместването на часовата зона.

И още нещо, което исках да спомена, е стандартът ISO 8601, който описва формата за дата и час за обмен на информация. По-специално низовото представяне на дата и час, използвани в сериализацията, трябва да отговаря на този стандарт, за да се предотвратят потенциални проблеми със съвместимостта. На практика изключително рядко се налага сами да реализирате форматирането, така че самият стандарт може да бъде полезен главно за информационни цели.

Тагове: Добавете тагове