DateTime v .NET Frameworku

2. díl - DateTime v .NET Frameworku

Tomáš Jecha, MVP, MCSD       24.06.2012       C#, VB.NET, .NET       13090 zobrazení

Práce se strukturou DateTime v .NET Frameworku lze využívat pro práci s lokálním časem i časem v jiných časových pásmech. Tento díl se věnuje základnímu popisu a konverzím mezi lokálním a UTC časovým údajem.

Struktura DateTime je v .NET Frameworku asi jednou z nejpoužívanějších. Lze do ní uložit datum a čas a s hodnotou dále pracovat. Důležité je poznamenat, že tento typ neobsahuje definici časového pásma. Je tedy z pohledu dat jedno, jestli ukládáte čas aktuální pro Českou Republiku nebo New York, či jestli zohledňujete letní čas. Datově je to jen hodnota roku, měsíce, dne, hodiny, minuty, vteřiny a milisekundy.

Nejčastěji se s tímto typem pracuje tzv. lokálním způsobem. Jinými slovy vás nezajímá, jestli v jiné oblasti někde na světě může být jiný čas. Vy budete pracovat pouze s časem, který je nastavený v systému. Představte si modelovou situaci - zákazník si v objednávkovém systému koupí službu a vy chcete, aby vypršela za 20 minut. Zjistíte tedy aktuální datum a čas, přičtete 20 minut a hodnotu si někam uložíte. Při následné kontrole, zda služba zákazníkovi vypršela, si načtete hodnotu a porovnáte ji s aktuálním časem. Vše se tedy točí okolo získání aktuálního času.

Now & UtcNow - získání aktuálního data a času

Nejčastěji se používá statická metoda DateTime.Now, která vrací aktuální datum a čas nastavený v systému. Na serveru s časovým pásmem “UTC-10 Hawaii” bude tedy vracet jiný čas, než ve stejnou dobu na serveru s časovým pásmem “UTC+1 Prague”.

Avšak může nastat hned několik situací, kdy budete potřebovat čas v jiném pásmu, většinou v univerzálním UTC (časové pásmo 0, bez letního času). Pokud se vrátím k modelové situaci se zákazníkem a vypršením nějaké služby za 20 minut – představte si, že máte několik serverů po světě (každý v jiném pásmu), které vyřizují objednávky služeb a posílají je na centrální server. Pokud by nákupní servery zasílaly datum a čas objednávky ve svém lokálním pásmu, budeme si muset pamatovat jejich nastavení pásma a datum konvertovat na lokální pásmo při přijetí dat. Z důvodu přílišné složitosti je jednodušší systém navrhnout tak, aby všechny servery posílaly datum v univerzálním UTC.

K tomu slouží metoda DateTime.UtcNow vracející aktuální čas UTC. Pro Českou Republiku tedy o jednu nebo dvě hodiny méně než je lokální čas (podle toho, zda je nebo není letní čas). Důležité je, že hodnota bude ve stejný čas stejná na všech počítačích, ať už je v ČR nebo na Hawaii.

Následující kód obě metody demonstruje:

// lokální čas (letní čas - UTC+2) -  například: 21.6.2012 19:23:27
Console.WriteLine(DateTime.Now);

// UTC aktuální čas - například: 21.6.2012 17:23:27
Console.WriteLine(DateTime.UtcNow);

Je vhodné se co nejdříve rozhodnout při psaní nového projektu, který přístup budete používat a v ideálním případě je nemíchat. Pokud na jednom místě v kódu použijete Now a na druhém UtcNow, výsledek bude při nejmenším dosti nekonzistentní. Pokud i přes to chcete (nebo musíte) používat oba druhy, doporučuji přidat ke jménu vlastností příponu “Utc”, popřípadě “Local”. Na první pohled bude tedy jasné, jaký formát času je uložený uvnitř proměnné.

DateTimeKind a konverze mezi lokálním a UTC časem

Datový typ DateTime nám nabízí několik základních metod pro konverzi převážně mezi lokálním a UTC časem. Mimo to obsahuje vlastnost Kind (typ DateTimeKind), která může s touto konverzí určitým způsobem pomoci. Nabývá těchto hodnot:

  • Local – datum a čas byl vytvořen podle lokálního času
  • Utc – datum a čas byl vytvořen podle pásma UTC
  • Unspecified – původ data a času nebyl specifikován

Následující kód všechny tyto hodnoty vypisuje:

// vrací "Local"
Console.WriteLine(DateTime.Now.Kind);

// vrací: "Utc"
Console.WriteLine(DateTime.UtcNow.Kind);

// vrací: "Unspecified"
Console.WriteLine(new DateTime(2012, 6, 22, 10, 57, 15).Kind);

Význam hodnot Local a Utc je zde celkem jasný. Získávám totiž čas skrze vlastnosti Now a UtcNow, které vlastnost Kind přednastaví. Poslední řádek ale vrací Unspecified – důvodem je to, že jsem při vytváření hodnoty času nijak nespecifikoval, zda byla hodnota získána podle UTC nebo lokálního času. Pokud však chcete vlastnost nastavit, lze její hodnotu předat konstruktoru struktury DateTime jako další parametr. Například:

// explicitně určujeme druh času
Console.WriteLine(new DateTime(2012, 6, 22, 10, 57, 15, DateTimeKind.Local).Kind);

Význam DateTimeKind a konverze

Tato vlastnost Kind slouží pouze funkcím pro konverzi mezi časovými pásmy. V první řadě nijak neovlivňuje běžnou práci s datem a časem jako je například porovnávání. To demonstruje následující příklad:

// výsledek bude "true" - hodnoty mají sice nastavený jiný druh, ale ten na porovnávání nemá vliv
var dateTime1 = new DateTime(2012, 6, 22, 10, 57, 15, DateTimeKind.Local);
var dateTime2 = new DateTime(2012, 6, 22, 10, 57, 15, DateTimeKind.Utc);
Console.WriteLine(dateTime1 == dateTime2);

Kdy tedy tuto vlastnost uplatníme? Existují dvě konverzní metody nad hodnotou DateTime:

  • ToUniversalTime – konvertuje čas z lokálního na UTC – pokud je Kind již nastaven na “Utc”, příkaz hodnotu nekonvertuje a vrací stejnou hodnotou
  • ToLocalTime – konvertuje čas z UTC na lokální – pokud je Kind již nastaven na “Local”, příkaz hodnotu nekonvertuje a vrací stejnou hodnotou

V následujícím příkladu je konverze předvedena:

var dateTime1 = DateTime.Now;

// 22.6.2012 12:22:41 / Local
Console.WriteLine("{0} / {1}", dateTime1, dateTime1.Kind);

// 22.6.2012 10:22:41 / Local
var dateTimeLocal = dateTime1.ToLocalTime();
Console.WriteLine("{0} / {1}", dateTimeLocal, dateTimeLocal.Kind);

// 22.6.2012 12:22:41 / Utc
var dateTimeUtc = dateTime1.ToUniversalTime();
Console.WriteLine("{0} / {1}", dateTimeUtc, dateTimeUtc.Kind);

V ukázce jsem si získal lokální čas (DateTime.Now), který měl Kind=Local. Při následném volání “ToLocalTime” se ale čas nezměnil, protože vlastnost Kind již byla nastavena na Local. U volání “ToUniversalTime” se konverze provedla a funkce vrátila novou hodnotu s časem UTC a nastavenou vlastností Kind=Utc.

Zcela opačné chování zaznamenáte, pokud jako vstup bude UTC čas. Funkce “ToUniversalTime” neprovede žádnou konverzi, ale “ToLocalTime” čas zkonvertuje. Kód si můžete vyzkoušet podle následujícího příkladu:

var dateTime2 = DateTime.UtcNow;

// 22.6.2012 10:22:41 / Utc
Console.WriteLine("{0} / {1}", dateTime2, dateTime2.Kind);

// 22.6.2012 12:22:41 / Local
var dateTimeLocal = dateTime2.ToLocalTime();
Console.WriteLine("{0} / {1}", dateTimeLocal, dateTimeLocal.Kind);

// 22.6.2012 10:22:41 / Utc
var dateTimeUtc = dateTime2.ToUniversalTime();
Console.WriteLine("{0} / {1}", dateTimeUtc, dateTimeUtc.Kind);

Jinými slovy vlastnost Kind slouží k rozpoznání, zda se má při konverzi mezi lokálním a UTC časem konverze vůbec provést, nebo zda se ponechá aktuální hodnota, protože je již v požadovaném formátu. A samozřejmě může sloužit i vám k rozpoznávání, případně validaci, zda je datum a čas v potřebném formátu.

Poslední ukázka demonstruje chování konverzních metod u hodnoty, která nemá specifikovaný druh původu (Kind=Unspecified), protože nebyla při vytváření DateTime uvedena. U takto definovaných hodnot se konverze na lokální / UTC čas provede v obou případech. Viz následující kód:

var dateTime3 = new DateTime(2012, 6, 22, 10, 22, 41);

// 22.6.2012 10:22:41 / Unspecified
Console.WriteLine("{0} / {1}", dateTime3, dateTime3.Kind);

// 22.6.2012 12:22:41 / Local
var dateTimeLocal = dateTime3.ToLocalTime();
Console.WriteLine("{0} / {1}", dateTimeLocal, dateTimeLocal.Kind);

// 22.6.2012 8:22:41 / Utc
var dateTimeUtc = dateTime3.ToUniversalTime();
Console.WriteLine("{0} / {1}", dateTimeUtc, dateTimeUtc.Kind);

Konverzi do konkrétního časového pásma (jiného než lokálního a UTC) proberu v jednom z dalších dílů.

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Všechny díly tohoto seriálu

3. DateTimeOffset v .NET Frameworku 26.06.2012
2. DateTime v .NET Frameworku 24.06.2012
1. Úvod do časových pásem a letního času 22.06.2012

 

Mohlo by vás také zajímat

Genericita, rozhraní a dědičnost

Jazyk C# je multiparadigmatický, což v praxi znamená, že v něm můžeme dělat hodně věcí. Jak ale do sebe jednotlivá paradigma zapadají? Co se hezky doplňuje a co není vzájemně kompatibilní? V tomto článku chci popsat, jak se chová IEquatable vzhledem k dědičnosti typu T.

Jednoduchý scheduler v .NETu

Asi to znáte – máte nějaký složitější systém na zpracování velkého objemu dat a čas od času potřebujete vykovat nějakou automatizovanou údržbu – typicky smazat všechny položky starší než několika dní. Možností, jak toho dosáhnout, je hodně. Snažil jsem se vymyslet něco jednoduchého a efektivního.

Visual Studio 2017 je venku!

 

 

Nový příspěvek

 

Diskuse: DateTime v .NET Frameworku

Dobrý den, předem bych chtěl poděkovat za zajímavý článek. Není mi však jasná jedna věc a to jak funguje formátovací funkce, resp. jak pozná jaký je rozdíl mezi UTC a Local time, když DateTime neobsahuje informaci a časové zóně. Je kdesi uvnitř DateTime uložena informace jak o UTC tak i o Local Time?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dobrý den,

zda se jedná o UTC nebo lokální čas určuje vlastnost Kind (což je ve článku popsáno). Více informací v sobě nenese. Všechny konverze pak probíhají v aktuálním časovém pásmu nastaveném v systému.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback