C # 'da belirli bir saat diliminde bir DateTime oluşturma


162

Yanlış ayarlanmış ve sonra düzeltilmiş olduğu için bir makinede saat dilimi değiştiğinde durumu test etmek için bir birim testi oluşturmaya çalışıyorum.

Testte, testi çalıştıran kişilerin bulundukları yerden bağımsız olarak başarılı bir şekilde yapabilmelerini sağlamak için hiçbir yerel saat diliminde DateTime nesneleri oluşturabilmem gerekiyor.

DateTime yapıcısından görebildiğim kadarıyla TimeZone'u yerel saat dilimi, UTC saat dilimi olarak ayarlayabilir veya belirtmeyebilirim.

PST gibi belirli bir saat dilimiyle nasıl bir DateTime oluşturabilirim?


Yanıtlar:


216

Jon'un cevabı TimeZone hakkında konuşuyor , ancak bunun yerine TimeZoneInfo kullanmanızı öneririm .

Şahsen ben (mümkün olan en azından geçmiş için; UTC gelecek için saklamak potansiyel sorunları vardır ) şeyler mümkün olduğunca UTC tutmak istiyorum, bu yüzden böyle bir yapı öneririm:

public struct DateTimeWithZone
{
    private readonly DateTime utcDateTime;
    private readonly TimeZoneInfo timeZone;

    public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone)
    {
        var dateTimeUnspec = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
        utcDateTime = TimeZoneInfo.ConvertTimeToUtc(dateTimeUnspec, timeZone); 
        this.timeZone = timeZone;
    }

    public DateTime UniversalTime { get { return utcDateTime; } }

    public TimeZoneInfo TimeZone { get { return timeZone; } }

    public DateTime LocalTime
    { 
        get 
        { 
            return TimeZoneInfo.ConvertTime(utcDateTime, timeZone); 
        }
    }        
}

İşleri daha net hale getirmek için "TimeZone" adlarını "TimeZoneInfo" olarak değiştirmek isteyebilirsiniz - Ben kısaca adları kendim tercih ederim.


5
Eşdeğer bir SQL Server yapısı bilmiyorum, korkarım. Bir sütun olarak saat dilimi adını ve başka bir sütunda UTC değerini sahip öneririz. Bunları ayrı ayrı getirin ve ardından örnekleri oldukça kolay bir şekilde oluşturabilirsiniz.
Jon Skeet

2
DateTime ve TimeZoneInfo alan yapıcı beklenen kullanımı hakkında emin değilim, ancak dateTime.ToUniversalTime () yöntemini çağırdığınız göz önüne alındığında, yerel saatte "belki" olacağını tahmin şüpheliyim. Bu durumda, gerçekten o zaman diliminde olması gerektiğini söylediği için UTC'ye dönüştürmek için geçti TimeZoneInfo kullanmanız gerektiğini düşünüyorum.
ıdisposable

2
@ChrisMoschini: Bu noktada sadece kendi kimlik planınızı icat ediyorsunuz - dünyadaki hiç kimsenin kullanmadığı bir şema. Endüstri standardı bölge bilgisine sadık kalacağım, teşekkürler. (Örneğin, "Avrupa / Londra" nın anlamsız olduğunu görmek zor.)
Jon Skeet

2
@ChrisMoschini: O zaman farklı bir örnek: CST. Bu UTC-5 veya UTC-6 mı? IST - veritabanınızdaki İsrail, Hindistan veya İrlanda mı? (Ve şu anda ofseti biliyor olsanız bile, aynı kısaltmayı gözlemleyen farklı ülkeler farklı zamanlarda değişebilir. Dolayısıyla, hangi gerçek saat dilimi anlamına geldiği konusunda hala belirsizlik var. Saat dilimi! = Ofset.) Davanıza geri dönme: talep edersiniz kısaltmaları kullanmanın probleminizi en iyi şekilde çözdüğünü. Endüstri standardı saat dilimi kimliklerini kullanmak nasıl daha kötü olurdu?
Jon Skeet

6
@ChrisMoschini: Belirsiz kısaltmalar yerine endüstri standardı, belirsiz zoneinfo kimliklerini kullanmanızı tavsiye edeceğim. Bu, kütüphanenin tercih edildiği bir mesele değil - kütüphanenin yazarlığı gerçekten bir sorun değil. Birisi iyi bir tanımlayıcı seçeneği olan başka bir kütüphane kullanmak isterse , sorun değil. Bir saat dilimi için tanımlayıcı seçim olsa önemli biridir ve bunun okuyucular kısaltmalar farkında olduğumuzu çok önemli olduğunu düşünüyorum vardır ben IST örnekle gösterilir sonra, belirsiz.
Jon Skeet

54

DateTimeOffset yapısı tam olarak bu tür bir kullanım için oluşturulmuştur.

Bkz. Http://msdn.microsoft.com/tr-tr/library/system.datetimeoffset.aspx

Belirli bir saat dilimine sahip bir DateTimeOffset nesnesi oluşturma örneği:

DateTimeOffset do1 = new DateTimeOffset(2008, 8, 22, 1, 0, 0, new TimeSpan(-5, 0, 0));


1
Teşekkürler, bunu başarmanın iyi bir yolu. DateTimeOffset nesnenizi doğru saat diliminde aldıktan sonra, oluşturduğunuz bir UTC saati almak için .UtcDateTime özelliğini kullanabilirsiniz. Tarihlerinizi UTC olarak
kaydederseniz

2
Bazı Zaman Dilimleri onurlandırırken bazıları bunu yapmadığından, Yaz Saati Uygulaması'nı doğru şekilde işlemediğini sanmıyorum. Ayrıca "gün" DST başlar / biter, o günün bazı bölümleri kapalı olacaktır.
crokusek

14
Ders. DST belirli bir saat diliminin kuralıdır. DateTimeOffset herhangi bir saat dilimi ile ilişkili değil. -5 gibi bir UTC ofset değerini bir saat dilimiyle karıştırmayın. Bu bir saat dilimi değil, bir ofset. Aynı ofset genellikle birçok saat dilimi tarafından paylaşılır, bu nedenle bir saat dilimine başvurmanın belirsiz bir yoludur. DateTimeOffset bir saat dilimi ile değil, bir ofset ile ilişkili olduğundan, muhtemelen DST kurallarını uygulayamaz. Yani DateTimeOffset yapısında (ör. Hours ve TimeOfDay özelliklerinde) istisnasız olarak, 03:00 yılın her günü 03:00 olacaktır.
Triynko

Eğer karışık olabilir nerede DateTimeOffset LocalDateTime özelliğine bakarsanız. Bu özellik bir DateTimeOffset DEĞİLDİR, türü DateTimeKind.Local olan bir DateTime örneğidir. Bu örnek bir saat dilimi ile ilişkilendirilir ... yerel sistem saat dilimi ne olursa olsun. Bu tesis gün ışığından yararlanma tasarrufunu yansıtacaktır.
Triynko

4
Yani, DateTimeOffset ile ilgili asıl sorun, yeterli bilgi içermemesidir. Bir saat dilimi değil bir ofset içerir. Ofset, çoklu zaman dilimleri ile belirsizdir.
Triynko

41

Buradaki diğer yanıtlar faydalıdır, ancak Pasifik'e özel olarak nasıl erişileceğini kapsamaz - işte gidiyorsunuz:

public static DateTime GmtToPacific(DateTime dateTime)
{
    return TimeZoneInfo.ConvertTimeFromUtc(dateTime,
        TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"));
}

Garip bir şekilde, "Pasifik Standart Saati" normalde "Pasifik Yaz Saati" nden farklı bir şey ifade etse de, bu durumda genel olarak Pasifik saati anlamına gelir. Aslında, FindSystemTimeZoneByIdonu almak için kullanırsanız, mevcut özelliklerden biri, saat diliminin şu anda yaz saatinde olup olmadığını gösteren bir bool.

Kullanıcının istediği yere bağlı olarak farklı TimeZones'da ihtiyacım olan DateTimes'la başa çıkmak için bir araya getirdiğim bir kütüphanede bunun daha genel örneklerini görebilirsiniz:

https://github.com/b9chris/TimeZoneInfoLib.Net

Zaman listesi Windows Kayıt Defteri'nden geldiğinden, bu Windows dışında çalışmaz (örneğin Linux'ta Mono): HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\

Bunun altında anahtarları bulacaksınız (Kayıt Defteri Düzenleyicisi'nde klasör simgeleri); bu anahtarların isimleri geçtiğiniz şeydir FindSystemTimeZoneById. Linux'ta, yeterince keşfetmediğim ayrı bir Linux standardında saat dilimi tanımları kümesi kullanmanız gerekir.


1
Ayrıca ConvertTimeBySystemTimeZoneId () ex: TimeZoneInfo.ConvertTimeBySystemTimeZoneId (DateTime.UtcNow, "Merkezi Standart Saat")
Brent

Windows'da TimeZone Kimlik Listesi de bu yanıtı görebilir: stackoverflow.com/a/24460750/4573839
yu yang Jian

7

Jon Skeet'in web için cevaplama yöntemini biraz değiştirdim . Ayrıca bir cazibe gibi masmavi üzerinde çalışır.

public static class DateTimeWithZone
{

private static readonly TimeZoneInfo timeZone;

static DateTimeWithZone()
{
//I added web.config <add key="CurrentTimeZoneId" value="Central Europe Standard Time" />
//You can add value directly into function.
    timeZone = TimeZoneInfo.FindSystemTimeZoneById(ConfigurationManager.AppSettings["CurrentTimeZoneId"]);
}


public static DateTime LocalTime(this DateTime t)
{
     return TimeZoneInfo.ConvertTime(t, timeZone);   
}
}

2

Bunun için özel bir nesne oluşturmanız gerekir. Özel nesneniz iki değer içerir:

  • DateTime değeri
  • Bir Dilimi nesnesi

CLR tarafından sağlanan bir veri türü olup olmadığından emin değilsiniz, ancak en azından TimeZone bileşeni zaten mevcut.


2

Jon Skeet'in cevabını seviyorum, ancak bir şey eklemek istiyorum. Jon'un sandığın her zaman Yerel saat diliminde geçmesini beklediğinden emin değilim. Ama bunu yerelden başka bir şey olduğu durumlarda kullanmak istiyorum.

Bir veritabanından değerleri okuyorum ve bu veritabanının hangi saat diliminde olduğunu biliyorum. Yani ctor'da, veritabanının saat dilimine geçeceğim. Ama sonra yerel zamanda değer istiyorum. Jon'un LocalTime değeri, yerel saat dilimi tarihine dönüştürülmüş orijinal tarihi döndürmez. Orijinal saat dilimine (tarihi ne olursa olsun) dönüştürülen tarihi döndürür.

Bence bu mülk adları belli oluyor ...

public DateTime TimeInOriginalZone { get { return TimeZoneInfo.ConvertTime(utcDateTime, timeZone); } }
public DateTime TimeInLocalZone    { get { return TimeZoneInfo.ConvertTime(utcDateTime, TimeZoneInfo.Local); } }
public DateTime TimeInSpecificZone(TimeZoneInfo tz)
{
    return TimeZoneInfo.ConvertTime(utcDateTime, tz);
}

0

TimeZones sınıfını kullanmak , zaman dilimine özel tarih oluşturmayı kolaylaştırır.

TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.FindSystemTimeZoneById(TimeZones.Paris.Id));

1
Üzgünüz, ama Asp .NET Core 2.2'de mevcut değil, VS2017 bir Outlook Nuget paketi yüklememi öneriyor.
Machado

örnek => TimeZoneInfo.ConvertTime (DateTime.Now, TimeZoneInfo.FindSystemTimeZoneById ( "Pasifik Standart Saati"))
AZ_
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.