DateTime ve DateTimeOffset


742

Şu anda, .NET ile DateTimeTimeZone farkında bir şekilde başa çıkmanın standart bir yoluna sahibiz : DateTimeUTC ürettiğimizde (örneğin kullanarak DateTime.UtcNow) bunu yaptığımızda ve her birini görüntülediğimizde UTC'den kullanıcının yerel saatine geri dönüş yapıyoruz .

Bu iyi çalışıyor, ancak DateTimeOffsetnesnenin kendisinde yerel ve UTC saatini nasıl okuduğunu ve nasıl yakaladığını okudum . Yani soru şu DateTimeOffsetki, daha önce yaptığımızla karşılaştırıldığında kullanmanın avantajları ne olurdu ?


3
Aşağıda bazı harika cevaplar var. Ama yine de, eğer bir şey varsa, DateTimeOffset'i kullanmaya başlamak için neyi ikna edebileceğini merak ettim.
HappyNomad


Depolama söz konusu olduğunda, stackoverflow.com/questions/4715620/… da ilginçtir.
Dejan

Yanıtlar:


1169

DateTimeOffsetbir temsilidir anlık zamanda (aynı zamanda mutlak zaman ). Bununla, herkes için evrensel olan bir anı kastediyorum ( artık saniye veya zaman genişlemesinin göreceli etkilerini hesaba katmamak ). Anlık zamanı temsil etmenin bir başka yolu, DateTimenerede .Kindolduğudur DateTimeKind.Utc.

Bu, birinin takviminde bir konum olan takvim saatinden ( sivil saat olarak da bilinir ) farklıdır ve dünyanın her yerinde birçok farklı takvim vardır. Bu takvimlere zaman dilimi diyoruz . Takvim süresi, DateTimenerede .Kindolduğu DateTimeKind.Unspecifiedveya ile temsil edilir DateTimeKind.Local. Ve .Localyalnızca sonucu kullanan bilgisayarın nerede konumlandığına dair zımni bir anlayışa sahip olduğunuz senaryolarda anlamlıdır. (Örneğin, bir kullanıcının iş istasyonu)

Öyleyse, neden DateTimeOffsetbir UTC yerine DateTime? Her şey perspektifle ilgili. Bir benzetme kullanalım - fotoğrafçı gibi davranacağız.

Bir takvim zaman çizelgesinde durduğunuzu, önünüzde ortaya çıkan anlık zaman çizelgesindeki bir kişiye kamerayı işaret ettiğinizi düşünün. Kameranızı saat diliminizin kurallarına göre sıralarsınız - gün ışığından yararlanma saati veya saat diliminizin yasal tanımındaki diğer değişiklikler nedeniyle düzenli olarak değişir. (Sabit bir eliniz yok, bu yüzden kameranız titriyor.)

Fotoğrafta duran kişi, kameranızın geldiği açıyı görür. Başkaları fotoğraf çekiyorsa, farklı açılardan olabilirler. Temsilin Offsetparçası budur DateTimeOffset.

Kameranızı "Doğu Saati" olarak etiketlerseniz, bazen -5'i, bazen de -4'ü işaret edersiniz. Tüm dünyada farklı şeyler etiketlenmiş kameralar var ve hepsi farklı açılardan aynı anlık zaman çizgisine işaret ediyor. Bazıları birbirinin hemen yanında (veya üstünde), bu yüzden ofseti bilmek zamanın hangi zaman dilimiyle ilişkili olduğunu belirlemek için yeterli değil.

Peki ya UTC? Sabit bir ele sahip olması garanti edilen tek kamera. Bir tripod üzerinde, yere sıkıca tutturulmuş. Hiçbir yere gitmiyor. Perspektif açısına sıfır ofseti diyoruz.

Anlık Zaman ve Takvim Süresi Görselleştirme

Peki - bu benzetme bize ne anlatıyor? Bazı sezgisel yönergeler sağlar.

  • Özellikle bir yere göre zamanı temsil ediyorsanız, bunu takvim zamanında a ile temsil edin DateTime. Sadece bir takvimi başka bir takvimle karıştırmamaya dikkat edin. Unspecifiedvarsayımınız olmalı. Localsadece yararlı geliyor DateTime.Now. Örneğin, DateTime.Nowbir veritabanına alıp kaydedebilirim - ancak aldığımda bunun olduğunu varsaymalıyım Unspecified. Yerel takvimimin ilk alındığı takvim olduğuna güvenemiyorum.

  • Andan her zaman emin olmanız gerekiyorsa, anlık zamanı temsil ettiğinizden emin olun. DateTimeOffsetZorlamak için kullanın veya DateTimekurallara göre UTC kullanın .

  • Anlık bir anı izlemeniz gerekiyorsa, ancak "Kullanıcı yerel takviminde saatin kaç olduğunu düşündü?" - o zaman gerekir bir kullanın DateTimeOffset. Bu, zaman işleyişi sistemleri için çok önemlidir - hem teknik hem de yasal kaygılar için.

  • Önceden kaydedilmiş bir değişikliği değiştirmeniz DateTimeOffsetgerekirse, yeni ofseti hala kullanıcı için uygun olduğundan emin olmak için yalnızca ofsette yeterli bilgiye sahip değilsiniz. Sen gerekir ayrıca bir zaman dilimi tanımlayıcı saklamak (düşünürsek - pozisyon değişmiş olsa bile yeni bir resim alabilir bu yüzden bu kameranın adı lazım).

    Ayrıca, Noda Time'ın bunun için bir temsili olduğuna dikkat edilmelidir ZonedDateTime, ancak .Net temel sınıf kütüphanesinde benzer bir şey yoktur. Hem a hem de DateTimeOffsetbir TimeZoneInfo.Iddeğer depolamanız gerekir .

  • Bazen, "kim bakarsa" yerel olan bir takvim süresini temsil etmek istersiniz. Örneğin, bugün ne anlama geldiğini tanımlarken . Bugün her zaman gece yarısından gece yarısına kadardır, ancak bunlar anlık zaman çizelgesinde sonsuz sayıda örtüşen aralığı temsil eder. (Uygulamada sınırlı sayıda zaman dilimimiz var, ancak ofsetleri kene kadar ifade edebilirsiniz) Bu gibi durumlarda, "kim soruyor?" tek bir zaman dilimine yöneltiniz veya bunları uygun şekilde anında zamana çevirmekle ilgileniniz.

İşte DateTimeOffsetbu benzetmeyi yedekleyen birkaç küçük parça ve düz tutmak için bazı ipuçları:

  • İki DateTimeOffsetdeğeri karşılaştırırsanız , karşılaştırma yapmadan önce bunlar sıfır ofsete normalleştirilir. Başka bir deyişle, 2012-01-01T00:00:00+00:00ve 2012-01-01T02:00:00+02:00aynı anlık anı ifade eder ve bu nedenle eşdeğerdir.

  • Herhangi bir birim testi yapıyorsanız ve ofsetten emin olmanız gerekiyorsa, hemDateTimeOffset değeri hem de .Offsetözelliği ayrı ayrı test edin .

  • DateTimeHerhangi bir DateTimeOffsetparametre veya değişkene geçmenizi sağlayan .Net çerçevesine yerleşik tek yönlü bir örtülü dönüştürme vardır . Bunu yaparken, önemli . Bir UTC türünü geçerseniz, sıfır ofseti ile taşınır, ancak ikisinden birini geçerseniz veya yerel olduğunu varsayar . Çerçeve temelde, "Peki, takvim zamanını anlık zamana dönüştürmemi istediniz, ancak bunun nereden geldiğine dair hiçbir fikrim yok, bu yüzden sadece yerel takvimi kullanacağım." Farklı bir saat dilimine sahip bir bilgisayara belirtilmemişse, bu çok büyük bir sorun. (IMHO - bu bir istisna atmalı - ama değil.).Kind.Local.UnspecifiedDateTime

Utanmaz Fiş:

Birçok kişi bu benzetmeyi son derece değerli bulduklarını benimle paylaştı, bu yüzden bunu Çoğul Görüş dersim olan Date and Time Fundamentals'a dahil ettim . İkinci modül olan "Bağlam Önemlidir" bölümünde, "Takvim Süresi ve Anlık Zaman" başlıklı klipte kamera benzetmesinin adım adım izlenmesini bulacaksınız.


4
@ZackJannsen DateTimeOffsetC # ' a sahipseniz DATETIMEOFFSET, SQL Server'da bunu devam ettirmelisiniz . DATETIME2veya sadece DATETIME(gereken aralığa bağlı olarak) normal DateTimedeğerler için uygundur. Evet - yerel bir saati herhangi bir saat dilimi + dto veya utc eşleştirmesinden çözebilirsiniz. Fark şu: Her çözümde kuralları her zaman hesaplamak mı istiyorsunuz, yoksa bunları önceden hesaplamak mı istiyorsunuz? Birçok durumda (bazen yasal kaygılar için) DTO daha iyi bir seçimdir.
Matt Johnson-Pint

3
@ZackJannsen Sorunuzun ikinci kısmı için olabildiğince sunucu tarafı yapmanızı tavsiye ederim. Javascript, saat dilimi hesaplaması için harika değildir. Yapmanız gerekiyorsa, bu kitaplıklardan birini kullanın . Ancak sunucu tarafı en iyisidir. Daha ayrıntılı sorularınız varsa, lütfen onlar için yeni bir SO sorusu başlatın ve eğer yapabilirsem cevaplayacağım. Teşekkürler.
Matt Johnson-Pint

4
@JoaoLeme - Bu, nereden aldığınıza bağlıdır. DateTimeOffset.NowSunucuda söylerseniz , gerçekten de sunucunun ofsetini alacaksınız. Mesele, DateTimeOffsettipin bu ofseti tutabilmesidir. Bunu istemcide kolayca yapabilir, sunucuya gönderebilirsiniz ve sonra sunucunuz istemcinin ofsetini bilir.
Matt Johnson-Pint

8
Gerçekten kamera benzetmesini seviyorum.
Sachin Kainth

2
Evet doğru. Ancak DTO (utc zaman, ofset) çifti olarak değil (yerel saat, ofset) çifti olarak saklanır. Başka bir deyişle, UTC'den sapma yerel saate zaten yansır. Utc'a geri dönmek için, ofset işaretini ters çevirin ve yerel saate uygulayın.
Matt Johnson-Pint

328

Microsoft'tan:

DateTimeOffset değerleri için bu kullanımlar DateTime değerleri için olanlardan çok daha yaygındır. Sonuç olarak, DateTimeOffset uygulama geliştirme için varsayılan tarih ve saat türü olarak kabul edilmelidir.

source: "DateTime, DateTimeOffset, TimeSpan ve TimeZoneInfo Arasında Seçim Yapma" , MSDN

DateTimeOffsetUygulamamız zaman içinde belirli noktalarla (örneğin bir kayıt oluşturulduğunda / güncellendiğinde) ilgilenirken neredeyse her şey için kullanıyoruz . Bir yan not olarak, DATETIMEOFFSETSQL Server 2008'de de kullanıyoruz.

Gördüğüm DateTimegenel anlamda, sadece tarihleri ile uğraşmak istediğinizde yararlı olarak biriyle kez sadece ya bir anlaşma. Örneğin, her gün sabah 7'de kapatmak istediğiniz bir alarmınız varsa, bunu bir DateTimekullanımında saklayabilirsiniz DateTimeKind, Unspecifiedçünkü DST'den bağımsız olarak saat 7'de kapanmasını istersiniz. Ancak, alarm olaylarının geçmişini temsil etmek istiyorsanız, kullanırsınız DateTimeOffset.

Bir karışımı kullanırken DateTimeOffsetve DateTimeözellikle türler arasında atama ve karşılaştırma yaparken dikkatli olun . Ayrıca, yalnızca DateTimeaynı örnekleri karşılaştırın , DateTimeKindçünkü DateTimekarşılaştırma sırasında saat dilimi uzaklığını yok sayar.


146
Kabul edilen cevap aşırı uzun ve benzetme gergin, bu IMO çok daha iyi ve daha özlü bir cevap.
nexus

10
Sadece bu cevabı da sevdiğimi söyleyeceğim ve iptal edeceğim. Son bölümde olsa da - Kindaynı olmalarını sağlamak bile , karşılaştırma hatalı olabilir. Eğer her iki taraf da DateTimeKind.Unspecifiedaynı saat diliminden geldiğini bilmiyorsunuz. Her iki taraf da ise DateTimeKind.Local, çoğu karşılaştırmalar ince olacak, ama yine de hatalar olabilir bir tarafı yerel saat diliminde belirsiz olmasıdır. Gerçekten sadece DateTimeKind.Utckarşılaştırmalar kusursuzdur ve evet, DateTimeOffsetgenellikle tercih edilir. (Şerefe!)
Matt Johnson-Pint

1
+1 Buna eklerdim: Seçtiğiniz DataType, niyetinizi yansıtmalıdır. DateTimeOffset'i her yerde kullanmayın, sadece neden. Ofset, Hesaplamalar ve Okuma-From / Persisting-DataBase için önemliyse, DateTimeOffset kullanın. Önemli değilse, DateTime'ı kullanın, böylece Ofset'in hiçbir yönü olmaması ve Times'ın C # Kodunuzun üzerinde çalıştığı Sunucu / Makinenin Konumuna göre kalması gerektiğini (yalnızca DataType'a bakarak) anlarsınız.
MikeTeeVee

"Ancak, alarm olaylarının geçmişini temsil etmek isterseniz, DateTimeOffset kullanırsınız." Bunun neden olduğunu düşündüğünüzü açıklamak ister misiniz? Bu sayfadaki tüm bilgileri okuyarak bunu doldurabilirim, ancak belki de bu bilgileri kolay okunabilir bir şekilde sağlayabilirsiniz. Bu çok okunabilir bir cevap.
Barrosy

77

DateTime, yerel saat ve UTC olmak üzere yalnızca iki ayrı zaman kaydedebilir. Tür özelliği ettiğini gösterir.

DateTimeOffset, yerel saatleri dünyanın herhangi bir yerinden depolayabilmesiyle bunu genişletir. Ayrıca , yerel saat ile UTC arasındaki farkı da kaydeder. Bu UTC ofsetini saklamak için sınıfınıza fazladan bir üye eklemezseniz DateTime'ın bunu nasıl yapamayacağını unutmayın. Veya yalnızca UTC ile çalışın. Kendi içinde iyi bir fikir btw.


33

DateTimeOffsetMantıklı olan birkaç yer var . Bunlardan biri, tekrar eden etkinlikler ve yaz saati uygulamasıyla uğraştığınız zamandır. Diyelim ki her gün sabah 9'da çalacak bir alarm ayarlamak istiyorum. "UTC olarak depola, yerel saat olarak görüntüle" kuralını kullanırsam, gün ışığından yararlanma saati geçerli olduğunda alarm farklı bir saatte çalacaktır .

Muhtemelen başkaları da var, ancak yukarıdaki örnek aslında geçmişte karşılaştığım bir örnek (bu DateTimeOffsetBCL'ye eklenmeden önceydi - o zamanki çözümüm zamanı yerel saat diliminde açıkça saklamak ve kaydetmekti. saat dilimi bilgilerinin yanı sıra: temel olarak DateTimeOffsetdahili olarak ne yapar).


12
DateTimeOffset DST sorununu
çözmez

2
TimeZoneInfo sınıfının kullanılması DST için kurallar taşır. .net 3.5 veya sonraki bir sürüm kullanıyorsanız, saat dilimi farkı ile birlikte yaz saati uygulaması gereken tarihlerle başa çıkmak için TimeZone veya TimeZoneInfo sınıflarını kullanın.
Ocak'ta Zack Jannsen

1
Evet iyi bir istisna örneği (alarm uygulaması) ancak zaman, tarihten daha önemli olduğunda, uygulama için zamanlama veri yapınızda ayrı olarak saklamanız gerekir, örneğin oluşum türü = Günlük ve saat = 09:00. Burada amaç, geliştiricinin kullanıcılara ne tür bir tarih kaydettiklerini, hesapladıklarını veya sunduklarını bilmeleri gerektiğidir. Özellikle uygulamalar daha küresel olma eğilimindedir, şimdi standart ve büyük uygulama mağazaları için internet olarak yazılım yazıyoruz. Yan düğüm olarak, Microsoft'un ayrı bir Tarih ve Saat yapısı eklediğini de görmek istiyorum.
Tony Wall

3
Jarrett ve Zack'in yorumlarını özetleme: DateTimeOffset'in tek başına DST problemini işlemeyeceği, ancak DateTimeOffset'in TimeZoneInfo ile birlikte kullanılması sorunu halledeceği anlaşılıyor. Bu tür Utc olan DateTime'dan farklı değildir. Her iki durumda da, anı yansıttığım takvimin saat dilimini (sadece ofset değil) bilmeliyim. (Bunu bir kullanıcının profilinde saklayabilir veya mümkünse istemciden (örneğin Windows) alabilirim). Kulağa doğru geliyor mu?
Jeremy Cook

"DateTimeOffset'in mantıklı olduğu birkaç yer var." --- Tartışmasız, çoğu zaman anlamsızdır.
Ronnie Overby

23

En önemli ayrım DateTime'ın saat dilimi bilgilerini saklamaması, DateTimeOffset ise saklamamasıdır.

DateTime, UTC ve Yerel arasında ayrım yapsa da, kendisiyle ilişkilendirilmiş kesin bir saat dilimi farkı yoktur. Herhangi bir serileştirme veya dönüştürme işlemi yaparsanız, sunucunun saat dilimi kullanılır. Bir UTC saatini dengelemek için dakikalar ekleyerek el ile yerel bir saat oluştursanız bile, serileştirme adımında yine de biraz alabilirsiniz, çünkü (DateTime'da herhangi bir açık ofset olmaması nedeniyle) sunucunun saat dilimi ofsetini kullanacaktır.

Örneğin, DateTime değerini Json.Net ve ISO tarih biçimini kullanarak Kind = Local ile serileştirirseniz, buna benzer bir dize alırsınız 2015-08-05T07:00:00-04. Son bölümün (-04) DateTime veya hesaplamak için kullandığınız herhangi bir ofset ile ilgisi olmadığına dikkat edin ... sadece sunucunun saat dilimi ofseti.

Bu arada, DateTimeOffset açıkça ofseti içerir. Saat diliminin adını içermeyebilir, ancak en azından ofseti içerir ve serileştirirseniz, sunucunun yerel saati ne olursa olsun değerinize açıkça dahil edilen ofseti alırsınız.


14
yukarıdaki tüm cevaplarla, merak ediyorum neden kimse hepsini özetleyen tek bir cümle yazmak için The most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
uğraşmadı

9
DateTimeOffset saat dilimi bilgilerini depolamaz. "DateTime, DateTimeOffset, TimeSpan ve TimeZoneInfo arasında seçim yapma" başlıklı MS dokümanı şunları belirtir: "Bir DateTimeOffset değeri belirli bir saat dilimine bağlı değil, ancak çeşitli saat dilimlerinden herhangi birinden kaynaklanabilir". Bununla birlikte, DateTimeOffset IS saat dilimi AWARE, UTC'den gelen farkı içerir, bu da tüm farkı yaratır ve bu nedenle tarih bilgileriyle ilgilenen uygulama geliştirme ile uğraşırken MS tarafından önerilen varsayılan sınıftır. Verilerin hangi belirli saat diliminden geldiğini gerçekten önemsiyorsanız, bunu ayrı olarak korumalısınız
stonedauwg

1
Evet, ancak birçok yerde gösterildiği gibi, + veya - saat hangi saat diliminde olduğunuz hakkında hiçbir şey söylemez ve sonuç olarak işe yaramaz. Ne yapmanız gerektiğine bağlı olarak, aynı zamanda bir tarih saatini Kind olarak depolayabilirsiniz.
Arwin

13

Microsoft'un bu kod parçası her şeyi açıklıyor:

// Find difference between Date.Now and Date.UtcNow
  date1 = DateTime.Now;
  date2 = DateTime.UtcNow;
  difference = date1 - date2;
  Console.WriteLine("{0} - {1} = {2}", date1, date2, difference);

  // Find difference between Now and UtcNow using DateTimeOffset
  dateOffset1 = DateTimeOffset.Now;
  dateOffset2 = DateTimeOffset.UtcNow;
  difference = dateOffset1 - dateOffset2;
  Console.WriteLine("{0} - {1} = {2}", 
                    dateOffset1, dateOffset2, difference);
  // If run in the Pacific Standard time zone on 4/2/2007, the example
  // displays the following output to the console:
  //    4/2/2007 7:23:57 PM - 4/3/2007 2:23:57 AM = -07:00:00
  //    4/2/2007 7:23:57 PM -07:00 - 4/3/2007 2:23:57 AM +00:00 = 00:00:00

9

Cevapların çoğu iyidir, ancak daha fazla bilgi için MSDN'nin bazı bağlantılarını eklemeyi düşündüm



7

Önemli bir fark, geçerli olandan farklı zaman dilimlerinde yerel zamanlara dönüştürmek için DateTimeOffsetbirlikte kullanılabilmesidir TimeZoneInfo.

Bu, farklı zaman dilimlerindeki kullanıcılar tarafından erişilen bir sunucu uygulamasında (örn. ASP.NET) kullanışlıdır.


3
@Bugeo Bugeo doğrudur, ancak bir risk vardır. Her biri için "ToUniversalTime" öğesini çağırarak iki DateTime karşılaştırabilirsiniz. Karşılaştırmada DateTimeKind = Belirtilmemiş tam olarak bir değeriniz varsa, stratejiniz başarısız olur. Bu başarısızlık potansiyeli, yerel saate dönüşüm gerektiğinde DateTime üzerinde DateTimeOffset'i dikkate almanın bir nedenidir.
Ocak'ta Zack Jannsen

Yukarıdaki gibi, bu senaryoda TimeZoneId depolamak iwth off nihayetinde hiçbir şey anlamına DateTimeOffset, daha iyi olduğunu düşünüyorum.
Arwin

2

Ben DateTimeOffset gördüğüm tek olumsuz tarafı Microsoft "unuttum" (tasarım gereği) kendi XmlSerializer sınıfında desteklemektir. Ama o zamandan beri XmlConvert yardımcı sınıfına eklendi.

XmlConvert.ToDateTimeOffset

XmlConvert.ToString

Ben devam edin ve tüm faydaları nedeniyle DateTimeOffset ve TimeZoneInfo kullanın, sadece veya XML (veya sonra tüm iş nesneleri) serileştirilmiş varlıklar oluştururken dikkat edin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.