Sql sunucu tarihlerinin sıfır ofseti. Doğru tarih ve saat kullanımı

Seçenek insancıldır ([bağlantıyı görmek için kayıt olmalısınız]).

Sorunun özü şudur.. Yanlışlıkla bir SQL sunucusuna "tarih ofseti" 0 olan bir veritabanını dağıttıysanız, veritabanında TIME türünde bir öznitelikle karşılaşıldığında bir sorun ortaya çıktı, yani 01.01.0001 10 :30:00 veya tarih ayarlanmış bu öznitelikte kayıtlı boş 01.01.0001 00:00:00. Böyle bir özniteliği kaydederken kaydedilmez.
İnternette, 2000 ofset ile yeni bir üs oluşturmayı öneriyorlar.
Ama gerçekten yeni bir temel oluşturmak istemedim. Ve tüm kullanıcılar için veritabanına giden yolu değiştirin.
Sonra yolu takip ettim ve bu değer SQL'de nerede saklanıyor. Bulundu ve 2000 olarak değiştirildi ve her şey yolundaydı ..
Ve şimdi adım adım nerede değiştirileceği.

Tüm kullanıcıları kovun.

DİKKAT!!!

1. İlk yapın destek olmak 1C aracılığıyla yani * .dt içinde boşaltın
Bu, "ofset" değiştirilmeden önce yapılmalıdır.
Bu yapılmazsa, tüm veritabanınızda, yönlendirmelerde, dokümanda vb.
bir sahne varsa, tarih örneğin 02.10.0009 olacaktır.
NELER İZİN VERİLMEMEKTEDİR….
Yani * .dt'ye bir yükleme yaptınız

2. SQL Server Management Studio'ya gidin
Listede üssünüzü bulun, artı işaretine tıklayın.
Oradaki "Masalar" babayı bul ve aç.
Bir sürü masa açılacak, en alta in, masayı bul
_YearOffset, üzerinde duruyoruz ve sağ tuşla "Tabloyu aç" öğesini seçiyoruz, bkz. Şekil 1
0 değerini 2000 olarak değiştirin
SQL Server Management Studio'yu kapatın

3. Konfigüratöre gidin ve önceden kaydedilmiş veritabanını yükleyin.

Bu yapılmazsa, tüm tarihler 0009 yılı ile olacaktır.
Veritabanı yüklendikten sonra... 1C'ye giderek tarihlerin normal olduğundan emin olabilirsiniz.
Sonuç "tarih ofsetini 0'dan 2000'e" değiştirdik

Bazen bu seçenek bir nedenden dolayı kullanılamaz. Sonra daha sert bir seçenek var ([bağlantıyı görüntülemek için kaydolmanız gerekir]):

için TablesAndFields imlecini bildir

Tablo adı olarak nesneler.adı, sütun adı olarak sütunlar.adı SEÇİN
dbo.sysobjects'DEN olarak nesneler
dbo.syscolumns öğesini object.id = column.id üzerindeki sütunlar olarak sola birleştir
burada object.xtype = "U" ve column.xtype = 61

TablesAndFields'ı aç

WHILE @@ FETCH_STATUS = 0
Yürütmeye BAŞLA (" Güncelleme "+ @TabloAdı + "
Ayarlamak " + @SütunAdı + " = ""2000- 01- 01 00:00:00" "
nerede " + @SütunAdı + " > ""3999- 12- 31 23:59:59" "")

Bu, önceki getirme başarılı olduğu sürece yürütülür.
TablesAndFields'dan @TableName, @ColumnName'e SONRAKİ AL
SON

TablesAndFields'ı kapat
TablesAndFields'ı serbest bırak
Git

Herhangi bir manipülasyondan önce, veritabanlarının kopyalarını almayı unutmayın!

Hemen hemen tüm projeler, yanlış kullanım ve tarih ve saatin saklanmasından kaynaklanan sorunlarla karşı karşıyadır. Proje aynı saat diliminde kullanılsa bile, kış / yaz saatine geçtikten sonra yine de hoş olmayan sürprizlerle karşılaşabilirsiniz. Aynı zamanda, başlangıçtan itibaren doğru mekanizmanın uygulanmasından çok az insan şaşırır, çünkü her şey önemsiz olduğu için bununla ilgili herhangi bir sorun olamaz gibi görünüyor. Ne yazık ki, sonraki gerçekler durumun böyle olmadığını gösteriyor.

Mantıksal olarak, tarih ve saatle ilgili aşağıdaki değer türleri ayırt edilebilir:


Unutmadan, her bir öğeyi ayrı ayrı ele alalım.

tarih ve saat

Diyelim ki analiz için malzeme toplayan laboratuvar +2 saat diliminde ve analizlerin zamanında yürütülmesini izleyen merkez şube +1 saat diliminde. Örnekte gösterilen süre, malzemenin ilk laboratuvar tarafından toplanması sırasında not edilmiştir. Soru ortaya çıkıyor - merkez ofis hangi saati görmeli? Açıkça, merkez ofis yazılımı 15 Ocak 2014 12:17:15 - bir saat daha az göstermelidir, çünkü saatlerine göre olay o anda gerçekleşti.

Verilerin istemciden sunucuya geçtiği ve istemcinin geçerli saat dilimine göre tarihi / saati her zaman doğru bir şekilde görüntülemenizi sağlayan olası eylem zincirlerinden birini düşünün:

  1. Değer, istemcide oluşturulur, örneğin 2 Mart 2016 15 : 13: 36, müşteri +2 saat diliminde.
  2. Değer, sunucuya iletilmek üzere bir dize temsiline dönüştürülür - “2016-03-02T 15 :13:36+02:00”.
  3. Serileştirilmiş veriler sunucuya gönderilir.
  4. Sunucu, saati bir tarih / saat nesnesine seri hale getirerek geçerli saat dilimine dönüştürür. Örneğin, sunucu +1'de çalışıyorsa, nesne 2 Mart 2016'yı içerecektir. 14 :13:36.
  5. Sunucu, verileri veritabanına kaydederken, saat dilimi hakkında herhangi bir bilgi içermez - en sık kullanılan tarih / saat türleri bu konuda hiçbir şey bilmez. Böylece veritabanı 2 Mart 2016'da kaydedilecek 14 : 13: 36 "bilinmeyen" bir zaman diliminde.
  6. Sunucu, veri tabanından veri okur ve 2 Mart 2016 değerine karşılık gelen bir nesne oluşturur. 14 : 13: 36. Ve sunucu +1 saat diliminde çalıştığı için bu değer de aynı saat diliminde yorumlanacaktır.
  7. Değer, müşteriye iletilmek üzere bir dizi temsiline dönüştürülür - “2016-03-02T 14 :13:36+01:00”.
  8. Serileştirilmiş veriler istemciye gönderilir.
  9. İstemci, alınan değeri bir tarih / saat nesnesine seri hale getirir ve onu geçerli saat dilimine dönüştürür. Örneğin, -5 ise, görüntülenen değer 2 Mart 2016 olmalıdır. 09 :13:36.
Her şey bütüncül görünüyor ama bu süreçte nelerin yanlış gidebileceğini bir düşünelim. Aslında, burada sorunlar hemen hemen her adımda olabilir.
  • İstemcideki saat, saat dilimi olmadan oluşturulabilir - örneğin, .NET'te DateTimeKind.Unspecified ile DateTime yazın.
  • Serileştirme altyapısı, saat dilimi farkını içermeyen bir biçim kullanabilir.
  • Bir nesnenin serisini kaldırırken, özellikle hem sunucuda hem de istemcide "ev yapımı" seri durumdan çıkarıcılarda saat dilimi ofseti yoksayılabilir.
  • Bir veritabanından okurken, bir saat dilimi olmadan bir tarih / saat nesnesi oluşturulabilir - örneğin, .NET'te DateTimeKind.Unspecified ile DateTime yazın. Ayrıca, pratikte, düzeltme okumasından hemen sonra başka bir DateTimeKind'i açıkça belirtmezseniz, .NET'te DateTime ile tam olarak bu olur.
  • Paylaşılan veritabanı ile çalışan uygulama sunucuları farklı zaman dilimlerinde ise zaman farkları konusunda ciddi bir kafa karışıklığı yaşanacaktır. Sunucu A tarafından veritabanına yazılan ve Sunucu B tarafından okunan tarih / saat değeri, Sunucu B tarafından yazılan ve Sunucu A tarafından okunan aynı orijinal değerden önemli ölçüde farklı olacaktır.
  • Uygulama sunucularını bir bölgeden diğerine taşımak, önceden depolanmış tarih/saat değerlerinin yanlış yorumlanmasına neden olacaktır.
Ancak yukarıda açıklanan zincirdeki en ciddi kusur, sunucuda yerel saat diliminin kullanılmasıdır. Gün ışığından yararlanma saati geçişi yoksa, ek bir sorun olmayacaktır. Ancak aksi takdirde, birçok hoş olmayan sürprizle karşılaşabilirsiniz.

Yaz / kış saatine dönüştürme kuralları, kesinlikle, değişkendir. Farklı ülkeler bazen kurallarını değiştirebilir ve bu değişiklikler önceden sistem güncellemelerine dahil edilmelidir. Uygulamada, bu mekanizmanın yanlış çalışması durumlarıyla tekrar tekrar karşılaştık, sonuç olarak düzeltmeler yüklenerek çözüldü veya işletim sistemi veya kullanılmış üçüncü taraf kitaplıkları... Aynı sorunların tekrarlanma olasılığı sıfır değildir, bu nedenle onlardan kaçınmanın garantili bir yolunun olması daha iyidir.

Yukarıda açıklanan hususları dikkate alarak, zamanın iletilmesi ve saklanması için en güvenilir ve basit yaklaşımı formüle edeceğiz: sunucuda ve veritabanında tüm değerler UTC saat dilimine dönüştürülmelidir.

Bu kuralın bize ne verdiğini düşünün:

  • Sunucuya veri gönderirken, sunucunun zamanı UTC'ye doğru bir şekilde dönüştürebilmesi için istemcinin saat dilimi farkını geçmesi gerekir. Alternatif seçenek- müşteriyi bu dönüşümü yapmaya zorlayın, ancak ilk seçenek daha esnektir. İstemci, sunucudan veri alırken, her durumda UTC'deki zamana geleceğini bilerek tarih ve saati kendi yerel saat dilimine çevirecektir.
  • UTC'de yaz saati uygulaması ve kış saati geçişleri yoktur, bu nedenle bununla ilgili sorunlar önemsiz olacaktır.
  • Sunucuda, veritabanından okurken zaman değerlerini dönüştürmeniz gerekmez, UTC'ye karşılık geldiğini açıkça belirtmeniz yeterlidir. Örneğin NET'te bu, time nesnesi için DateTimeKind öğesini DateTimeKind.Utc olarak ayarlayarak gerçekleştirilebilir.
  • Ortak bir veritabanı ile çalışan sunucular arasındaki saat dilimleri farkı ve sunucuların bir bölgeden diğerine aktarılması, alınan verilerin doğruluğunu hiçbir şekilde etkilemeyecektir.
Böyle bir kuralı uygulamak için üç şeye dikkat etmek yeterlidir:
  1. Serileştirme ve seri durumdan çıkarma mekanizmasını, tarih/saat değerlerinin UTC'den yerel saat dilimine ve geriye doğru çevrilmesini sağlayacak şekilde yapın.
  2. Sunucu tarafı seri kaldırıcının UTC'de tarih/saat nesneleri oluşturduğundan emin olun.
  3. Veritabanından okurken, UTC'de tarih/saat nesnelerinin oluşturulduğundan emin olun. Bu madde bazen kod değişikliği olmadan sağlanır - sadece tüm sunuculardaki sistem saat dilimi UTC'ye ayarlanır.
Yukarıdaki hususlar ve yönergeler harika çalışıyor iki koşulun bir kombinasyonu ile:
  • Sistem gereksinimlerinin yerel saati ve/veya saat dilimi farkını tam olarak kaydedildiği gibi görüntülemesine gerek yoktur. Örneğin uçak biletlerinde, kalkış ve varış saatleri, havalimanının konumuna karşılık gelen saat diliminde yazdırılmalıdır. Veya sunucu farklı ülkelerde oluşturulan faturaları yazdırmak üzere gönderirse, her birinin sonunda sunucunun saat dilimine dönüştürülmeden yerel saat olmalıdır.
  • Sistemdeki tüm tarih ve saat değerleri "mutlak" - yani. UTC'de tek bir değere karşılık gelen gelecekte veya geçmişte bir zaman noktasını tanımlar. Örneğin, "fırlatma aracının lansmanı Kiev saatiyle 23:00'te gerçekleşti" veya "toplantı Minsk saatiyle 13:30-14:30 arasında yapılacak." Farklı zaman dilimlerinde, bu olayların sayıları farklı olacaktır, ancak zaman içinde aynı anı tanımlayacaklardır. Ancak, gereksinimlerin yazılım bazı durumlarda "göreceli" yerel saati ifade eder. Örneğin, "Bu TV programı, TV kanalının bir şubesi olan her ülkede sabah 9:00 ile 10:00 arasında yayınlanacaktır." Programın gösterisinin bir olay değil, birkaç tane olduğu ve potansiyel olarak hepsinin "mutlak" ölçekte farklı zaman aralıklarında meydana gelebileceği ortaya çıktı.
Birinci koşulun ihlal edildiği durumlarda, hem sunucuda hem de veritabanında saat dilimini içeren veri türleri kullanılarak sorun çözülebilir. Aşağıda, farklı platformlar ve DBMS için küçük bir örnek listesi bulunmaktadır.
.AĞ TarihSaatOfseti
Java org.joda.time.DateTime, java.time.ZonedDateTime
MS SQL tarihzaman ofset
Oracle, PostgreSQL ZAMAN DİLİMİ İLE TIMESTAMP
MySQL

İkinci koşulun ihlali daha zor bir durumdur. Bu "göreceli" zamanın yalnızca görüntüleme için saklanması gerekiyorsa ve belirli bir zaman dilimi için bir olayın meydana geldiği veya gerçekleşeceği zamandaki "mutlak" anı belirlemek için herhangi bir görev yoksa, yalnızca zaman dönüştürmeyi devre dışı bırakmak yeterlidir. . Örneğin, kullanıcı 25 Mart 2016 saat 9:00'da TV şirketinin tüm şubeleri için iletim başlangıcını girdi ve bu formda iletilecek, saklanacak ve görüntülenecektir. Ancak bazı programlayıcıların, her programın başlangıcından bir saat önce otomatik olarak özel eylemleri gerçekleştirmesi gerekebilir (bildirim gönderin veya TV şirketinin veritabanında bazı verilerin olup olmadığını kontrol edin). Böyle bir zamanlayıcıyı güvenilir bir şekilde uygulamak basit bir iş değildir. Diyelim ki planlayıcı her dalın hangi saat diliminde olduğunu biliyor. Ve şubenin olduğu ülkelerden biri bir süre sonra saat dilimini değiştirmeye karar verir. Durum göründüğü kadar nadir değil - bu ve önceki iki yıl için bu tür 10'dan fazla olay saydım (http://www.timeanddate.com/news/time/). Kullanıcıların saat dilimi bağlamalarını güncel tutması gerektiği veya zamanlayıcının bu bilgileri aşağıdaki gibi küresel kaynaklardan otomatik olarak alması gerektiği ortaya çıktı. Google haritaları Saat Dilimi API'si. Bu tür durumlar için evrensel bir çözüm önermeyi taahhüt etmiyorum, sadece bu tür durumların ciddi bir çalışma gerektirdiğini not ediyorum.

Yukarıdan da görebileceğiniz gibi, vakaların %100'ünü kapsayan tek bir yaklaşım yoktur... Bu nedenle, öncelikle yukarıdaki durumlardan hangisinin sisteminizde olacağını gereksinimlerden net bir şekilde anlamanız gerekir. Büyük olasılıkla, her şey UTC'de depolama ile önerilen ilk yaklaşımla sınırlı olacaktır. Eh, açıklanan istisnai durumlar onu iptal etmez, sadece özel durumlar için başka çözümler ekler.

Saatsiz tarih

Diyelim ki müşterinin saat dilimini dikkate alarak tarih ve saatin doğru gösterimini bulduk. Zamansız tarihlere geçelim ve bu dava için başlangıçta belirtilen örnek - "yeni sözleşme 2 Şubat 2016'da yürürlüğe giriyor". Zamanlarla "normal" tarihler gibi değerler için aynı türleri ve aynı mekanizmayı kullanırsanız ne olur?

Tüm platformlar, diller ve DBMS'ler yalnızca tarihi depolayan türlere sahip değildir. Örneğin, .NET'te yalnızca DateTime tipi vardır, ayrı bir “sadece Tarih” yoktur. Böyle bir nesne oluşturulurken sadece tarih belirtilmiş olsa bile, saat hala mevcuttur ve 00:00:00'a eşittir. "2 Şubat 2016 00:00:00" değerini +2 ofsetli bölgeden +1'e çevirirsek "1 Şubat 2016 23:00:00" alırız. Yukarıdaki örnek için bu, bir zaman diliminde yeni sözleşmenin 2 Şubat'ta ve diğerinde - 1 Şubat'ta başlayacağı gerçeğine eşdeğer olacaktır. Hukuki açıdan bakıldığında, bu saçmadır ve elbette böyle olmamalıdır. "Saf" tarihler için genel kural son derece basittir - bu tür değerler kaydetme ve okumanın hiçbir aşamasında dönüştürülmemelidir.

Tarihler için dönüşümden kaçınmanın birkaç yolu vardır:

  • Platform, saati olmayan bir tarihi temsil eden bir türü destekliyorsa, bu kullanılmalıdır.
  • Serileştiriciye şunu söyleyecek nesnelerin meta verilerine özel bir özellik ekleyin: verilen değer zaman dilimi göz ardı edilmelidir.
  • İstemciden tarihi geçirin ve bir dize olarak geri alın ve bir tarih olarak saklayın. Bu yaklaşım, müşterinin yalnızca tarihi görüntülemesi gerekmiyorsa, aynı zamanda üzerinde bazı işlemler gerçekleştirmesi gerekiyorsa uygun değildir: karşılaştırma, çıkarma vb.
  • Bir dize olarak geçirin ve saklayın ve yalnızca istemcinin yerel ayarına göre biçimlendirme için bir tarihe dönüştürün. Önceki seçeneğe göre daha da fazla dezavantajı vardır - örneğin, saklanan dizedeki tarihin bölümleri "yıl, ay, gün" sırasında değilse, o zaman tarih üzerinde etkili bir dizine alınmış arama yapmak imkansız olacaktır. Aralık.
Elbette, bir karşı örnek vermeye çalışabilir ve sözleşmenin yalnızca akdedildiği ülke içinde anlam ifade ettiğini, ülkenin aynı saat diliminde bulunduğunu ve dolayısıyla anını açık bir şekilde belirlemenin mümkün olduğunu söyleyebilirsiniz. yürürlüğe girmesi. Ancak bu durumda bile, diğer saat dilimlerindeki kullanıcılar bu olayın yerel saatlerinin hangi noktasında gerçekleşeceği ile ilgilenmeyecektir. Ve bu anı zaman içinde gösterme ihtiyacı olsa bile, o zaman sadece tarih değil, aynı zamanda başlangıç ​​koşuluyla çelişen saatin de gösterilmesi gerekirdi.

Zaman aralığı

Zaman aralıklarının depolanması ve işlenmesi ile her şey basittir: değerleri saat dilimine bağlı değildir, bu nedenle burada özel bir öneri yoktur. Bir dizi zaman birimi (gerekli kesinliğe bağlı olarak tamsayı veya kayan nokta) olarak saklanabilir ve iletilebilirler. İkinci hassasiyet önemliyse - o zaman saniye sayısı, eğer milisaniye - o zaman milisaniye sayısı vb.

Ancak aralığın hesaplanmasında tuzaklar olabilir. İki olay arasındaki zaman aralığını sayan bir örnek C# kodumuz olduğunu varsayalım:

DateTime başlangıç ​​= DateTime.Now; // ... DateTime bitiş = DateTime.Now; çift ​​saat = (bitiş - başlangıç) .TotalHours;
İlk bakışta, burada herhangi bir sorun yok, ancak durum böyle değil. İlk olarak, bu tür bir kodun birim testinde sorunlar olabilir, ancak bundan biraz sonra bahsedeceğiz. İkinci olarak, zamanın ilk anının kış, son anın ise yaz olduğunu düşünelim (örneğin, çalışma saatleri bu şekilde ölçülür ve işçiler gece vardiyası vardır).

Kodun, 2016'da yaz saatinin 27 Mart gecesi gerçekleştiği bir saat diliminde çalıştığını varsayalım ve yukarıda açıklanan durumu simüle edelim:

DateTime başlangıç ​​= DateTime.Parse ("2016-03-26T20: 00: 15 + 02"); DateTime end = DateTime.Parse ("2016-03-27T05: 00: 15 + 03"); çift ​​saat = (bitiş - başlangıç) .TotalHours;
Bu kod 9 saat ile sonuçlanacaktır, ancak aslında bu anlar arasında 8 saat geçmiştir. Kodu şu şekilde değiştirerek bunu kolayca doğrulayabilirsiniz:

DateTime başlangıç ​​= DateTime.Parse ("2016-03-26T20: 00: 15 + 02").ToUniversalTime (); DateTime end = DateTime.Parse ("2016-03-27T05: 00: 15 + 03").ToUniversalTime (); çift ​​saat = (bitiş - başlangıç) .TotalHours;
Dolayısıyla sonuç - herhangi Aritmetik işlemler tarih ve saat, UTC değerleri veya saat dilimi hakkında bilgi depolayan türler kullanılarak yapılmalıdır. Ve sonra gerekirse yerele geri çevirin. Bu bakış açısından, orijinal örnek DateTime.Now olarak DateTime.UtcNow olarak değiştirilerek kolayca düzeltilebilir.

Bu nüans, belirli bir platforma veya dile bağlı değildir. İşte aynı dezavantaja sahip benzer bir Java kodu:

LocalDateTime başlangıç ​​= LocalDateTime.now (); // ... LocalDateTime bitiş = LocalDateTime.now (); uzun saatler = ChronoUnit.HOURS.between (başlangıç, bitiş);
Ayrıca kolayca düzeltilir - örneğin, LocalDateTime yerine ZonedDateTime kullanılarak.

Planlanmış etkinlikleri planlayın

Zamanlanmış olayları planlamak daha karmaşık bir durumdur. Standart kitaplıklarda programları depolamak için evrensel bir tür yoktur. Ancak böyle bir görev çok nadiren ortaya çıkmaz, bu nedenle hazır çözümler sorunsuz bir şekilde bulunabilir. İyi bir örnek, Quartz gibi başka çözümler tarafından şu veya bu biçimde kullanılan cron zamanlayıcı formatıdır: http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html. Ayın ikinci Cuma günü seçenekleri de dahil olmak üzere neredeyse tüm zamanlama ihtiyaçlarını karşılar.

Çoğu durumda, kendi zamanlayıcınızı yazmanın bir anlamı yoktur, çünkü zamana göre test edilmiş esnek çözümler vardır, ancak herhangi bir nedenle kendi mekanizmanızı oluşturmanız gerekiyorsa, en azından program formatı cron'dan ödünç alınabilir.

Farklı türde zaman değerlerinin saklanması ve işlenmesiyle ilgili yukarıda açıklanan önerilere ek olarak, bahsetmek istediğim başka öneriler de var.

İlk olarak, geçerli saati almak için statik sınıf üyelerini kullanma hakkında - DateTime.UtcNow, ZonedDateTime.now (), vb. Belirtildiği gibi, bunları doğrudan kodda kullanmak birim testini ciddi şekilde karmaşıklaştırabilir, çünkü şimdiki zamanı özel sahte çerçeveler olmadan değiştirmek imkansızdır. Bu nedenle, birim testleri yazmayı planlıyorsanız, bu tür yöntemlerin uygulanmasının değiştirilebileceğinden emin olmalısınız. Bu sorunu çözmenin en az iki yolu vardır:

  • Geçerli saati döndüren tek bir yöntemle IDateTimeProvider arabirimini vurgulayın. Ardından, geçerli saati almanız gereken tüm kod birimlerinde bu arayüze bir bağımlılık ekleyin. Programın normal yürütülmesi sırasında, "varsayılan" uygulama, gerçek zamanı döndüren tüm bu yerlere ve birim testlerinde - diğer gerekli uygulamalara enjekte edilecektir. Bu yöntem, test açısından en esnek olanıdır.
  • Şimdiki zamanı almak için bir yöntemle kendi statik sınıfınızı yapın ve bu yöntemin herhangi bir uygulamasını dışarıdan yükleme yeteneği. Örneğin, C# kodu durumunda, bu sınıf UtcNow özelliğini ve SetImplementation'ı (Func impl). Geçerli zamanı elde etmek için statik bir özellik veya yöntem kullanmak, bağımlılığı her yerde ek bir arabirime açıkça yazma ihtiyacını ortadan kaldırır, ancak OOP ilkeleri açısından bu ideal bir çözüm değildir. Ancak, herhangi bir nedenle önceki seçenek uygun değilse, bunu kullanabilirsiniz.
Şimdiki zaman sağlayıcı uygulamanıza geçerken çözülmesi gereken ek bir sorun da, "eski moda" hiç kimsenin standart sınıfları kullanmaya devam etmemesini sağlamaktır. Çoğu kod kalite kontrol sisteminde bu görevin çözülmesi kolaydır. Özünde, "varsayılan" uygulamanın bildirildiği dosya dışındaki tüm dosyalarda "istenmeyen" bir alt dizi aramaya başlar.

Şimdiki zamanı elde etmenin ikinci nüansı şudur: müşteriye güvenilemez... Kullanıcıların bilgisayarlarındaki o anki zaman, gerçek zamandan çok farklı olabilir ve eğer buna bağlı bir mantık varsa, o zaman bu fark her şeyi mahvedebilir. Geçerli saati almanın gerekli olduğu tüm yerler, mümkünse sunucu tarafında yapılmalıdır. Ve daha önce de belirtildiği gibi, zamanla ilgili tüm aritmetik işlemler, UTC değerlerinde veya saat dilimi ofsetini depolayan türler kullanılarak gerçekleştirilmelidir.

Ve bahsetmek istediğim bir şey daha, bilgi alışverişi için tarih ve saat biçimini tanımlayan ISO 8601 standardıdır. Özellikle, olası uyumluluk sorunlarından kaçınmak için serileştirmede kullanılan tarih ve saatin dize gösterimi bu standarda uygun olmalıdır. Pratikte, biçimlendirmeyi kendiniz uygulamak zorunda kalmanız son derece nadirdir, bu nedenle standardın kendisi esas olarak bilgi amaçlı yararlı olabilir.

Etiketler: Etiket Ekle

DateTimeOffset testDateAndTime = yeni DateTimeOffset (2008, 5, 1, 8, 6, 32, yeni TimeSpan (1, 0, 0)); // TEMİZ SAAT VE TARİH testDateAndTime = testDateAndTime.DateTime.Date; var dateTableEntry = db.DatesTable.First (dt => dt.Id == someTestId); dateTableEntry.test = testDateAndTime; db.SaveChangesAsync();

VERİTABANI SONUÇ: 2008-05-01 00:00: 00.0000000 -04:00

-4: 00 - +00: 00 (kaydetmeden önceki koddan) nasıl etkinleştiririm?

Denedim:

Genel Görev SetTimeZoneOffsetToZero (DateTimeOffset dateTimeOffSetObj) (TimeSpan zeroOffsetTimeSpan = yeni TimeSpan (0, 0, 0, 0, 0); dönüş dateTimeOffSetObj.ToOffset (zeroOffsetTimeSpan);)

O hiçbir şey yapmaz.

Nihai hedef, yalnızca saat veya saat dilimi farkı olmayan bir tarihe sahip olmaktır. Saati başka bir saat dilimine dönüştürmek İSTEMİYORUM (örn. 00:00: 00.0000000 00:00: 00.0000000 zamanından 4 saat ve ayarlanan zamanın 00: 00: 00.0000000 ofsetinden +00:00 ile çıkarılmasını istemiyorum, Sadece ofseti +00: 00 olarak ayarlamasını istiyorum). İstiyorum Geçerli tarih sıfır ofset ile.

Düzenlemek:

İşte başka bir yerde önerebilecekleriniz:

DateTimeOffset testDateAndTime = yeni DateTimeOffset (2008, 5, 1, 8, 6, 32, yeni TimeSpan (1, 0, 0)); testDateAndTime = testDateAndTime.DateTime.Date; // Zaman kısmını sıfırla testDateAndTime = DateTime.SpecifyKind (testDateAndTime.Date, DateTimeKind.Utc); // "Sıfır çıkış" ofset kısmı

SpecifyKind'in dateTimeOffset'imin SpecifyKind'i olacağından oldukça emindim, örneğin BOTH saatini ve saat dilimi ofsetini değiştirin, ancak testte, istediğim şey olan sadece saat dilimi ofsetini değiştiriyor gibi görünüyor. Bununla ilgili bir sorun mu var?

1 cevap

Sorunun veritabanıyla ilgisi yok. Bir kesme noktası ayarladıysanız veya bir yere çıkış kaydettiyseniz, bu koddan kısa bir süre sonra ofset ekini görmelisiniz:

TestDateAndTime = testDateAndTime.DateTime.Date;

Bunu parçalayalım:

  • DateTimeOffset 2008-05-01T08: 06: 32 + 01:00 ile başladınız
  • Ardından, DateTimeKind.Unspecified ile 2008-05-01T08: 06: 32 DateTime değeriyle sonuçlanan.DateTime öğesini çağırdınız.
  • Sonra aradın DateTime ile sonuçlanan DateTime değeri 2008-05-01T00: 00:00 ile DateTimeKind.Unspecified.
  • Sonucu DateTimeOffset türündeki testDateAndTime içinde döndürüyorsunuz. Bu, DateTime'dan DateTimeOffset'e örtük bir dönüşüme neden olur yerel saat dilimini uygulayan... Sizin durumunuzda, yerel saat diliminizde bu değerin ofsetinin -04:00 olduğu görülüyor, bu nedenle ortaya çıkan değer, tarif ettiğiniz gibi DateTimeOffset 2008-05-01T00: 00: 00-04: 00.

Dedin:

Nihai hedef, yalnızca saat veya saat dilimi farkı olmayan bir tarihe sahip olmaktır.

Eh, şu anda sadece bir tarih olan ve zamansız bir yerel C # veri türü yok. corefxlab'deki System.Time paketinde salt bir Date türü vardır, ancak bu tipik bir üretim uygulaması için tam olarak hazır değildir. Noda zaman kitaplığında bugün kullanabileceğiniz LocalDate var, ancak yine de veritabanında depolamadan önce yerel bir türe dönüştürmeniz gerekiyor. Yani, aynı zamanda yapabileceğiniz en iyi şey:

  • Bu alandaki tarih türünü kullanmak için SQL Server'ınızı değiştirin.
  • .NET kodunuzda DateTime ile 00:00:00 ve DateTimeKind.Unspecified kullanın. Zaman bölümünü görmezden gelmeyi unutmamalısınız (çünkü belirli saat dilimlerinde yerel gece yarısı olmayan tarihler vardır).
  • Test uyarısını DateTimeOffset değil, DateTime olarak değiştirin.

Genel olarak DateTimeOffset birçok senaryo için (zaman damgası olayları gibi) uygunken, sadece tarih değerleri için uygun değildir.

Geçerli tarihi sıfır ofsetli istiyorum.

Gerçekten bir DateTimeOffset olarak istiyorsanız, şunları yaparsınız:

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

Ancak buna karşı tavsiyede bulunuyorum. Bunu yaparak, orijinal değerin yerel tarihini alıyor ve UTC'de olduğunu iddia ediyorsunuz. Orijinal ofset sıfırdan farklıysa, bu yanlış bir ifade olacaktır. Oluşturduğunuzdan farklı bir zaman noktasından (potansiyel olarak farklı bir tarihle) bahsettiğiniz için, daha sonra farklı hatalara yol açacaktır.

Düzenlemenizde sorulan ek soruya gelince - DateTimeKind.Utc öğesinin belirtilmesi, örtük yayınlama davranışını değiştirir. Yerel saat dilimini kullanmak yerine, her zaman sıfır ofseti olan UTC saati kullanılır. Sonuç, yukarıda verdiğim daha açık görüşle aynı. Hala aynı nedenlerle buna karşı tavsiye ediyorum.

2016-12-31T22:00:00-04:00'dan başlayan bir örnek düşünün. Yaklaşımınıza göre 2016-12-31T00: 00:00 + 00:00 veritabanında saklamanız gerekir. Ancak, bunlar zaman içinde iki farklı noktadır. İlki UTC'ye normalize edilmiş, 2017-01-01T02: 00:00 + 00:00 ve ikincisi, farklı bir saat dilimine dönüştürülmüş, 2016-12-30T20: 00: 00-04: 00 olacaktır. . Dönüşümdeki tarihlerdeki değişikliği not edin. Bu muhtemelen uygulamanızda kullanmak istediğiniz davranış değildir.

Sorunun veritabanıyla ilgisi yok. Bir kesme noktası ayarlarsanız veya bir yere bir çıkış girerseniz, bu koddan kısa bir süre sonra ofsetin sabitlendiğini görebilmeniz gerekir:

TestDateAndTime = testDateAndTime.DateTime.Date;

Bunu parçalayalım:

  • DateTimeOffset değeriyle başladınız 2008-05-01T08:06:32+01:00
  • Sonra DateTime'ı çağırdınız, bu da DateTimeKind.Unspecified ile 2008-05-01T08: 06: 32 DateTime değeriyle sonuçlandı.
  • Ardından, DateTimeKind.Unspecified ile bir DateTime değeri olan 2008-05-01T00: 00:00 ile sonuçlanan Date öğesini çağırdınız.
  • Sonucu DateTimeOffset türündeki testDateAndTime öğesine atıyorsunuz. Bu, DateTime'dan DateTimeOffset'e örtülü bir atamayı çağırır - hangisi geçerli yerel Saat dilimi... Sizin durumunuzda, yerel saat diliminizdeki bu değerin ofseti -04: 00 gibi görünüyor, bu nedenle ortaya çıkan değer, tanımladığınız gibi 2008-05-01T00: 00: 00-04: 00'den DateTimeOffset'tir.

Dedin:

Nihai hedef, saat veya saat dilimi farkı olmayan bir tarihe sahip olmaktır.

peki var şu anda yerel bir C # veri türü değil, yalnızca zamansız bir tarih. Sistem zamanı corefxlab'da paket, ancak henüz tipik bir üretim uygulaması için tam olarak hazır değil. Noda Time kitaplığında, bugün kullanabileceğiniz LocalDate var, ancak yine de veritabanına kaydetmeden önce yerel bir türe dönüştürmeniz gerekiyor. Yani, aynı zamanda yapabileceğiniz en iyi şey:

  • Alandaki tarih türünü kullanmak için SQL Server'ınızı değiştirin.
  • .NET kodunuzda DateTime ile 00:00:00 ve DateTimeKind.Unspecified kullanın. Zaman bölümünü görmezden gelmeyi unutmamalısınız (çünkü belirli saat dilimlerinde yerel gece yarısı olmayan tarihler vardır).
  • Prop testini DateTimeOffset değil, DateTime olacak şekilde değiştirin.

Genel olarak, DateTimeOffset çok sayıda senaryo için uygundur (örn. zaman damgaları olaylar), yalnızca tarih değerleri için iyi çalışmaz.

Geçerli tarihi sıfır ofsetli istiyorum.

Eğer sen Gerçekten istemek DateTimeOffset gibi, şunları yapabilirsiniz:

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

Ancak, bunu yapmanızı önermiyorum. Bunu yaparken alırsın yerel orijinal değerin tarihi ve UTC'de olduğunu iddia edin. Orijinal ofset sıfırdan farklıysa, bu yanlış bir ifade olacaktır. Oluşturduğunuzdan farklı bir zaman noktasından (potansiyel olarak farklı bir tarihle) bahsettiğiniz için daha sonra farklı hatalara yol açacaktır.

Kurulunuz tarafından sorulan ek bir soruyla ilgili. DateTimeKind.Utc'nin belirtilmesi, örtük yayınlama davranışını değiştirir. Yerel saat dilimini kullanmak yerine, her zaman sıfır ofseti olan UTC saati kullanılır. Sonuç, yukarıda verdiğim daha açık görüşle aynı. Hala aynı nedenlerle buna karşı tavsiye ediyorum.

2016-12-31T22:00:00-04:00 ile başlayan bir örnek düşünün. Yaklaşımınıza göre 2016-12-31T00: 00:00 + 00:00 veritabanında saklamanız gerekir. Ancak, bunlar zaman içinde iki farklı noktadır. UTC'ye normalleştirilen ilki 2017-01-01T02: 00:00 + 00:00 ve ikincisi, farklı bir saat dilimine dönüştürülen 2016-12-30T20: 00: 00-04: 00 olacaktır. . Dönüşümdeki tarihlerdeki değişikliği not edin. Bu muhtemelen uygulamanızda kullanmak istediğiniz davranış değildir.