Teisingas darbas su data ir laiku. Teisingas darbas su data ir laiku Nulinės datos poslinkis sql serveris

Humaniška parinktis ([norėdami peržiūrėti nuorodą turite užsiregistruoti]).

Problemos esmė tokia.. Jei netyčia įdiegėte duomenų bazę SQL serveryje, kurios „datos poslinkis“ yra 0, tada iškyla problema, kai duomenų bazėje yra atributas, kurio tipas yra TIME, t. y. šis atributas nustatytas į 01. /01/0001 10:30:00 arba registravimo data tuščia 01.01.0001 00:00:00. Įrašant tokias detales, jos neįrašomos.
Internete jie siūlo sukurti naują duomenų bazę su 2000 poslinkiu.
Bet aš tikrai nenorėjau kurti naujos bazės. Ir pakeiskite kelią į duomenų bazę visiems vartotojams.
Tada nuėjau keliu, kur ši reikšmė saugoma SQL. Radau ir pakeičiau į 2000 ir viskas ok..
O dabar žingsnis po žingsnio kur keisti.

Spardyti visus vartotojus.

DĖMESIO!!!

1. Pirmiausia padarykite tai atsarginė kopija naudojant 1C t.y. įkelkite jį į *.dt
Tai turi būti padaryta prieš pakeičiant „offset“
Jei tai nebus padaryta, visoje jūsų duomenų bazėje bus nuorodos, dokumentai ir kt.
kur yra rekvizitas, data bus tarkim 02.10.0009
KAS NEGALIMA...
Taigi jūs įkėlėte į *.dt

2. Eikite į „SQL Server Management Studio“.
Suraskite savo bazę sąraše ir paspauskite pliuso ženklą.
Raskite ten aplanką „Lentelės“ ir atidarykite jį.
Atsidarys krūva lentelių, eik į patį apačią, surask lentelę
_YearOffset, atsistokite ant jo ir dešiniuoju mygtuku pasirinkite elementą „Atidaryti lentelę“, žr. 1 pav.
Pakeiskite reikšmę 0 į 2000
Uždarykite „SQL Server Management Studio“.

3. Eikite į konfigūratorių ir įkelkite anksčiau išsaugotą duomenų bazę.

Jei tai nebus padaryta, visos datos bus su 0009 metais.
Įkėlus duomenų bazę... Galite eiti į 1C ir įsitikinti, kad datos yra normalios.
Rezultatas yra tas, kad mes pakeitėme „datos poslinkį nuo 0 iki 2000“

Kartais atsitinka taip, kad ši parinktis negali būti naudojama dėl vienokių ar kitokių priežasčių. Tada yra sudėtingesnė parinktis ([turite užsiregistruoti, kad pamatytumėte nuorodą]):

Paskelbti TablesAndFields žymeklį

SELECT objects.name kaip lentelės pavadinimas, stulpeliai.pavadinimas kaip stulpelio pavadinimas
IŠ dbo.sysobjects kaip objektų
kairėje sujunkite dbo.syscolumns kaip stulpelius objektuose.id = columns.id
kur objektai.xtipas = "U" ir stulpeliai.xtipas = 61

atidarykite TablesAndFields

WHILE @@FETCH_STATUS = 0
BEGIN Exec(" atnaujinti"+ @Lentelės pavadinimas + "
rinkinys " + @ColumnName + " = ""2000- 01- 01 00:00:00" "
kur" + @ColumnName + " > ""3999- 12- 31 23:59:59" "")

Tai vykdoma tol, kol sėkmingas ankstesnis gavimas.
ATGAUTI KITAS IŠ TablesAnDFields į @TableName, @ColumnName
GALAS

uždarykite TablesAndFields
deallocateTablesAndFields
eik

Prieš atlikdami bet kokias manipuliacijas, nepamirškite pasidaryti duomenų bazių kopijų!

Problema neturi nieko bendra su duomenų baze. Jei nustatysite pertraukos tašką arba kur nors įvesite išvestį, netrukus po šio kodo turėtumėte matyti poslinkį:

TestDateAndTime = testDateAndTime.DateTime.Date;

Išskaidykime:

  • Pradėjote nuo DateTimeOffset vertės 2008-05-01T08:06:32+01:00
  • Tada iškvietėte .DateTime , o tai davė reikšmę DateTime 2008-05-01T08:06:32 su DateTimeKind.Unspecified .
  • Tada paskambinote .Date, todėl DateTime reikšmė buvo 2008-05-01T00:00:00 su DateTimeKind.Unspecified.
  • Rezultatą priskiriate testDateAndTime, kurio tipas yra DateTimeOffset. Tai sukelia netiesioginį perėjimą iš DateTime į DateTimeOffset - , kuris taikomas vietinis Laiko zona. Jūsų atveju atrodo, kad šios vertės poslinkis jūsų vietinėje laiko juostoje yra -04:00 , todėl gauta vertė yra DateTimeOffset 2008-05-01T00:00:00-04:00, kaip apibūdinote.

Tu sakei:

Galutinis tikslas yra tiesiog turėti datą be laiko ar laiko juostos poslinkio.

Na, yra šiuo metu ne vietinis C# duomenų tipas, kuris yra tik data be laiko. Yra grynas datos tipas Sistemos laikas paketą Corefxlab, tačiau jis dar nėra visiškai paruoštas įprastai gamybos programai. „Noda Time“ bibliotekoje yra „LocalDate“, kurią galite naudoti šiandien, tačiau prieš išsaugodami duomenų bazėje vis tiek turėsite konvertuoti į vietinį tipą. Taigi kol kas geriausia, ką galite padaryti:

  • Pakeiskite SQL serverį, kad lauke būtų naudojamas datos tipas.
  • Savo .NET kode naudokite DateTime su laiku 00:00:00 ir DateTimeKind.Unspecified. Turite nepamiršti nepaisyti laiko dalies (nes iš tikrųjų tam tikrose laiko juostose yra datų be vietinio vidurnakčio).
  • Pakeiskite bandymo rekvizitus į DateTime, o ne DateTimeOffset.

Apskritai, nors DateTimeOffset tinka daugeliui scenarijų (pvz., laiko žymesįvykius), jis netinkamai tinka tik datos reikšmėms.

Noriu dabartinė data su nuliniu poslinkiu.

Jei tu labai noriu tai kaip DateTimeOffset , galite padaryti:

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

Tačiau aš nerekomenduoju to daryti. Tai darydami jūs imate vietinis pradinės vertės datą ir teigti, kad ji yra UTC. Jei pradinis poslinkis yra kitoks nei nulis, tai bus klaidingas teiginys. Vėliau tai sukels kitų klaidų, nes iš tikrųjų kalbate apie kitą laiko momentą (galbūt skirtingą datą), nei tas, kurį sukūrėte.

Palyginti papildomas klausimas, nustatykite savo lentoje. Nurodant DateTimeKind.Utc pakeičiama numanomo perdavimo elgsena. Vietoj vietinės laiko juostos naudojamas UTC laikas, kuris visada turi nulinį poslinkį. Rezultatas yra toks pat kaip ir aiškesnis vaizdas, kurį pateikiau aukščiau. Dėl tų pačių priežasčių vis dar rekomenduoju to nedaryti.

Apsvarstykite pavyzdį, prasidedantį 2016-12-31T22:00:00-04:00. Pagal savo požiūrį duomenų bazėje turėtumėte išsaugoti 2016-12-31T00:00:00+00:00. Tačiau tai yra du skirtingi laiko momentai. Pirmasis, normalizuotas pagal UTC, būtų 2017-01-01T02:00:00+00:00, o antrasis, konvertuotas į kitą laiko juostą, būtų 2016-12-30T20:00:00-04:00. Atkreipkite dėmesį į konversijos datų pasikeitimą. Tikriausiai tai nėra elgesys, kurio norėtumėte savo paraiškoje.

Beveik visuose projektuose kyla problemų dėl netinkamo tvarkymo ir datos bei laiko saugojimo. Net jei projektas naudojamas toje pačioje laiko juostoje, perėjus į žiemos/vasaros laiką vis tiek galite sulaukti nemalonių staigmenų. Tuo pačiu metu mažai žmonių glumina teisingo mechanizmo įgyvendinimą nuo pat pradžių, nes atrodo, kad dėl to negali kilti problemų, nes viskas yra nereikšminga. Deja, vėliau realybė rodo, kad taip nėra.

Logiškai mąstant, galima išskirti šiuos su data ir laiku susijusių reikšmių tipus:


Apsvarstykime kiekvieną tašką atskirai, nepamiršdami.

data ir laikas

Tarkime, laboratorija, kuri surinko medžiagą analizei, yra +2 laiko juostoje, o centrinė atšaka, kuri stebi, ar laiku atliekamos analizės, yra +1 zonoje. Pavyzdyje pateikti laikai buvo pažymėti, kai medžiaga buvo surinkta pirmoje laboratorijoje. Kyla klausimas – kokį laiko skaičių turėtų matyti centrinė įstaiga? Akivaizdu, kad programinė įranga centrinis biuras turėtų rodyti 2014 m. sausio 15 d. 12:17:15 - valanda mažiau, nes pagal jų laikrodį įvykis įvyko būtent tuo momentu.

Panagrinėkime vieną iš galimų veiksmų grandinių, per kurias duomenys perduodami iš kliento į serverį ir atgal, kas leidžia visada teisingai rodyti datą/laiką pagal esamą kliento laiko juostą:

  1. Vertė sukuriama klientui, pavyzdžiui, 2016 m. kovo 2 d 15 :13:36, klientas yra +2 laiko juostoje.
  2. Reikšmė konvertuojama į eilutės atvaizdą, skirtą perduoti į serverį - „2016-03-02T 15 :13:36+02:00”.
  3. Serializuoti duomenys siunčiami į serverį.
  4. Serveris deserializuoja laiką į datos / laiko objektą, perkeldamas jį į dabartinę laiko juostą. Pavyzdžiui, jei serveris veikia esant +1, tada objekte bus 2016 m. kovo 2 d 14 :13:36.
  5. Serveris išsaugo duomenis į duomenų bazę, tačiau joje nėra jokios informacijos apie laiko juostą – dažniausiai naudojami datos/laiko tipai tiesiog nieko apie tai nežino. Taigi duomenų bazėje bus išsaugota 2016 m. kovo 2 d 14 :13:36 "nezinomoje" laiko juostoje.
  6. Serveris nuskaito duomenis iš duomenų bazės ir sukuria atitinkamą objektą, kurio reikšmė 2016 m. kovo 2 d 14 :13:36. Kadangi serveris veikia +1 laiko juostoje, ši reikšmė taip pat bus interpretuojama toje pačioje laiko juostoje.
  7. Reikšmė konvertuojama į eilutės atvaizdą, kad būtų galima perduoti klientui - „2016-03-02T 14 :13:36+01:00”.
  8. Serializuoti duomenys siunčiami klientui.
  9. Klientas deserializuoja gautą reikšmę į datos / laiko objektą, perkeldamas ją į dabartinę laiko juostą. Pavyzdžiui, jei jis yra -5, tada rodoma reikšmė turėtų būti 2016 m. kovo 2 d 09 :13:36.
Atrodo, kad viskas nepažeista, bet pagalvokime, kas šiame procese gali suklysti. Tiesą sakant, problemų čia gali kilti beveik kiekviename žingsnyje.
  • Kliento laikas gali būti generuojamas visai be laiko juostos, pavyzdžiui, DateTime tipas .NET su DateTimeKind.Unspecified.
  • Serializacijos variklis gali naudoti formatą, kuriame nėra laiko juostos poslinkio.
  • Deserializuojant į objektą, laiko juostos poslinkis gali būti ignoruojamas, ypač „namuose“ deserializatoriuose – tiek serveryje, tiek kliente.
  • Skaitant iš duomenų bazės, datos / laiko objektas gali būti sugeneruotas išvis be laiko juostos – pavyzdžiui, DateTime tipas .NET su DateTimeKind.Unspecified. Be to, naudojant DateTime .NET, praktiškai taip atsitinka, jei iškart po korektūros nenurodote kito DateTimeKind.
  • Jei taikomųjų programų serveriai, dirbantys su bendra duomenų baze, yra skirtingose ​​laiko juostose, gali kilti rimta painiava dėl laiko poslinkių. Serverio A į duomenų bazę įrašyta ir serverio B nuskaityta datos / laiko reikšmė pastebimai skirsis nuo tos pačios pradinės reikšmės, kurią parašė serveris B ir nuskaito serveris A.
  • Perkėlus taikomųjų programų serverius iš vienos zonos į kitą, bus neteisingai interpretuojamos jau išsaugotos datos/laiko reikšmės.
Tačiau rimčiausias aukščiau aprašytos grandinės trūkumas yra vietinės laiko juostos naudojimas serveryje. Jei nėra perėjimo į vasaros/žiemos laiką, tada ne papildomų problemų nebus. Tačiau kitu atveju galite sulaukti daug nemalonių staigmenų.

Griežtai kalbant, perėjimo į vasaros/žiemos laiką taisyklės yra įvairios. Įvairios šalys gali retkarčiais keisti savo taisykles ir šie pakeitimai turi būti įtraukti į sistemos naujinimus iš anksto. Praktikoje ne kartą susidūrėme su situacijomis, kai šis mechanizmas neveikė tinkamai, kurios galiausiai buvo išspręstos įdiegus karštąsias pataisas arba Operacinė sistema, arba naudojo trečiųjų šalių bibliotekas. Tikimybė, kad tos pačios problemos pasikartos, nėra lygi nuliui, todėl geriau turėti būdą, kaip užtikrinti, kad jų būtų išvengta.

Atsižvelgdami į aukščiau aprašytas aplinkybes, suformuluosime patikimiausią ir paprasčiausią laiko perdavimo ir saugojimo būdą: serveryje ir duomenų bazėje visos reikšmės turi būti konvertuotos į UTC laiko juostą.

Pažiūrėkime, ką mums suteikia ši taisyklė:

  • Siųsdamas duomenis į serverį, klientas turi pereiti laiko juostos poslinkį, kad serveris galėtų teisingai konvertuoti laiką į UTC. Alternatyvus variantas— įpareigoti klientą atlikti šią pertvarką, tačiau pirmasis variantas yra lankstesnis. Gavęs duomenis iš serverio, klientas konvertuos datą ir laiką į savo vietos laiko juostą, žinodamas, kad bet kuriuo atveju laiką gaus UTC.
  • UTC nėra perėjimų tarp vasaros ir žiemos laiko, todėl su tuo susijusios problemos nebus aktualios.
  • Serveryje, skaitant iš duomenų bazės, nereikia konvertuoti laiko reikšmių, tiesiog reikia aiškiai nurodyti, kad tai atitinka UTC. Pavyzdžiui, .NET tai galima pasiekti nustačius DateTimeKind laiko objekte į DateTimeKind.Utc.
  • Laiko juostų skirtumas tarp serverių, dirbančių su bendra duomenų baze, taip pat serverių perkėlimas iš vienos zonos į kitą, jokiu būdu neturės įtakos gaunamų duomenų teisingumui.
Norint įgyvendinti tokią taisyklę, pakanka pasirūpinti trimis dalykais:
  1. Padarykite serializavimo ir deserializacijos mechanizmą taip, kad datos / laiko reikšmės būtų teisingai išverstos iš UTC į vietinę laiko juostą ir atgal.
  2. Įsitikinkite, kad serverio deserializatorius sukuria datos / laiko objektus UTC.
  3. Įsitikinkite, kad skaitant iš duomenų bazės datos/laiko objektai sukuriami UTC. Šis elementas kartais pateikiamas be kodo pakeitimų – tiesiog sistemos laiko juosta visuose serveriuose yra nustatyta UTC.
Pirmiau pateikti svarstymai ir rekomendacijos veikia puikiai kai sujungiamos dvi sąlygos:
  • Nereikia rodyti sistemos reikalavimuose vietinis laikas ir (arba) laiko juostos poslinkis tiksliai toks, koks buvo išsaugotas. Pavyzdžiui, lėktuvų bilietuose turi būti spausdinami išvykimo ir atvykimo laikai laiko juostoje, atitinkančioje oro uosto vietą. Arba jei serveris siunčia spausdinimo sąskaitas faktūras, sukurtas skirtingos salys, kiekvienas turėtų baigtis vietiniu laiku, o ne konvertuoti į serverio laiko juostą.
  • Visos datos ir laiko reikšmės sistemoje yra „absoliučios“ – t.y. apibūdinkite laiko momentą ateityje arba praeityje, kuris atitinka vieną reikšmę UTC. Pavyzdžiui, „nešėja įvyko 23:00 Kijevo laiku“ arba „susitikimas vyks nuo 13:30 iki 14:30 Minsko laiku“. Šių įvykių skaičiai skirtingose ​​laiko juostose skirsis, tačiau jie apibūdina tą patį laiko momentą. Tačiau gali atsitikti taip, kad reikalavimai programinė įranga kai kuriais atvejais reiškia „santykinį“ vietos laiką. Pavyzdžiui, „ši televizijos programa bus rodoma nuo 9:00 iki 10:00 ryto visose šalyse, kuriose yra televizijos kanalo filialas“. Pasirodo, kad programos transliacija yra ne vienas įvykis, o keli, ir potencialiai visi jie gali įvykti skirtingu laiko periodu „absoliučiu“ mastu.
Tais atvejais, kai pažeidžiama pirmoji sąlyga, problemą galima išspręsti naudojant duomenų tipus, kuriuose yra laiko juosta – tiek serveryje, tiek duomenų bazėje. Žemiau pateikiamas nedidelis įvairių platformų ir DBVS pavyzdžių sąrašas.
.NET DateTimeOffset
Java org.joda.time.DateTime, java.time.ZonedDateTime
MS SQL datetime offset
Oracle, PostgreSQL LAIKAS ŽEMĖS SU LAIKO JUOSTA
MySQL

Antros sąlygos pažeidimas – daugiau sunkus atvejis. Jei šį „santykinį“ laiką reikia saugoti tiesiog atvaizdavimui ir nėra užduoties nustatyti „absoliutų“ laiko momentą, kada įvykis įvyko arba įvyks tam tikroje laiko juostoje, pakanka tiesiog išjungti laiko konvertavimą. Pavyzdžiui, vartotojas įvedė programos pradžią visiems televizijos bendrovės filialams 2016 m. kovo 25 d. 9:00 ir ji bus perduodama, saugoma ir rodoma tokia forma. Tačiau gali atsitikti taip, kad koks nors planuotojas turėtų automatiškai atlikti specialius veiksmus likus valandai iki kiekvienos programos pradžios (išsiųsti pranešimus arba patikrinti, ar televizijos kompanijos duomenų bazėje yra tam tikrų duomenų). Patikimai įdiegti tokį planuotoją nėra nereikšminga užduotis. Tarkime, kad planuotojas žino, kurioje laiko juostoje yra kiekviena šaka. Ir viena iš šalių, kurioje yra filialas, po kurio laiko nusprendžia pakeisti laiko juostą. Atvejis nėra toks retas, kaip gali atrodyti – per šiuos ir dvejus ankstesnius metus suskaičiavau daugiau nei 10 panašių įvykių (http://www.timeanddate.com/news/time/). Pasirodo, kad arba vartotojai turi atnaujinti laiko juostų susiejimus, arba planuotojas turi automatiškai paimti šią informaciją iš pasaulinių šaltinių, pvz. Google žemėlapiai Laiko juostos API. Nesiimu siūlyti universalaus sprendimo tokiems atvejams, tiesiog pažymėsiu, kad tokios situacijos reikalauja rimto tyrimo.

Kaip matyti iš aukščiau, nėra vieno požiūrio, kuris apimtų 100% atvejų. Todėl pirmiausia iš reikalavimų turite aiškiai suprasti, kuri iš aukščiau paminėtų situacijų įvyks jūsų sistemoje. Greičiausiai viskas apsiribos pirmuoju pasiūlytu metodu, kai saugojimas UTC. Na, o aprašytos išskirtinės situacijos to nepanaikina, o tiesiog prideda kitų sprendimų ypatingiems atvejams.

Pasimatymas be laiko

Tarkime, kad mes sutvarkėme teisingą datos ir laiko rodymą atsižvelgdami į kliento laiko juostą. Pereikime prie datų be laiko ir pradžioje šiam atvejui pateikto pavyzdžio – „nauja sutartis įsigalioja 2016 m. vasario 2 d.“. Kas atsitiks, jei tokioms reikšmėms bus naudojami tie patys tipai ir tas pats mechanizmas kaip „įprastoms“ datoms ir laikui?

Ne visos platformos, kalbos ir DBVS turi tik datos tipus. Pavyzdžiui, .NET yra tik DateTime tipas, nėra atskiro tipo „tik data“. Net jei kuriant tokį objektą buvo nurodyta tik data, laikas vis tiek yra ir yra lygus 00:00:00. Jei reikšmę „2016 m. vasario 2 d. 00:00:00“ perkelsime iš zonos, kurios poslinkis yra +2 į +1, gauname „2016 m. vasario 1 d. 23:00“. Aukščiau pateiktame pavyzdyje tai atitiktų naują sutartį, kuri vienoje laiko juostoje prasideda vasario 2 d., o kitoje – vasario 1 d. Teisiniu požiūriu tai absurdiška ir, žinoma, taip neturėtų būti. Pagrindinė taisyklė„grynoms“ datoms tai labai paprasta - tokios reikšmės neturėtų būti konvertuojamos jokiame išsaugojimo ir skaitymo etape.

Yra keli būdai, kaip išvengti datų konvertavimo:

  • Jei platforma palaiko tipą, vaizduojantį datą be laiko, tai turėtų būti naudojama.
  • Prie objekto metaduomenų pridėkite specialų atributą, kuris nurodys serializatoriui, kad duota vertė laiko juosta turėtų būti ignoruojama.
  • Perduokite datą iš kliento ir atgal kaip eilutę ir išsaugokite ją kaip datą. Toks metodas yra nepatogus, jei reikia ne tik rodyti kliento datą, bet ir atlikti su ja kai kurias operacijas: lyginti, atimti ir pan.
  • Perduokite ir saugokite kaip eilutę ir konvertuokite į datą tik formatavimui pagal kliento regioninius nustatymus. Ji turi dar daugiau trūkumų nei ankstesnė parinktis - pavyzdžiui, jei saugomos eilutės datos dalys nėra „metai, mėnuo, diena“, tada bus neįmanoma atlikti veiksmingos indeksuotos paieškos pagal dienų seką.
Žinoma, galite pabandyti pateikti priešingą pavyzdį ir pasakyti, kad sutartis turi prasmę tik toje šalyje, kurioje ji sudaryta, šalis yra toje pačioje laiko juostoje, todėl jos įsigaliojimo momentą galima vienareikšmiškai nustatyti. Tačiau net ir šiuo atveju naudotojai iš kitų laiko juostų nebus suinteresuoti, kuriuo momentu jų vietos laiku įvyks šis įvykis. Ir net jei būtų poreikis rodyti šį laiko momentą, tai turėtų rodyti ne tik datą, bet ir laiką, o tai prieštarauja pradinei sąlygai.

Laiko intervalas

Su laiko intervalų saugojimu ir apdorojimu viskas paprasta: jų reikšmė nepriklauso nuo laiko juostos, todėl specialių rekomendacijų čia nėra. Jie gali būti saugomi ir perduodami kaip laiko vienetų skaičius (sveikasis skaičius arba slankusis kablelis, priklausomai nuo reikalingo tikslumo). Jei svarbus sekundės tikslumas, tai kaip sekundžių skaičius, jei svarbus milisekundžių tikslumas, tai kaip milisekundžių skaičius ir pan.

Tačiau intervalo skaičiavimas gali turėti spąstų. Tarkime, kad turime C# kodo pavyzdį, kuris apskaičiuoja laiko intervalą tarp dviejų įvykių:

DateTime start = DateTime.Now; //... DateTime end = DateTime.Now; dvigubos valandos = (pabaiga - pradžia).TotalHours;
Iš pirmo žvilgsnio čia nėra jokių problemų, bet taip nėra. Pirma, gali kilti problemų bandant tokį kodą, tačiau apie tai kalbėsime šiek tiek vėliau. Antra, įsivaizduokime, kad pradinis laiko momentas patenka į žiemos laiką, o paskutinis – į vasaros laiką (pavyzdžiui, taip matuojamas darbo valandų skaičius, o darbuotojai turi naktinę pamainą).

Tarkime, kad kodas veikia laiko juostoje, kurioje 2016 m. vasaros laikas yra kovo 27 d. naktį, ir imituok aukščiau aprašytą situaciją:

DateTime start = DateTime.Parse("2016-03-26T20:00:15+02"); DateTime pabaiga = DateTime.Parse("2016-03-27T05:00:15+03"); dvigubos valandos = (pabaiga - pradžia).TotalHours;
Šis kodas duos 9 valandas, nors iš tikrųjų tarp šių akimirkų praėjo 8 valandos. Tai galite lengvai patikrinti pakeisdami kodą taip:

DateTime start = DateTime.Parse("2016-03-26T20:00:15+02").ToUniversalTime(); DateTime pabaiga = DateTime.Parse("2016-03-27T05:00:15+03").ToUniversalTime(); dvigubos valandos = (pabaiga - pradžia).TotalHours;
Taigi išvada - bet kokios aritmetinės operacijos su data ir laiku turi būti atliekamos naudojant UTC reikšmes arba tipus, kuriuose saugoma laiko juostos informacija. Ir tada, jei reikia, perkelkite atgal į vietinį. Šiuo požiūriu pradinį pavyzdį galima nesunkiai pataisyti pakeitus DateTime.Now į DateTime.UtcNow.

Šis niuansas nepriklauso nuo konkrečios platformos ar kalbos. Čia yra panašus „Java“ kodas, turintis tą pačią problemą:

LocalDateTime start = LocalDateTime.now(); //... LocalDateTime end = LocalDateTime.now(); ilgos valandos = ChronoUnit.HOURS.beveen(pradžia, pabaiga);
Jį taip pat galima lengvai pataisyti, pavyzdžiui, naudojant ZonedDateTime vietoj LocalDateTime.

Numatytų renginių tvarkaraštis

Suplanuotų įvykių planavimas yra sudėtingesnė situacija. Universalus tipas, leidžiantis saugoti tvarkaraščius standartines bibliotekas Nr. Tačiau tokia užduotis iškyla ne itin retai, todėl paruoštus sprendimus galima rasti be problemų. Geras pavyzdys yra cron planavimo formatas, kurį viena ar kita forma naudoja kiti sprendimai, pavyzdžiui, Quartz: http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html. Ji apima beveik visus planavimo poreikius, įskaitant tokias parinktis kaip „antrasis mėnesio penktadienis“.

Daugeliu atvejų nėra prasmės rašyti savo planuotoją, nes yra lanksčių, laiko patikrintų sprendimų, tačiau jei dėl kokių nors priežasčių reikia sukurti savo mechanizmą, bent jau grafiko formatą galima pasiskolinti iš cron.

Be aukščiau aprašytų rekomendacijų dėl skirtingų laiko verčių saugojimo ir apdorojimo, taip pat norėčiau paminėti keletą kitų.

Pirma, dėl statinių klasės narių naudojimo dabartiniam laikui gauti - DateTime.UtcNow, ZonedDateTime.now() ir kt. Kaip buvo sakyta, jų naudojimas tiesiogiai kode gali labai apsunkinti vienetų testavimą, nes be specialių pašaipių karkasų galite pakeisti Dabartinis laikas neveiks. Todėl, jei planuojate rašyti vienetinius testus, turėtumėte įsitikinti, kad tokių metodų įgyvendinimas gali būti pakeistas. Yra bent du būdai, kaip išspręsti šią problemą:

  • Pateikite IDateTimeProvider sąsają su vienu metodu, kuris grąžina dabartinį laiką. Tada pridėkite šios sąsajos priklausomybę visuose kodo vienetuose, kur reikia gauti dabartinį laiką. Įprasto programos vykdymo metu į visas šias vietas bus įvedamas „numatytasis“ diegimas, kuris grąžina realų dabartinį laiką, o vienetų testuose - bet koks kitas reikalingas diegimas. Šis metodas yra lankstiausias bandymo požiūriu.
  • Sukurkite savo statinę klasę naudodami metodą, skirtą dabartiniam laikui gauti, ir galimybę įdiegti bet kokį šio metodo įgyvendinimą iš išorės. Pavyzdžiui, C# kodo atveju ši klasė gali atskleisti UtcNow ypatybę ir SetImplementation(Func) metodą. impl). Naudojimas statinė savybė arba esamo laiko gavimo metodas pašalina poreikį visur aiškiai nurodyti priklausomybę nuo papildomos sąsajos, tačiau OOP principų požiūriu tai nėra idealus sprendimas. Tačiau jei dėl kokių nors priežasčių ankstesnė parinktis netinka, galite naudoti šią.
Papildoma problema, kurią reikia išspręsti pereinant prie dabartinio laiko teikėjo diegimo, yra užtikrinti, kad niekas ir toliau nenaudotų standartinių klasių „senamadiškai“. Šią užduotį nesunku išspręsti daugelyje kodų kokybės kontrolės sistemų. Iš esmės tai reiškia, kad visuose failuose reikia ieškoti „nepageidaujamos“ poeilutės, išskyrus tą, kurioje deklaruojamas „numatytasis“ diegimas.

Antrasis įspėjimas norint gauti dabartinį laiką yra tas klientu negalima pasitikėti. Dabartinis laikas vartotojų kompiuteriuose gali labai skirtis nuo tikrojo, o jei su juo susieta logika, tai šis skirtumas gali viską sugadinti. Visos vietos, kur reikia gauti dabartinį laiką, jei įmanoma, turi būti padarytos serverio pusėje. Ir, kaip minėta anksčiau, visos aritmetinės operacijos su laiku turi būti atliekamos UTC reikšmėmis arba naudojant tipus, kurie saugo laiko juostos poslinkį.

Ir dar vienas dalykas, kurį norėjau paminėti, yra ISO 8601 standartas, kuriame aprašomas datos ir laiko formatas keičiantis informacija. Visų pirma, datos ir laiko eilutės atvaizdavimas, naudojamas serializuojant, turi atitikti šį standartą, kad būtų išvengta galimų suderinamumo problemų. Praktikoje itin retai tenka formatavimą įgyvendinti pačiam, todėl pats standartas gali būti naudingas daugiausia informaciniais tikslais.

Žymos: pridėti žymų

Dalintis