S nulovým posunom dátumu. Správne spracovanie dátumu a času

Táto možnosť je humánna ([na zobrazenie odkazu sa musíte zaregistrovať]).

Podstatou problému je, čo .. Ak ste omylom nasadili databázu na serveri SQL s „posunom dátumu“ 0, potom nastal problém, keď sa v databáze vyskytne atribút s typom TIME, tj. 01.01.0001 10 : 30: 00 alebo dátum je nastavený v tomto atribúte registrovaný prázdny 01.01.0001 00:00:00. Pri zaznamenávaní takého atribútu sa nezaznamenáva.
Na internete navrhujú vytvoriť novú základňu s ofsetom 2000.
Ale veľmi som nechcel vytvoriť novú základňu. A zmeňte cestu k databáze pre všetkých používateľov.
Potom som nasledoval cestu a kde je táto hodnota uložená v SQL. Našiel sa a zmenil sa na 2000 a všetko bolo v poriadku.
A teraz krok za krokom, kde sa zmeniť.

Vyhoďte všetkých používateľov.

POZOR !!!

1. Najprv urobte záloha pomocou 1C t.j. zahoďte to do * .dt
To musíte urobiť pred zmenou „offsetu“
Ak sa tak nestane, potom v celej vašej databáze, odporúčaniach, dokumentoch atď.
kde sú rekvizity, dátum bude napríklad 02.10.0009
ČO NIE JE PRÍPUSTNÉ….
Odovzdali ste teda súbor * .dt

2. Prejdite na SQL Server Management Studio
Nájdite v zozname svoju základňu, kliknite na znamienko plus.
Nájdite tam ocka „Tabuľky“ a otvorte ho.
Otvorí sa veľa stolov, prejdite na úplné dno a nájdite stôl
_RokOffset, stojíme na ňom a pravým tlačidlom vyberáme položku „Otvorená tabuľka“, pozri obr.
Zmeňte hodnotu z 0 na 2000
Zatvorte SQL Server Management Studio

3. Prejdite do konfigurátora a načítajte predtým uloženú databázu.

Ak sa tak nestane, všetky dátumy budú s rokom 0009.
Po načítaní databázy ... Môžete prejsť na 1C a uistiť sa, že dátumy sú normálne.
Výsledkom je, že sme zmenili „posunutie dátumu z 0 na 2000“

Niekedy sa stane, že túto možnosť nemožno použiť z jedného alebo iného dôvodu. Potom existuje tvrdšia možnosť ([na zobrazenie odkazu sa musíte zaregistrovať]):

Deklarujte kurzor TablesAndFields pre

VYBERTE objekty.názov ako názov tabuľky, stĺpce.názov ako názov stĺpca
Z dbo.sysobjects ako predmety
vľavo spojiť dbo.syscolumns ako stĺpce na objekty.id = columns.id
kde objects.xtype = "U" a stĺpce.xtype = 61

otvoriť TablesAndFields

WHILE @@ FETCH_STATUS = 0
BEGIN Exec (" aktualizovať "+ @TableName + "
nastaviť " + @ColumnName + " = ""2000- 01- 01 00:00:00" "
kde " + @ColumnName + " > ""3999- 12- 31 23:59:59" "")

Toto sa vykonáva, pokiaľ je predchádzajúce načítanie úspešné.
Načítať NEXT Z TablesAndFields do @TableName, @ColumnName
KONIEC

zavrieť TablesAndFields
deallocate TablesAndFields
choď

Pred každou manipuláciou si nezabudnite vytvoriť kópie databáz!

Takmer všetky projekty čelia problémom spôsobeným nesprávnym spracovaním a uchovávaním dátumu a času. Aj keď sa projekt používa v rovnakom časovom pásme, po prechode na zimný / letný čas vás môžu stále čakať nepríjemné prekvapenia. Zároveň si málokto láme hlavu nad implementáciou správneho mechanizmu od začiatku, pretože sa zdá, že s tým nemôžu byť žiadne problémy, pretože všetko je triviálne. Neskoršia realita bohužiaľ ukazuje, že to tak nie je.

Logicky je možné rozlíšiť nasledujúce typy hodnôt vzťahujúcich sa na dátum a čas:


Uvažujme každú položku osobitne, pričom nesmieme zabudnúť.

dátum a čas

Povedzme, že laboratórium, ktoré zbieralo materiál na analýzu, sa nachádza v časovom pásme +2 a centrálna pobočka, ktorá monitoruje včasné vykonanie analýz, sa nachádza v časovom pásme +1. Čas uvedený v príklade bol zaznamenaný počas zberu materiálu prvým laboratóriom. Vynára sa otázka - koľko času by mala centrálna kancelária vidieť? Je zrejmé, že softvér centrálnej kancelárie by mal ukázať 15. januára 2014 12:17:15 - o hodinu menej, pretože podľa ich hodín k udalosti došlo v tom okamihu.

Zvážte jeden z možných reťazcov akcií, ktorými údaje prechádzajú z klienta na server a naopak, čo vám umožní vždy správne zobraziť dátum / čas podľa aktuálneho časového pásma klienta:

  1. Hodnota sa vytvorí na klientovi, napríklad 2. marca 2016 15 : 13: 36, klient je v èasovom pásme +2.
  2. Hodnota sa skonvertuje na reťazcovú reprezentáciu na prenos na server-„2016-03-02T 15 :13:36+02:00”.
  3. Serializované údaje sú odoslané na server.
  4. Server deserializuje čas na objekt dátumu a času a prevedie ho na aktuálne časové pásmo. Ak napríklad server funguje na +1, objekt bude obsahovať 2. marca 2016 14 :13:36.
  5. Server uloží údaje do databázy, pričom neobsahuje žiadne informácie o časovom pásme - najbežnejšie používané typy dátumu a času o ňom jednoducho nič nevedia. Databáza bude teda 2. marca 2016 uložená 14 : 13: 36 v "neznámom" èasovom pásme.
  6. Server načíta údaje z databázy a vytvorí zodpovedajúci objekt s hodnotou 2. marca 2016 14 : 13: 36. A keďže server pracuje v časovom pásme +1, bude táto hodnota interpretovaná aj v rámci rovnakého časového pásma.
  7. Hodnota sa prevedie na reťazcovú reprezentáciu na prenos klientovi-„2016-03-02T 14 :13:36+01:00”.
  8. Serializované údaje sú odoslané klientovi.
  9. Klient deserializuje prijatú hodnotu na objekt dátumu / času a uvedie ju do aktuálneho časového pásma. Ak je napríklad -5, potom by mala byť zobrazená hodnota 2. marca 2016 09 :13:36.
Zdá sa, že je všetko holistické, ale zamyslime sa nad tým, čo sa môže v tomto procese pokaziť. V skutočnosti sa tu problémy môžu vyskytnúť takmer na každom kroku.
  • Čas na klientovi je možné generovať bez časového pásma - napríklad typ DateTime v .NET s DateTimeKind.Unspecified.
  • Serializačný stroj môže používať formát, ktorý nezahŕňa posun časového pásma.
  • Pri deserializácii na objekt je možné ignorovať posun časového pásma, najmä pri „domácich“ deserializátoroch - na serveri aj na klientovi.
  • Pri čítaní z databázy môže byť objekt dátumu / času vytvorený bez časového pásma - napríklad typ DateTime v .NET s DateTimeKind.Unspecified. Navyše, v praxi sa to presne deje s DateTime v .NET, ak bezprostredne po korektúre neurčíte výslovne iný DateTimeKind.
  • Ak sú aplikačné servery pracujúce so zdieľanou databázou v rôznych časových pásmach, dôjde k vážnemu zmätku v súvislosti s časovými posunmi. Hodnota dátumu / času zapísaná do databázy serverom A a prečítaná serverom B sa bude výrazne líšiť od pôvodnej hodnoty zapísanej serverom B a načítanej serverom A.
  • Presun aplikačných serverov z jednej zóny do druhej bude mať za následok nesprávnu interpretáciu už uložených hodnôt dátumu / času.
Ale najvážnejšou chybou v reťazci popísanom vyššie je použitie miestneho časového pásma na serveri. Ak nedôjde k prechodu na letný čas, nebudú žiadne ďalšie problémy. Ale inak môžete získať veľa nepríjemných prekvapení.

Pravidlá prechodu na letný / zimný čas sú, striktne povedané, variabilné. Rôzne krajiny môžu niekedy zmeniť svoje pravidlá a tieto zmeny by mali byť vopred začlenené do aktualizácií systému. V praxi sme sa opakovane stretávali so situáciami nesprávneho fungovania tohto mechanizmu, ktoré boli v dôsledku toho vyriešené inštaláciou rýchlych opráv resp. operačný systém alebo použité knižnice tretích strán... Pravdepodobnosť opakovania rovnakých problémov nie je nulová, preto je lepšie mať zaručený spôsob, ako sa im vyhnúť.

Berúc do úvahy vyššie popísané úvahy, sformulujeme najspoľahlivejší a najjednoduchší prístup k prenosu a uchovávaniu času: na serveri a v databáze musia byť všetky hodnoty uvedené do časového pásma UTC.

Zvážte, čo nám toto pravidlo dáva:

  • Pri odosielaní údajov na server musí klient prejsť posunom časového pásma, aby server mohol správne previesť čas na UTC. Alternatívna možnosť- zaviazať klienta k tejto transformácii, ale prvá možnosť je flexibilnejšia. Pri prijímaní údajov späť zo servera klient preloží dátum a čas do svojho miestneho časového pásma s vedomím, že v každom prípade príde na čas v UTC.
  • V UTC neexistujú žiadne prechody na letný a zimný čas, a preto problémy s tým spojené nebudú relevantné.
  • Na serveri pri čítaní z databázy nemusíte prevádzať časové hodnoty, stačí, keď výslovne uvediete, že zodpovedá UTC. V NET to možno napríklad dosiahnuť nastavením DateTimeKind pre časový objekt na DateTimeKind.Utc.
  • Rozdiel v časových pásmach medzi servermi pracujúcimi so spoločnou databázou, ako aj prenos serverov z jednej zóny do druhej, nijako neovplyvní správnosť prijatých údajov.
Na implementáciu takéhoto pravidla stačí postarať sa o tri veci:
  1. Vykonajte mechanizmus serializácie a deserializácie tak, aby boli hodnoty dátumu / času správne preložené z UTC do miestneho časového pásma a späť.
  2. Uistite sa, že deserializátor na strane servera vytvára objekty dátumu / času v UTC.
  3. Uistite sa, že pri čítaní z databázy sú objekty dátumu / času vytvárané v UTC. Táto klauzula je niekedy poskytovaná bez zmeny kódu - systémové časové pásmo je na všetkých serveroch nastavené na UTC.
Vyššie uvedené úvahy a pokyny fungujú skvele s kombináciou dvoch podmienok:
  • Systémové požiadavky nemusia zobrazovať posun miestneho času a / alebo časového pásma presne tak, ako boli uložené. Napríklad na letenkách musia byť časy odletov a príletov vytlačené v časovom pásme zodpovedajúcom umiestneniu letiska. Alebo ak server odošle na tlač faktúry vygenerované v rôznych krajinách, každá by mala skončiť s miestnym časom, nie skonvertovaným na časové pásmo servera.
  • Všetky hodnoty dátumu a času v systéme sú „absolútne“ - to znamená. popísať časový okamih v budúcnosti alebo minulosti, ktorý zodpovedá jednej hodnote v UTC. Napríklad „štart nosnej rakety sa uskutočnil o 23:00 kyjevského času“ alebo „schôdza sa bude konať od 13:30 do 14:30 minského času“. V rôznych časových pásmach budú čísla týchto udalostí odlišné, ale budú popisovať rovnaký časový okamih. Ale môže sa stať, že požiadavky na softvér v niektorých prípadoch znamenajú „relatívny“ miestny čas. Napríklad „táto televízna show bude prebiehať od 9:00 do 10:00 v každej krajine, kde je pobočka televízneho kanála“. Ukazuje sa, že ukážka programu nie je jedna udalosť, ale niekoľko, a potenciálne všetky z nich sa môžu vyskytnúť v rôznych časových intervaloch na „absolútnej“ stupnici.
V prípadoch, keď je porušená prvá podmienka, je možné problém vyriešiť použitím dátových typov obsahujúcich časové pásmo - na serveri aj v databáze. Nasleduje malý zoznam príkladov pre rôzne platformy a DBMS.
.NET DateTimeOffset
Java org.joda.time.DateTime, java.time.ZonedDateTime
MS SQL posun dátumu a času
Oracle, PostgreSQL TIMESTAMP S ČASOVOU ZÓNOU
MySQL

Porušenie druhej podmienky je ťažším prípadom. Ak je potrebné tento „relatívny“ čas uložiť jednoducho na zobrazenie a neexistuje žiadna úloha na určenie „absolútneho“ momentu v čase, keď pre určité časové pásmo nastala alebo nastane udalosť, stačí jednoducho zakázať prevod času . Používateľ napríklad zadal začiatok vysielania pre všetky pobočky televíznej spoločnosti 25. marca 2016 o 9:00 a v tomto formáte sa bude prenášať, ukladať a zobrazovať. Ale môže sa stať, že nejaký plánovač musí jednu hodinu pred začiatkom každého programu automaticky vykonať špeciálne akcie (odoslať oznámenia alebo skontrolovať prítomnosť niektorých údajov v databáze televíznej spoločnosti). Spoľahlivá implementácia takéhoto plánovača nie je triviálna úloha. Povedzme, že plánovač vie, v akom časovom pásme sa jednotlivé vetvy nachádzajú. A jedna z krajín, kde je pobočka, sa po chvíli rozhodne zmeniť časové pásmo. Prípad nie je taký vzácny, ako by sa mohlo zdať - za tento a predchádzajúce dva roky som napočítal viac ako 10 takýchto udalostí (http://www.timeanddate.com/news/time/). Ukazuje sa, že buď používatelia musia udržiavať svoje väzby časových pásiem aktuálne, alebo plánovač musí tieto informácie automaticky prevziať z globálnych zdrojov, ako napríklad Google MapyČasové pásmo API. Nezaväzujem sa ponúkať univerzálne riešenie pre takéto prípady, len poznamenávam, že takéto situácie vyžadujú serióznu štúdiu.

Ako vidíte z vyššie uvedeného, neexistuje jediný prístup, ktorý by pokrýval 100% prípadov... Preto najskôr musíte z požiadaviek jasne pochopiť, ktorá z vyššie uvedených situácií bude vo vašom systéme. S najväčšou pravdepodobnosťou bude všetko obmedzené na prvý navrhovaný prístup s ukladaním v UTC. Opísané výnimočné situácie to však nezrušia, ale jednoducho pridajú ďalšie riešenia pre špeciálne prípady.

Dátum bez času

Povedzme, že sme prišli na správne zobrazenie dátumu a času s prihliadnutím na časové pásmo klienta. Prejdeme k dátumom bez času a príkladu uvedenému pre tento prípad na začiatku - „nová zmluva nadobúda platnosť 2. februára 2016“. Čo sa stane, ak pre hodnoty použijete rovnaké typy a rovnaký mechanizmus ako pre „bežné“ dátumy s časmi?

Nie všetky platformy, jazyky a DBMS majú typy, ktoré ukladajú iba dátum. Napríklad v .NET existuje iba typ DateTime, neexistuje žiadny samostatný „iba dátum“. Aj keď bol pri vytváraní takéhoto objektu určený iba dátum, čas je stále prítomný a rovná sa 00:00:00. Ak preložíme hodnotu „2. február 2016 00:00:00“ zo zóny s posunom +2 do +1, dostaneme „1. február 2016 23:00:00“. Pre vyššie uvedený príklad by to zodpovedalo skutočnosti, že v jednom časovom pásme začne nová zmluva 2. februára a v inom - 1. februára. Z právneho hľadiska je to absurdné a samozrejme by to tak nemalo byť. Všeobecné pravidlo pre „čisté“ dátumy je veľmi jednoduché - takéto hodnoty by sa nemali prevádzať v žiadnom kroku ukladania a čítania.

Existuje niekoľko spôsobov, ako sa vyhnúť konverzii pre dátumy:

  • Ak platforma podporuje typ, ktorý predstavuje dátum bez času, mal by sa použiť.
  • Pridajte do metadát objektov špeciálnu funkciu, ktorá serializátoru povie, že pre danú hodnotučasové pásmo by sa malo ignorovať.
  • Odošlite dátum od klienta a späť ako reťazec a uložte ho ako dátum. Tento prístup je nepohodlný, ak klient potrebuje nielen zobraziť dátum, ale vykonať s ním aj niektoré operácie: porovnanie, odčítanie atď.
  • Odošlite a uložte ako reťazec a konvertujte na dátum iba na formátovanie miestneho nastavenia klienta. Má ešte viac nevýhod ako predchádzajúca možnosť - napríklad, ak časti dátumu v uloženom reťazci nie sú v poradí „rok, mesiac, deň“, potom nebude možné efektívne indexované vyhľadávanie podľa dátumu rozsah.
Môžete sa samozrejme pokúsiť uviesť protipriklad a povedať, že zmluva má zmysel iba v rámci krajiny, v ktorej je uzavretá, krajina sa nachádza v rovnakom časovom pásme, a preto je možné jednoznačne určiť okamih jej platnosti. nadobudnutie účinnosti. Ale ani v tomto prípade používateľov z iných časových pásiem nebude zaujímať, v ktorom bode ich miestneho času sa táto udalosť vyskytne. A aj keby bola potreba ukázať tento moment v čase, potom by musel byť zobrazený nielen dátum, ale aj čas, čo je v rozpore s pôvodným stavom.

Časový interval

S ukladaním a spracovaním časových intervalov je všetko jednoduché: ich hodnota nezávisí od časového pásma, takže tu neexistujú žiadne špeciálne odporúčania. Môžu byť uložené a prenášané ako niekoľko časových jednotiek (celočíselné alebo s pohyblivou rádovou čiarkou, v závislosti od požadovanej presnosti). Ak je dôležitá druhá presnosť - potom ako počet sekúnd, ak milisekundy - potom ako počet milisekúnd atď.

Výpočet intervalu však môže mať úskalia. Predpokladajme, že máme nejaký ukážkový C # kód, ktorý počíta časový interval medzi dvoma udalosťami:

DateTime start = DateTime.Now; // ... DateTime end = DateTime.Now; dvojité hodiny = (koniec - začiatok) .Celkové hodiny;
Na prvý pohľad tu nie sú žiadne problémy, ale nie je tomu tak. Po prvé, môžu existovať problémy s jednotkovým testovaním takéhoto kódu, ale o tom si povieme trochu neskôr. Za druhé, predstavme si, že počiatočný časový okamih pripadol na zimný čas a konečný okamih na letný čas (napríklad sa takto meria počet pracovných hodín a pracovníci majú nočnú smenu).

Predpokladajme, že kód funguje v časovom pásme, v ktorom dochádza k letnému času v roku 2016 v noci 27. marca, a simulujme situáciu opísanú vyššie:

DateTime start = DateTime.Parse ("2016-03-26T20: 00: 15 + 02"); DateTime end = DateTime.Parse ("2016-03-27T05: 00: 15 + 03"); dvojité hodiny = (koniec - začiatok) .Celkové hodiny;
Tento kód bude mať za následok 9 hodín, aj keď v skutočnosti medzi týmito okamihmi uplynulo 8 hodín. Môžete to ľahko overiť zmenou kódu takto:

DateTime start = DateTime.Parse ("2016-03-26T20: 00: 15 + 02"). ToUniversalTime (); DateTime end = DateTime.Parse ("2016-03-27T05: 00: 15 + 03"). ToUniversalTime (); dvojité hodiny = (koniec - začiatok) .Celkové hodiny;
Preto záver - akýkoľvek aritmetické operácie dátum a čas by sa mali vykonať pomocou hodnôt UTC alebo typov, ktoré ukladajú informácie o časovom pásme. A potom v prípade potreby preložiť späť do miestneho jazyka. Z tohto hľadiska môže byť pôvodný príklad ľahko opravený zmenou DateTime.Now na DateTime.UtcNow.

Táto nuansa nezávisí od konkrétnej platformy alebo jazyka. Tu je podobný kód Java s rovnakou nevýhodou:

LocalDateTime start = LocalDateTime.now (); // ... LocalDateTime end = LocalDateTime.now (); dlhé hodiny = ChronoUnit.HOURS.medzera (začiatok, koniec);
Je tiež ľahko opraviteľný - napríklad pomocou ZonedDateTime namiesto LocalDateTime.

Naplánujte si naplánované akcie

Plánovanie plánovaných udalostí je zložitejšia situácia. Univerzálny typ na ukladanie plánov do štandardných knižníc neexistuje. Ale taká úloha nevzniká tak zriedka, takže hotové riešenia je možné nájsť bez problémov. Dobrým príkladom je formát plánovača cron, ktorý v tej či onej forme používajú iné riešenia, napríklad Quartz: http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html. Pokrýva takmer všetky potreby plánovania, vrátane možností druhého piatka v mesiaci.

Vo väčšine prípadov nemá zmysel písať si vlastný plánovač, pretože existujú flexibilné časovo testované riešenia, ale ak z nejakého dôvodu existuje potreba vytvoriť si vlastný mechanizmus, je možné si z cronu požičať aspoň formát plánu.

Okrem vyššie popísaných odporúčaní o ukladaní a spracovávaní rôznych typov časových hodnôt existuje ešte niekoľko ďalších, o ktorých by som tiež chcel hovoriť.

Najprv o použití členov statickej triedy na získanie aktuálneho času - DateTime.UtcNow, ZonedDateTime.now () atď. Ako už bolo spomenuté, ich použitie priamo v kóde môže vážne skomplikovať testovanie jednotiek, pretože bez špeciálnych falošných rámcov nie je možné nahradiť aktuálny čas. Preto, ak plánujete písať jednotkové testy, mali by ste sa uistiť, že implementáciu takýchto metód je možné nahradiť. Existujú najmenej dva spôsoby, ako tento problém vyriešiť:

  • Zvýraznite rozhranie IDateTimeProvider jednou metódou, ktorá vráti aktuálny čas. Potom do tohto rozhrania pridajte závislosť vo všetkých jednotkách kódu, kde potrebujete získať aktuálny čas. Pri bežnom vykonávaní programu bude do všetkých týchto miest vložená „predvolená“ implementácia, ktorá vracia skutočný aktuálny čas, a v jednotkových testoch - akákoľvek iná potrebná implementácia. Táto metóda je z hľadiska testovania najflexibilnejšia.
  • Vytvorte si vlastnú statickú triedu s metódou na získanie aktuálneho času a možnosťou nainštalovať akúkoľvek implementáciu tejto metódy zvonku. Napríklad v prípade kódu C # môže táto trieda odhaliť vlastnosť UtcNow a SetImplementation (Func impl). Použitie statickej vlastnosti alebo metódy na získanie aktuálneho času síce eliminuje potrebu explicitne všade písať závislosť na dodatočnom rozhraní, ale z hľadiska princípov OOP to nie je ideálne riešenie. Ak však z nejakého dôvodu predchádzajúca možnosť nie je vhodná, môžete ju použiť.
Ďalším problémom, ktorý by mal byť vyriešený pri prechode na vašu implementáciu súčasného poskytovateľa, je zaistiť, aby nikto „staromódnym spôsobom“ naďalej nepoužíval štandardné triedy. Túto úlohu je ľahké vyriešiť vo väčšine systémov na kontrolu kvality kódu. V podstate sa scvrkáva na hľadanie „nechceného“ podreťazca vo všetkých súboroch okrem toho, v ktorom je deklarovaná „predvolená“ implementácia.

Druhou nuansou získania aktuálneho času je, že klientovi sa nedá veriť... Aktuálny čas na počítačoch používateľov sa môže veľmi líšiť od skutočného, ​​a ak je k tomu viazaná logika, potom tento rozdiel môže všetko pokaziť. Všetky miesta, kde je potrebné zistiť aktuálny čas, by sa mali, pokiaľ je to možné, vykonávať na strane servera. A ako už bolo spomenuté, všetky aritmetické operácie s časom sa musia vykonávať buď v hodnotách UTC, alebo pomocou typov, ktoré ukladajú posun časového pásma.

A ešte jedna vec, ktorú som chcel spomenúť, je norma ISO 8601, ktorá popisuje formát dátumu a času na výmenu informácií. Reťazcová reprezentácia dátumu a času použitého pri serializácii musí predovšetkým zodpovedať tomuto štandardu, aby sa predišlo potenciálnym problémom s kompatibilitou. V praxi je extrémne zriedkavé, že musíte formátovanie implementovať sami, takže samotný štandard môže byť užitočný hlavne na informačné účely.

Značky: Pridajte značky

DateTimeOffset testDateAndTime = nový DateTimeOffset (2008, 5, 1, 8, 6, 32, nový TimeSpan (1, 0, 0)); // ČISTI ČAS A DÁTUM testDateAndTime = testDateAndTime.DateTime.Date; var dataTableEntry = db.DatesTable.First (dt => dt.Id == someTestId); dataTableEntry.test = testDateAndTime; db.SaveChangesAsync ();

VÝSLEDOK V DATABÁZE: 2008-05-01 00: 00: 00.0000000 -04: 00

Ako povoliť -4: 00 až +00: 00 (z kódu pred uložením)?

Skúsil som:

Verejná úloha SetTimeZoneOffsetToZero (DateTimeOffset dateTimeOffSetObj) (TimeSpan zeroOffsetTimeSpan = new TimeSpan (0, 0, 0, 0, 0); return dateTimeOffSetObj.ToOffset (zeroOffsetTimeSpan);)

Nerobí nič.

Konečným cieľom je mať dátum bez posunu času alebo časového pásma. Nechcem prevádzať čas na iné časové pásmo (tj. 00: 00: 00.0000000 Nechcem, aby to odpočítalo 4 hodiny od 00: 00: 00.0000000 času a 00: 00: 00.0000000 posunu nastaveného času o +00: 00, Chcem len, aby bol posun nastavený na +00: 00). Chcem aktuálny dátum s nulový posun.

Upraviť:

Čo môžete navrhnúť inde:

DateTimeOffset testDateAndTime = nový DateTimeOffset (2008, 5, 1, 8, 6, 32, nový TimeSpan (1, 0, 0)); testDateAndTime = testDateAndTime.DateTime.Date; // Čas nulového času testDateAndTime = DateTime.SpecifyKind (testDateAndTime.Date, DateTimeKind.Utc); // Odsadená časť „Nula von“

Bol som si celkom istý, že SpecifyKind bude SpecifyKind môjho dateTimeOffset, napríklad zmení OBOJ časový a časový posun, ale pri testovaní sa zdá, že len zmení offset časového pásma, čo chcem. Je s tym problem?

1 odpoveď

Problém nemá nič spoločné s databázou. Ak ste niekde nastavili bod prerušenia alebo ste zaznamenali výjazd, krátko po tomto kóde by sa vám malo zobraziť posunutie offsetu:

TestDateAndTime = testDateAndTime.DateTime.Date;

Poďme to rozobrať:

  • Začali ste s DateTimeOffset 2008-05-01T08: 06: 32 + 01: 00
  • Potom ste zavolali.DateTime, čo viedlo k hodnote DateTime 2008-05-01T08: 06: 32 s DateTimeKind.Unspecified.
  • Potom ste zavolali. Date, výsledkom čoho bola hodnota DateTime 2008-05-01T00: 00: 00 s DateTimeKind.Unspecified.
  • Výsledok vraciate v testDateAndTime, čo je typ DateTimeOffset. To spôsobí implicitnú konverziu z DateTime na DateTimeOffset ktorý uplatňuje miestne časové pásmo... Vo vašom prípade sa zdá, že posun pre túto hodnotu vo vašom miestnom časovom pásme je -04: 00, takže výsledná hodnota je DateTimeOffset 2008-05-01T00: 00: 00-04: 00, ako ste popísali.

Povedal si:

Konečným cieľom je mať dátum bez posunu času alebo časového pásma.

V súčasnosti neexistuje žiadny natívny dátový typ C #, ktorý by bol iba dátumom bez času. V balíku System.Time v corefxlab je čistý typ dátumu, ale to nie je celkom pripravené na typickú produkčnú aplikáciu. V časovej knižnici Noda je LocalDate, ktoré môžete použiť aj dnes, ale pred uložením do databázy musíte stále previesť späť na pôvodný typ. Zároveň však to najlepšie, čo môžete urobiť, je:

  • Zmeňte svoj SQL Server, aby v tomto poli používal typ dátumu.
  • Vo svojom kóde .NET použite DateTime s časom 00:00:00 a DateTimeKind.Unspecified. Časovú časť musíte ignorovať (pretože v určitých časových pásmach skutočne existujú dátumy bez miestnej polnoci).
  • Zmeňte výstrahu testu na DateTime, nie DateTimeOffset.

Vo všeobecnosti platí, že zatiaľ čo DateTimeOffset je vhodný pre mnoho scenárov (napríklad pre udalosti s časovým označením), nie je vhodný pre hodnoty iba pre dátum.

Chcem aktuálny dátum s nulovým posunom.

Ak to skutočne chcete ako DateTimeOffset, urobili by ste:

TestDateAndTime = new DateTimeOffset (testDateAndTime.Date, TimeSpan.Zero);

Toto však neodporúčam. Pritom beriete miestny dátum pôvodnej hodnoty a tvrdíte, že je v UTC. Ak je pôvodný posun iný ako nula, bude to nepravdivé tvrdenie. Potom to povedie k rôznym chybám, pretože v skutočnosti hovoríte o inom časovom bode (s potenciálne odlišným dátumom), ako je ten, ktorý ste vytvorili.

Pokiaľ ide o dodatočnú otázku položenú vo vašej úprave - zadanie DateTimeKind.Utc, zmení sa správanie implicitného prenášania. Namiesto použitia miestneho časového pásma sa používa čas UTC, ktorý má vždy nulový posun. Výsledok je rovnaký ako explicitnejší pohľad, ktorý som uviedol vyššie. Stále vám to odporúčam z rovnakých dôvodov.

Zoberme si príklad začínajúci 2016-12-31T22: 00: 00-04: 00. Podľa vášho prístupu by ste mali uložiť do databázy 2016-12-31T00: 00: 00 + 00: 00. Ide však o dva rôzne časové body. Prvý, normalizovaný na UTC, bude 2017-01-01T02: 00: 00 + 00: 00, a druhý, prevedený na iné časové pásmo, bude 2016-12-30T20: 00: 00-04: 00 . Všimnite si zmeny dátumov v konverzii. Toto pravdepodobne nie je správanie, ktoré by ste chceli vo svojej aplikácii používať.

Problém nemá nič spoločné s databázou. Ak nastavíte bod prerušenia alebo niekde zadáte výjazd, krátko po zadaní tohto kódu by ste mali vidieť pripnutie offsetu:

TestDateAndTime = testDateAndTime.DateTime.Date;

Poďme to rozobrať:

  • Začali ste s hodnotou DateTimeOffset 2008-05-01T08: 06: 32 + 01: 00
  • Potom ste zavolali DateTime, čo malo za následok hodnotu DateTime 2008-05-01T08: 06: 32 s DateTimeKind.Unspecified.
  • Potom ste zavolali Date, výsledkom čoho bola hodnota DateTime 2008-05-01T00: 00: 00 s DateTimeKind.Unspecified.
  • Výsledok priradíte testDateAndTime, čo je typu DateTimeOffset. Toto volá implicitné prenášanie z DateTime na DateTimeOffset - ktorá platí miestnyČasové pásmo... Vo vašom prípade sa zdá, že posun pre túto hodnotu vo vašom miestnom časovom pásme je -04: 00, takže výsledná hodnota je DateTimeOffset od 2008-05-01T00: 00: 00-04: 00, ako ste popísali.

Povedal si:

Konečným cieľom je mať dátum bez posunu času alebo časového pásma.

No existuje aktuálne nie je natívny dátový typ C #, čo je iba dátum bez času. Existuje čistý typ dátumu v Systémový čas balík v corefxlab, ale ešte nie je celkom pripravený na typickú produkčnú aplikáciu. V knižnici Noda Time je LocalDate, ktoré môžete používať dnes, ale pred uložením do databázy musíte stále previesť späť na pôvodný typ. Zároveň však najlepšie, čo môžete urobiť, je:

  • Zmeňte svoj SQL Server tak, aby používal typ dátumu v poli.
  • Vo svojom kóde .NET použite DateTime s časom 00:00:00 a DateTimeKind.Unspecified. Časovú časť musíte ignorovať (pretože v určitých časových pásmach skutočne existujú dátumy bez miestnej polnoci).
  • Zmeňte test rekvizity na DateTime a nie na DateTimeOffset.

Vo všeobecnosti je DateTimeOffset vhodný pre veľký počet scenárov (napr. časové pečiatky udalosti), nefunguje dobre iba pre hodnoty dátumu.

Chcem aktuálny dátum s nulovým posunom.

Ak ty skutočne chcieť je to ako DateTimeOffset, môžete urobiť:

TestDateAndTime = new DateTimeOffset (testDateAndTime.Date, TimeSpan.Zero);

Neodporúčam to však robiť. Pri tom beriete miestny dátum pôvodnej hodnoty a tvrdiť, že je v UTC. Ak pôvodný offset nie je nič iné ako nula, bude to nepravdivé tvrdenie. Potom to povedie k rôznym chybám, pretože v skutočnosti hovoríte o inom časovom bode (s potenciálne odlišným dátumom), ako je ten, ktorý ste vytvorili.

K ďalšej otázke, ktorú položila vaša rada. Zadaním DateTimeKind.Utc sa zmení správanie implicitného prenášania. Namiesto použitia miestneho časového pásma sa používa čas UTC, ktorý má vždy nulový posun. Výsledok je rovnaký ako explicitnejší pohľad, ktorý som uviedol vyššie. Stále vám to odporúčam z rovnakých dôvodov.

Zoberme si príklad začínajúci 2016-12-31T22: 00: 00-04: 00. Podľa vášho prístupu by ste mali uložiť do databázy 2016-12-31T00: 00: 00 + 00: 00. Ide však o dva rôzne časové body. Prvý, normalizovaný na UTC, bude 2017-01-01T02: 00: 00 + 00: 00, a druhý, prevedený na iné časové pásmo, bude 2016-12-30T20: 00: 00-04: 00 . Všimnite si zmeny dátumov v konverzii. Toto pravdepodobne nie je správanie, ktoré by ste chceli vo svojej aplikácii používať.