Ispravan rad sa datumom i vremenom. Uklanjanje pomaka vremenske zone iz DateTimeOffset sa pomakom datuma nula

Problem nema veze sa bazom podataka. Ako postavite tačku prekida ili negdje unesete izlaz, trebali biste biti u mogućnosti da vidite kako se pomak ukida ubrzo nakon ovog koda:

TestDateAndTime = testDateAndTime.DateTime.Date;

Hajde da to raskinemo:

  • Počeli ste sa DateTimeOffset vrijednošću 2008-05-01T08:06:32+01:00
  • Zatim ste pozvali .DateTime , što je rezultiralo vrijednošću DateTime 2008-05-01T08:06:32 sa DateTimeKind.Unspecified .
  • Zatim ste pozvali .Date, što je rezultiralo vrijednošću DateTime od 2008-05-01T00:00:00 sa DateTimeKind.Unspecified.
  • Rezultat dodjeljujete testDateAndTime, koji je tipa DateTimeOffset. Ovo uzrokuje implicitno prebacivanje sa DateTime na DateTimeOffset - , što važi lokalni Vremenska zona. U vašem slučaju, izgleda da je pomak za ovu vrijednost u vašoj lokalnoj vremenskoj zoni -04:00 , tako da je rezultirajuća vrijednost DateTimeOffset od 2008-05-01T00:00:00-04:00 kao što ste opisali.

Ti si rekao:

Krajnji cilj je jednostavno imati datum bez pomaka vremena ili vremenske zone.

Pa, postoji trenutno nije izvorni tip podataka C#, koji je samo datum bez vremena System.Time paket u corefxlab-u, ali još nije sasvim spreman za tipičnu proizvodnu aplikaciju. Postoji LocalDate u biblioteci Noda Time koji možete koristiti danas, ali ćete i dalje morati da ga konvertujete nazad u izvorni tip pre nego što ga sačuvate u bazi podataka. Dakle, u međuvremenu, najbolje što možete učiniti je:

  • Promijenite svoj SQL Server da koristi tip datuma u polju.
  • U svom .NET kodu koristite DateTime sa vremenom 00:00:00 i DateTimeKind.Unspecified. Morate zapamtiti da zanemarite vremenski dio (pošto zaista postoje datumi bez lokalne ponoći u određenim vremenskim zonama).
  • Promijenite test prop da bude DateTime, a ne DateTimeOffset.

Općenito, dok je DateTimeOffset pogodan za veliki broj scenarija (npr vremenske oznake događaji), ne uklapa se dobro za vrijednosti samo za datum.

želim trenutni datum With nulti pomak.

Ako ti stvarno želim to je kao DateTimeOffset , možete učiniti:

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

Međutim, ne preporučujem da to radite. Radeći ovo uzimate lokalni datum originalne vrijednosti i tvrdite da je u UTC-u. Ako je originalni pomak nešto drugo osim nule, ovo će biti lažna izjava. To će kasnije dovesti do drugih grešaka jer zapravo govorite o drugom trenutku (sa potencijalno drugačijim datumom) od onog koji ste kreirali.

Relativno dodatno pitanje, postavljen u vašoj tabli. Navođenje DateTimeKind.Utc mijenja ponašanje implicitnog cast. Umjesto korištenja lokalne vremenske zone, koristi se UTC vrijeme, koje uvijek ima nulti pomak. Rezultat je isti kao eksplicitniji stav koji sam dao gore. I dalje ne preporučujem ovo iz istih razloga.

Razmotrite primjer koji počinje od 2016-12-31T22:00:00-04:00. Prema vašem pristupu trebalo bi da sačuvate 2016-12-31T00:00:00+00:00 u bazi podataka. Međutim, to su dva različita vremena. Prva, normalizirana na UTC, bila bi 2017-01-01T02:00:00+00:00, a druga, pretvorena u drugu vremensku zonu, bila bi 2016-12-30T20:00:00-04:00. Obratite pažnju na promjenu datuma u konverziji. Ovo vjerovatno nije ponašanje koje biste željeli u svojoj aplikaciji.

DateTimeOffset testDateAndTime = novi DateTimeOffset(2008, 5, 1, 8, 6, 32, novi vremenski raspon(1, 0, 0)); //ČIŠĆENJE VRIJEME I DATUM testDateAndTime = testDateAndTime.DateTime.Date; var datesTableEntry = db.DatesTable.First(dt => dt.Id == someTestId); datesTableEntry.test= testDateAndTime; db.SaveChangesAsync();

REZULTATI U BACI PODATAKA: 2008-05-01 00:00:00.0000000 -04:00

Kako omogućiti -4:00 do +00:00 (od koda prije snimanja)?

Pokušavao sam:

Javni zadatak SetTimeZoneOffsetToZero(DateTimeOffset dateTimeOffSetObj) ( TimeSpan zeroOffsetTimeSpan = new TimeSpan(0, 0, 0, 0, 0); return dateTimeOffSetObj.ToOffset(zeroOffsetTimeSpan); )

On ništa ne radi.

Krajnji cilj je jednostavno imati datum bez pomaka vremena ili vremenske zone. NE ŽELIM da konvertujem vreme u drugu vremensku zonu (tj. 00:00:00.0000000 Ne želim da oduzima 4 sata od 00:00:00.0000000 vremena i 00:00:00.0000000 poništi postavljeno vreme za +00 :00 , samo želim da postavi pomak na +00:00). Želim trenutni datum sa nultim pomakom.

Uredi:

Evo šta možete ponuditi drugdje:

DateTimeOffset testDateAndTime = novi DateTimeOffset(2008, 5, 1, 8, 6, 32, novi vremenski raspon(1, 0, 0)); testDateAndTime = testDateAndTime.DateTime.Date; //Vremenski dio za nulti izlaz testDateAndTime = DateTime.SpecifyKind(testDateAndTime.Date, DateTimeKind.Utc); // "Zero out" offset dio

Bio sam siguran da će SpecifyKind SpecifyKind moj dateTimeOffset, kao da će promijeniti I pomak vremenske i vremenske zone, ali kada se testira, čini se da samo mijenja pomak vremenske zone, što je ono što želim. Postoji li problem sa ovim?

1 odgovor

Problem nema veze sa bazom podataka. Ako postavite tačku prekida ili negdje registrujete izlaz, trebali biste vidjeti da je pomak vezan ubrzo nakon ovog koda:

TestDateAndTime = testDateAndTime.DateTime.Date;

Hajde da to raskinemo:

  • Počeli ste sa DateTimeOffset 2008-05-01T08:06:32+01:00
  • Zatim ste pozvali .DateTime, što je rezultiralo vrijednošću DateTime od 2008-05-01T08:06:32 sa DateTimeKind.Unspecified.
  • Zatim ste pozvali .Date, što je rezultiralo vrijednošću DateTime od 2008-05-01T00:00:00 sa DateTimeKind.Unspecified.
  • Rezultat vraćate u testDateAndTime koji je tipa DateTimeOffset. Ovo uzrokuje implicitnu konverziju iz DateTime u DateTimeOffset koji se odnosi na lokalnu vremensku zonu. U vašem slučaju, izgleda da je pomak za ovu vrijednost u vašoj lokalnoj vremenskoj zoni -04:00 , tako da je rezultirajuća vrijednost DateTimeOffset 2008-05-01T00:00:00-04:00 kao što ste opisali,

Ti si rekao:

Krajnji cilj je jednostavno imati datum bez pomaka vremena ili vremenske zone.

Pa, trenutno ne postoji izvorni C# tip podataka koji je samo datum bez vremena. Postoji čist tip datuma u System.Time paketu u corefxlab-u, ali nije baš spreman za tipičnu proizvodnu aplikaciju. U Nodinoj vremenskoj biblioteci postoji LocalDate, koji možete koristiti danas, ali ćete i dalje morati da ga konvertujete nazad u izvorni tip pre nego što ga sačuvate u bazi podataka. Dakle, u međuvremenu, najbolje što možete učiniti je:

  • Promijenite svoj SQL Server da koristi tip datuma u ovom polju.
  • U svom .NET kodu koristite DateTime sa vremenom 00:00:00 i DateTimeKind.Unspecified. Morate zapamtiti da zanemarite vremenski dio (pošto zaista postoje datumi bez lokalne ponoći u određenim vremenskim zonama).
  • Promijenite testno upozorenje na DateTime umjesto DateTimeOffset.

Općenito, iako je DateTimeOffset prikladan za veliki broj scenarija (kao što su događaji vremenskog žiga), nije prikladan za vrijednosti samo za datum.

Želim trenutni datum sa nultim pomakom.

Ako zaista želite to kao DateTimeOffset, uradili biste:

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

Međutim, ne savjetujem ovo. Na taj način uzimate lokalni datum originalne vrijednosti i tvrdite da je u UTC-u. Ako je originalni pomak nešto drugo osim nule, ovo će biti lažna izjava. To će kasnije dovesti do drugih grešaka jer zapravo govorite o drugom trenutku (sa potencijalno drugačijim datumom) od onog koji ste kreirali.

Što se tiče dodatnog pitanja postavljenog u vašem uređivanju - navođenje DateTimeKind.Utc mijenja ponašanje implicitnog cast. Umjesto korištenja lokalne vremenske zone, koristi se UTC vrijeme, koje uvijek ima nulti pomak. Rezultat je isti kao eksplicitniji stav koji sam dao gore. I dalje ne preporučujem ovo iz istih razloga.

Pogledajmo primjer počevši od 2016-12-31T22:00:00-04:00. Prema vašem pristupu trebalo bi da sačuvate 2016-12-31T00:00:00+00:00 u bazi podataka. Međutim, to su dva različita vremena. Prva, normalizirana na UTC, bila bi 2017-01-01T02:00:00+00:00, a druga, pretvorena u drugu vremensku zonu, bila bi 2016-12-30T20:00:00-04:00. Obratite pažnju na promjenu datuma u konverziji. Ovo vjerovatno nije ponašanje koje biste željeli u svojoj aplikaciji.

Gotovo svi projekti nailaze na probleme uzrokovane nepravilnim rukovanjem i pohranjivanjem datuma i vremena. Čak i ako se projekt koristi u istoj vremenskoj zoni, i dalje možete dobiti neugodna iznenađenja nakon prelaska na zimsko/ljetno računanje vremena. Istovremeno, malo ljudi je zbunjeno implementacijom ispravnog mehanizma od početka, jer izgleda da s tim ne može biti problema, jer je sve trivijalno. Nažalost, kasnija stvarnost pokazuje da to nije tako.

Logično, mogu se razlikovati sljedeće vrste vrijednosti koje se odnose na datum i vrijeme:


Razmotrimo svaku tačku posebno, ne zaboravljajući.

datum i vrijeme

Recimo da se laboratorija koja je prikupljala materijal za analizu nalazi u vremenskoj zoni +2, a centralna filijala koja prati blagovremeno izvršenje testova je u zoni +1. Vremena navedena u primjeru zabilježena su kada je materijal prikupljen u prvom laboratoriju. Postavlja se pitanje - koju cifru vremena treba da vidi centrala? Očigledno softver centralna kancelarija trebalo bi da pokaže 15. januar 2014 12:17:15 - sat manje, pošto se prema njihovom satu događaj desio baš u tom trenutku.

Razmotrimo jedan od mogućih lanaca radnji kroz koje podaci prolaze od klijenta do servera i nazad, što vam omogućava da uvijek ispravno prikažete datum/vrijeme prema trenutnoj vremenskoj zoni klijenta:

  1. Vrijednost se kreira na klijentu, na primjer 02.03.2016 15 :13:36, klijent je u vremenskoj zoni +2.
  2. Vrijednost se konvertuje u prikaz niza za prijenos na server - “2016-03-02T 15 :13:36+02:00”.
  3. Serijski podaci se šalju na server.
  4. Server deserijalizira vrijeme u objekt datuma/vremena, dovodeći ga u njegovu trenutnu vremensku zonu. Na primjer, ako server radi na +1, tada će objekt sadržavati 2. mart 2016 14 :13:36.
  5. Server sprema podatke u bazu podataka, ali ona ne sadrži nikakve informacije o vremenskoj zoni - najčešće korišteni tipovi datuma/vremena jednostavno ne znaju ništa o tome. Tako će 2. mart 2016. biti sačuvan u bazi podataka 14 :13:36 u "nepoznatoj" vremenskoj zoni.
  6. Server čita podatke iz baze podataka i kreira odgovarajući objekat sa vrednošću 02.03.2016 14 :13:36. A pošto server radi u vremenskoj zoni +1, ova vrijednost će se također interpretirati unutar iste vremenske zone.
  7. Vrijednost se pretvara u prikaz niza za prijenos klijentu - “2016-03-02T 14 :13:36+01:00”.
  8. Serijski podaci se šalju klijentu.
  9. Klijent deserijalizira primljenu vrijednost u objekt datuma/vremena, pretvarajući je u svoju trenutnu vremensku zonu. Na primjer, ako je -5, tada bi prikazana vrijednost trebala biti 2. marta 2016 09 :13:36.
Čini se da je sve netaknuto, ali razmislimo šta bi moglo poći po zlu u ovom procesu. Zapravo, problemi se ovdje mogu dogoditi gotovo na svakom koraku.
  • Vrijeme na klijentu može se generirati bez vremenske zone uopće - na primjer, tip DateTime u .NET sa DateTimeKind.Unspecified.
  • Motor za serijalizaciju može koristiti format koji ne uključuje pomak vremenske zone.
  • Prilikom deserijalizacije u objekt, pomak vremenske zone se može zanemariti, posebno u "domaćim" deserializatorima - i na serveru i na klijentu.
  • Prilikom čitanja iz baze podataka, objekt datuma/vremena može se generirati bez vremenske zone - na primjer, tip DateTime u .NET-u sa DateTimeKind.Unspecified. Štaviše, sa DateTime u .NET-u, u praksi se upravo to dešava ako ne navedete eksplicitno drugi DateTimeKind odmah nakon lekture.
  • Ako se serveri aplikacija koji rade sa zajedničkom bazom podataka nalaze u različitim vremenskim zonama, doći će do ozbiljne zabune u vremenskim pomacima. Vrijednost datuma/vremena upisana u bazu podataka od strane servera A i pročitana od strane servera B bit će primjetno drugačija od iste originalne vrijednosti koju je napisao server B i pročitao server A.
  • Prenošenje aplikacijskih servera iz jedne zone u drugu će dovesti do pogrešne interpretacije već pohranjenih vrijednosti datuma/vremena.
Ali najozbiljniji nedostatak u gore opisanom lancu je korištenje lokalne vremenske zone na serveru. Ako nema prelaska na ljetno/zimsko računanje vremena, onda ne dodatni problemi neće biti. Ali inače, možete dobiti mnoga neprijatna iznenađenja.

Pravila za prelazak na ljetno/zimsko računanje vremena su, striktno govoreći, promjenjiva. Različite zemlje mogu s vremena na vrijeme mijenjati svoja pravila, a ove promjene bi trebale biti ugrađene u ažuriranja sistema mnogo unaprijed. U praksi smo se više puta susreli sa situacijama da ovaj mehanizam nije funkcionisao kako treba, a koje su na kraju bile riješene instaliranjem hitnih ispravki ili operativni sistem, ili korištene biblioteke trećih strana. Vjerovatnoća da se isti problemi ponove nije nula, pa je bolje imati način da osigurate da se oni izbjegnu.

Uzimajući u obzir gore opisana razmatranja, formuliraćemo najpouzdaniji i najjednostavniji pristup prijenosu i pohranjivanju vremena: na serveru i u bazi podataka, sve vrijednosti moraju biti konvertovane u UTC vremensku zonu.

Pogledajmo šta nam ovo pravilo daje:

  • Prilikom slanja podataka na server, klijent mora proći pomak vremenske zone kako bi server mogao ispravno konvertirati vrijeme u UTC. Alternativna opcija— obavezati klijenta da izvrši ovu transformaciju, ali prva opcija je fleksibilnija. Kada prima podatke nazad od servera, klijent će konvertovati datum i vreme u svoju lokalnu vremensku zonu, znajući da će u svakom slučaju primiti vreme u UTC.
  • U UTC-u nema prijelaza između ljetnog i zimskog računanja vremena, tako da problemi povezani s tim neće biti relevantni.
  • Na serveru, kada čitate iz baze podataka, ne morate konvertovati vremenske vrijednosti, samo trebate eksplicitno naznačiti da odgovara UTC-u. U .NET-u, na primjer, to se može postići postavljanjem DateTimeKind na objektu vremena na DateTimeKind.Utc.
  • Razlika u vremenskim zonama između servera koji rade sa zajedničkom bazom podataka, kao i prenos servera iz jedne zone u drugu, ni na koji način neće uticati na ispravnost primljenih podataka.
Da biste implementirali takvo pravilo, dovoljno je voditi računa o tri stvari:
  1. Učinite mehanizam serijalizacije i deserijalizacije tako da vrijednosti datuma/vremena budu ispravno prevedene iz UTC u lokalnu vremensku zonu i natrag.
  2. Osigurajte da deserijalizator na strani servera kreira objekte datuma/vremena u UTC-u.
  3. Uvjerite se da se prilikom čitanja iz baze podataka objekti datuma/vremena kreiraju u UTC-u. Ova stavka se ponekad pruža bez promjene koda - jednostavno je sistemska vremenska zona na svim serverima postavljena na UTC.
Gore navedena razmatranja i preporuke odlično funkcioniraju kada se kombinuju dva uslova:
  • Nema potrebe za prikazivanjem u sistemskim zahtjevima lokalno vrijeme i/ili pomak vremenske zone tačno onako kako je sačuvan. Na primjer, avio karte moraju ispisati vrijeme polaska i dolaska u vremenskoj zoni koja odgovara lokaciji aerodroma. Ili ako server šalje ispisne fakture kreirane u različite zemlje, svaki bi trebao završiti s lokalnim vremenom, a ne konvertiran u vremensku zonu servera.
  • Sve vrijednosti datuma i vremena u sistemu su "apsolutne" - tj. opisati tačku u vremenu u budućnosti ili prošlosti koja odgovara jednoj vrijednosti u UTC-u. Na primjer, „nosna raketa je održana u 23:00 po kijevskom vremenu“ ili „sastanak će se održati od 13:30 do 14:30 po Minsku“. Brojevi za ove događaje će biti različiti u različitim vremenskim zonama, ali će opisivati ​​istu tačku u vremenu. Ali može se dogoditi da zahtjevi za softver podrazumijevaju "relativno" lokalno vrijeme za neke slučajeve. Na primjer, “ovaj televizijski program će se emitovati od 9:00 do 10:00 ujutro u svakoj zemlji u kojoj postoji podružnica TV kanala.” Ispada da emitovanje programa nije jedan događaj, već nekoliko, a potencijalno se svi mogu dogoditi u različitim vremenskim periodima u „apsolutnoj“ skali.
Za slučajeve kada je prvi uslov prekršen, problem se može rešiti korišćenjem tipova podataka koji sadrže vremensku zonu – i na serveru i u bazi podataka. Ispod je mala lista primjera za različite platforme i DBMS-ove.
.NET DateTimeOffset
Java org.joda.time.DateTime, java.time.ZonedDateTime
MS SQL datetimeoffset
Oracle, PostgreSQL VREMENSKA OZNAKA SA VREMENSKOM ZONOM
MySQL

Kršenje drugog uslova - više težak slučaj. Ako ovo „relativno“ vrijeme treba pohraniti samo za prikaz, a ne postoji zadatak da se odredi „apsolutni“ trenutak u vremenu kada se događaj dogodio ili će se dogoditi za datu vremensku zonu, dovoljno je jednostavno onemogućiti konverziju vremena. Na primjer, korisnik je 25. marta 2016. godine u 9:00 unio početak programa za sve filijale televizijske kuće, koji će se prenositi, čuvati i prikazivati ​​u ovom obliku. Ali može se dogoditi da neki planer automatski izvrši posebne radnje sat vremena prije početka svakog programa (pošalje obavještenja ili provjeri prisutnost nekih podataka u bazi podataka TV kuće). Pouzdana implementacija takvog planera nije trivijalan zadatak. Recimo da planer zna u kojoj se vremenskoj zoni nalazi svaka grana. I jedna od zemalja u kojoj postoji podružnica odluči da nakon nekog vremena promijeni vremensku zonu. Slučaj nije tako rijedak kao što se čini - tokom ove i prethodne dvije godine izbrojao sam više od 10 sličnih događaja (http://www.timeanddate.com/news/time/). Ispostavilo se da ili korisnici moraju ažurirati vezivanja vremenske zone, ili planer mora automatski preuzeti ove informacije iz globalnih izvora kao što je google mape API vremenske zone. Ne obvezujem se ponuditi univerzalno rješenje za takve slučajeve, samo ću napomenuti da takve situacije zahtijevaju ozbiljno proučavanje.

Kao što se vidi iz gore navedenog, ne postoji jedinstven pristup koji pokriva 100% slučajeva. Stoga, prvo morate jasno razumjeti iz zahtjeva koja će se od gore navedenih situacija dogoditi u vašem sistemu. Najvjerovatnije će sve biti ograničeno na prvi predloženi pristup sa pohranom u UTC. Pa, opisane izuzetne situacije to ne poništavaju, već jednostavno dodaju druga rješenja za posebne slučajeve.

Datum bez vremena

Recimo da smo sredili ispravan prikaz datuma i vremena uzimajući u obzir vremensku zonu klijenta. Pređimo na datume bez vremena i primjer dat za ovaj slučaj na početku - "novi ugovor stupa na snagu 2. februara 2016. godine." Šta će se dogoditi ako se isti tipovi i isti mehanizam koriste za takve vrijednosti kao za "regularne" datume i vremena?

Nemaju sve platforme, jezici i DBMS-ovi samo tipovi datuma. Na primjer, u .NET-u postoji samo tip DateTime, ne postoji poseban tip "samo datum". Čak i ako je naveden samo datum prilikom kreiranja takvog objekta, vrijeme je i dalje prisutno i jednako je 00:00:00. Ako prenesemo vrijednost “2. februar 2016. 00:00:00” iz zone sa pomakom od +2 do +1, dobićemo “1. februar 2016. 23:00:00”. Za gornji primjer, ovo bi bilo ekvivalentno novom ugovoru koji počinje 2. februara u jednoj vremenskoj zoni, a 1. februara u drugoj. Sa pravne tačke gledišta, ovo je apsurdno i, naravno, ne bi trebalo da bude tako. Opšte pravilo za "čiste" datume je krajnje jednostavno - takve vrijednosti ne bi trebalo pretvarati ni u jednom koraku spremanja i čitanja.

Postoji nekoliko načina da izbjegnete konverziju za datume:

  • Ako platforma podržava tip koji predstavlja datum bez vremena, onda to treba koristiti.
  • Dodajte poseban atribut metapodacima objekta koji će reći serijalizatoru da je za datu vrijednost vremensku zonu treba zanemariti.
  • Prosledite datum od klijenta i nazad kao string i pohranite ga kao datum. Ovaj pristup je nezgodan ako trebate ne samo prikazati datum na klijentu, već i izvršiti neke operacije na njemu: poređenje, oduzimanje itd.
  • Prosledite i pohranite kao string i konvertujte u datum samo za formatiranje na osnovu regionalnih postavki klijenta. Ima još više nedostataka od prethodne opcije - na primjer, ako dijelovi datuma u pohranjenom nizu nisu u redoslijedu „godina, mjesec, dan“, tada će biti nemoguće izvršiti efektivnu indeksiranu pretragu po rasponu datuma.
Možete, naravno, pokušati da navedete kontraprimjer i kažete da ugovor ima smisla samo unutar zemlje u kojoj je sklopljen, zemlja je u istoj vremenskoj zoni, pa se stoga može nedvosmisleno odrediti trenutak stupanja na snagu. Ali čak i u ovom slučaju, korisnike iz drugih vremenskih zona neće zanimati u kojem trenutku u njihovom lokalnom vremenu će se dogoditi ovaj događaj. A čak i da postoji potreba da se prikaže ovaj trenutak u vremenu, onda bi morao da prikaže ne samo datum, već i vreme, što je u suprotnosti sa prvobitnim stanjem.

Vremenski interval

Sa pohranjivanjem i obradom vremenskih intervala sve je jednostavno: njihova vrijednost ne ovisi o vremenskoj zoni, tako da ovdje nema posebnih preporuka. Mogu se pohraniti i prenijeti kao određeni broj vremenskih jedinica (cijeli broj ili pokretni zarez, ovisno o potrebnoj preciznosti). Ako je tačnost sekunde važna, onda kao broj sekundi, ako je važna tačnost u milisekundama, onda kao broj milisekundi, itd.

Ali izračunavanje intervala može imati zamke. Recimo da imamo neki uzorak C# koda koji izračunava vremenski interval između dva događaja:

Datum i vrijeme početka = DateTime.Now; //... DateTime end = DateTime.Now; dupli sati = (kraj - početak).Ukupno sati;
Na prvi pogled, tu nema nikakvih problema, ali nije tako. Prvo, može postojati problem sa jediničnim testiranjem takvog koda, ali o tome ćemo govoriti malo kasnije. Drugo, zamislimo da je početni trenutak vremena pao na zimsko računanje vremena, a konačni trenutak na ljetno računanje vremena (na primjer, tako se mjeri broj radnih sati, a radnici imaju noćnu smjenu).

Pretpostavimo da kod radi u vremenskoj zoni u kojoj se ljetno računanje vremena u 2016. odvija u noći 27. marta i simuliramo gore opisanu situaciju:

DateTime start = DateTime.Parse("2016-03-26T20:00:15+02"); DateTime end = DateTime.Parse("2016-03-27T05:00:15+03"); dupli sati = (kraj - početak).Ukupno sati;
Ovaj kod će rezultirati za 9 sati, iako je između ovih trenutaka u stvari prošlo 8 sati. To možete lako provjeriti promjenom koda na sljedeći način:

DateTime start = DateTime.Parse("2016-03-26T20:00:15+02").ToUniversalTime(); DateTime end = DateTime.Parse("2016-03-27T05:00:15+03").ToUniversalTime(); dupli sati = (kraj - početak).Ukupno sati;
Otuda zaključak - sve aritmetičke operacije s datumom i vremenom moraju se obaviti koristeći ili UTC vrijednosti ili tipove koji pohranjuju informacije o vremenskoj zoni. I onda se vratite na lokalni ako je potrebno. Sa ove tačke gledišta, originalni primjer se može lako ispraviti promjenom DateTime.Now u DateTime.UtcNow.

Ova nijansa ne zavisi od određene platforme ili jezika. Evo sličnog koda u Javi koji ima isti problem:

LocalDateTime start = LocalDateTime.now(); //... LocalDateTime end = LocalDateTime.now(); dugi sati = ChronoUnit.HOURS.between(početak, kraj);
Također se može lako popraviti - na primjer, korištenjem ZonedDateTime umjesto LocalDateTime.

Raspored zakazanih događaja

Zakazivanje zakazanih događaja je složenija situacija. Univerzalni tip koji vam omogućava pohranjivanje rasporeda standardne biblioteke br. Ali takav zadatak se ne javlja vrlo rijetko, pa se gotova rješenja mogu pronaći bez problema. Dobar primjer je format cron planera koji se u jednom ili drugom obliku koristi od strane drugih rješenja, na primjer, Quartz: http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html. Pokriva gotovo sve potrebe planiranja, uključujući opcije poput „drugog petka u mjesecu“.

U većini slučajeva nema smisla pisati vlastiti planer, jer postoje fleksibilna, vremenski testirana rješenja, ali ako iz nekog razloga postoji potreba za kreiranjem vlastitog mehanizma, onda se barem format rasporeda može posuditi from cron.

Pored gore opisanih preporuka u vezi sa pohranjivanjem i obradom različitih vrsta vremenskih vrijednosti, postoji nekoliko drugih koje bih također želio spomenuti.

Prvo, u vezi sa upotrebom statičkih članova klase za dobijanje trenutnog vremena - DateTime.UtcNow, ZonedDateTime.now(), itd. Kao što je rečeno, njihovo direktno korištenje u kodu može ozbiljno zakomplikovati testiranje jedinica, jer bez posebnih okvira za ruganje možete zamijeniti trenutno vrijeme neće raditi. Stoga, ako planirate pisati jedinične testove, trebali biste se pobrinuti da se implementacija takvih metoda može zamijeniti. Postoje najmanje dva načina za rješavanje ovog problema:

  • Osigurajte IDateTimeProvider sučelje s jednom metodom koja vraća trenutno vrijeme. Zatim dodajte ovisnost o ovom sučelju u sve jedinice koda gdje trebate dobiti trenutno vrijeme. Tokom normalnog izvršavanja programa, na sva ova mjesta će se ubaciti “podrazumevana” implementacija, koja vraća stvarno trenutno vrijeme, au jediničnim testovima - bilo koju drugu potrebnu implementaciju. Ova metoda je najfleksibilnija sa stanovišta testiranja.
  • Napravite sopstvenu statičku klasu sa metodom za dobijanje trenutnog vremena i mogućnošću da instalirate bilo koju implementaciju ove metode izvana. Na primjer, u slučaju C# koda, ova klasa može izložiti svojstvo UtcNow i metodu SetImplementation(Func) impl). Upotreba statičko svojstvo ili metoda za dobijanje trenutnog vremena eliminiše potrebu da se svuda eksplicitno navede zavisnost od dodatnog interfejsa, ali sa stanovišta OOP principa to nije idealno rešenje. Međutim, ako iz nekog razloga prethodna opcija nije prikladna, onda možete koristiti ovu.
Dodatni problem koji bi trebao biti riješen prilikom migracije na implementaciju vašeg trenutnog dobavljača vremena je osigurati da niko ne nastavi koristiti standardne klase na „staromodni način“. Ovaj zadatak je lako rešiti u većini sistema kontrole kvaliteta koda. U suštini, sve se svodi na traženje “neželjenog” podniza u svim datotekama osim u onom gdje je deklarirana “podrazumevana” implementacija.

Drugo upozorenje za dobijanje trenutnog vremena je to klijentu se ne može vjerovati. Trenutno vrijeme na računarima korisnika može biti veoma različito od stvarnog, a ako se za njega veže logika, ta razlika može sve pokvariti. Sva mjesta gdje postoji potreba za dobijanjem trenutnog vremena treba, ako je moguće, uraditi na strani servera. I, kao što je ranije spomenuto, sve aritmetičke operacije s vremenom moraju se izvoditi ili u UTC vrijednostima ili pomoću tipova koji pohranjuju pomak vremenske zone.

I još jedna stvar koju sam htio spomenuti je ISO 8601 standard, koji opisuje format datuma i vremena za razmjenu informacija. Konkretno, prikaz datuma i vremena u nizu koji se koristi u serijalizaciji mora biti u skladu s ovim standardom kako bi se spriječili potencijalni problemi kompatibilnosti. U praksi je izuzetno rijetko da morate sami implementirati formatiranje, tako da sam standard može biti koristan uglavnom u informativne svrhe.

Oznake: Dodaj oznake

Humana opcija ([morate se registrirati da biste vidjeli link]).

Suština problema je u ovome. Ako ste slučajno postavili bazu podataka na SQL server sa "pomakom datuma" od 0, onda se problem javlja kada baza podataka sadrži atribut tipa TIME, tj. ovaj atribut je postavljen na 01 /01/0001 10:30:00 ili datum registracije prazan 01.01.0001 00:00:00. Prilikom snimanja takvih detalja, to se ne snima.
Na internetu predlažu kreiranje nove baze podataka sa pomakom od 2000.
Ali nisam baš želio da stvaram novu bazu. I promijenite putanju do baze podataka za sve korisnike.
Zatim sam pratio putanju gdje je ova vrijednost pohranjena u SQL-u. Našao sam ga i promijenio na 2000 i sve je bilo ok..
A sada korak po korak gdje promijeniti.

Izbaci sve korisnike.

PAŽNJA!!!

1. Uradite to prvo rezervna kopija pomoću 1C tj. prenesite ga na *.dt
Ovo se mora uraditi prije promjene "offseta"
Ako se to ne učini, onda će u cijeloj vašoj bazi podataka biti reference, dokumenti itd.
gdje postoji rekvizit datum će biti recimo 02.10.0009
ŠTA NIJE DOZVOLJENO...
Dakle, otpremili ste na *.dt

2. Idite na SQL Server Management Studio
Pronađite svoju bazu na listi i pritisnite znak plus.
Tamo pronađite fasciklu „Tabele“ i otvorite je.
Otvoriće se gomila tablica, idite na samo dno, pronađite sto
_YearOffset, stanite na njega i desnim dugmetom izaberite stavku „Otvori sto“, pogledajte sl. 1
Promijenite vrijednost 0 na 2000
Zatvorite SQL Server Management Studio

3. Idite na konfigurator i učitajte prethodno spremljenu bazu podataka.

Ako se to ne učini, svi datumi će biti sa godinom 0009.
Nakon što se baza podataka učita... Možete otići na 1C i uvjeriti se da su datumi normalni.
Rezultat je da smo promijenili “pomak datuma od 0 do 2000”

Ponekad se desi da se ova opcija ne može koristiti iz jednog ili drugog razloga. Zatim postoji još hardcore opcija ([morate se registrirati da vidite vezu]):

Objavite kursor TablesAndFields za

SELECT objects.name kao Tablename, columns.name kao columnname
IZ dbo.sysobjects as objekata
lijevo pridruži dbo.syscolumns kao stupce na objects.id = columns.id
gdje je objects.xtype = "U" i columns.xtype = 61

otvorite TablesAndFields

WHILE @@FETCH_STATUS = 0
BEGIN Exec(" ažuriraj"+ @TableName + "
postavi " + @Ime kolone + " = ""2000- 01- 01 00:00:00" "
gdje " + @Ime kolone + " > ""3999- 12- 31 23:59:59" "")

Ovo se izvršava sve dok prethodno preuzimanje bude uspješno.
PREUZMI SLJEDEĆE IZ TablesAndFields u @TableName, @ColumnName
KRAJ

zatvori TablesAndFields
deallocateTablesAndFields
idi

Prije bilo kakvih manipulacija, ne zaboravite napraviti kopije baza podataka!

Dijeli