.NET DateTime'dan milisaniye kesilir


334

Ben bir veritabanı saklanan değeri gelen bir istek bir zaman damgası karşılaştırmak çalışıyorum. SQL Server, elbette zamanında milisaniyede bir hassasiyet tutar ve bir .NET DateTime'a okunduğunda, bu milisaniyeyi içerir. Ancak sisteme gelen istek bu hassasiyeti sunmuyor, bu yüzden sadece milisaniyeyi düşürmem gerekiyor.

Bariz bir şeyi kaçırmış gibi hissediyorum, ama bunu yapmak için zarif bir yol bulamadım (C #).


(3. deneme ...) Yanıtların% 20'si ( 1 , 2 , 3 ) milisaniye bileşeninin a'nın biçimlendirilmiş stringgösteriminden nasıl çıkarılacağını veya kaldırılacağını DateTimeaçıkladığından, "kısaltmayı" netleştirmek için belki de bir düzenleme gereklidir / "damla" milisaniye anlamına gelir "bir üretmek DateTimetüm tarih / saat bileşenleri hariç aynıdır değeri TimeOfDay.TotalMillisecondsolduğunu 0." İnsanlar elbette okumuyorlar, ama sadece belirsizlikleri ortadan kaldırmak için.
BACON

Yanıtlar:


557

Aşağıdaki, kesirli milisaniyeye sahip ve aynı zamanda Kind özelliğini (Yerel, Utc veya Tanımsız) koruyan bir DateTime için çalışacaktır.

DateTime dateTime = ... anything ...
dateTime = new DateTime(
    dateTime.Ticks - (dateTime.Ticks % TimeSpan.TicksPerSecond), 
    dateTime.Kind
    );

veya eşdeğeri ve daha kısa:

dateTime = dateTime.AddTicks( - (dateTime.Ticks % TimeSpan.TicksPerSecond));

Bu bir genişletme yönteminde genelleştirilebilir:

public static DateTime Truncate(this DateTime dateTime, TimeSpan timeSpan)
{
    if (timeSpan == TimeSpan.Zero) return dateTime; // Or could throw an ArgumentException
    if (dateTime == DateTime.MinValue || dateTime == DateTime.MaxValue) return dateTime; // do not modify "guard" values
    return dateTime.AddTicks(-(dateTime.Ticks % timeSpan.Ticks));
}

aşağıdaki gibi kullanılır:

dateTime = dateTime.Truncate(TimeSpan.FromMilliseconds(1)); // Truncate to whole ms
dateTime = dateTime.Truncate(TimeSpan.FromSeconds(1)); // Truncate to whole second
dateTime = dateTime.Truncate(TimeSpan.FromMinutes(1)); // Truncate to whole minute
...

Size teknik olarak doğru olduğunuz için bunu vereceğim, ancak SQL Server'dan veri okuyan kişilerin bazı dağıtılmış verilerle karşılaştırması için (benim durumumda Web tabanlı bir istek), bu çözüm miktarı gerekli değildir.
Jeff Putz

1
Güzel. Açıkçası birisinin DateTime sınıfına en yakın olana yuvarlamak için bazı uzatma yöntemleri vermesi gerekir, böylece bu tür iyi kodlama yeniden kullanılabilir.
chris.w.mclean

Bu pek olası değildir, ancak keneler = 0 olduğunda bu yaklaşım bozulmaz mı?
adotout

@adotout, timeSpan parametresi sıfırsa yukarıdaki Truncate yöntemi bir DivideByZeroException kurar, "ticks = 0 olduğunda yaklaşım kesilir" derken bu mu? TimeSpan sıfır olduğunda bir ArgumentException özel durumu oluşturmak daha iyi olur.
Joe

145
var date = DateTime.Now;

date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind);

34
Açık ve basit, önemli bir bilgi parçasını kaybetmediğinizden emin olmak için kurucunun sonuna ", date.Kind" eklemeyi unutmayın.
JMcDaniel

9
Performansa duyarlı koddaki bu çözüm konusunda dikkatli olun. Uygulamam CPU zamanının% 12'sini System.DateTime.GetDatePart'ta harcıyordu .
Albay Panik

3
Basit, ancak en iyi cevap olarak işaretlenen sorudan daha yavaş. Bunun bir darboğaz olabileceğinden değil, yaklaşık 7-8x daha yavaştır.
Jonas

"Çok daha yavaş" ifadeler tam olarak doğru değildir, çalışma süresine bağlı olarak fark% 50 ile yaklaşık% 100 arasındadır; net 4.7.2: 0.35 µs vs 0.62 µs ve çekirdek 3.1: 0.18 µs vs 0.12 µs mikro saniye (10 ^ -6 saniye)
juwens

62

İşte herhangi bir çözüme kısalmanızı sağlayacak bir önceki cevaba dayanan bir uzatma yöntemi ...

Kullanımı:

DateTime myDateSansMilliseconds = myDate.Truncate(TimeSpan.TicksPerSecond);
DateTime myDateSansSeconds = myDate.Truncate(TimeSpan.TicksPerMinute)

Sınıf:

public static class DateTimeUtils
{
    /// <summary>
    /// <para>Truncates a DateTime to a specified resolution.</para>
    /// <para>A convenient source for resolution is TimeSpan.TicksPerXXXX constants.</para>
    /// </summary>
    /// <param name="date">The DateTime object to truncate</param>
    /// <param name="resolution">e.g. to round to nearest second, TimeSpan.TicksPerSecond</param>
    /// <returns>Truncated DateTime</returns>
    public static DateTime Truncate(this DateTime date, long resolution)
    {
        return new DateTime(date.Ticks - (date.Ticks % resolution), date.Kind);
    }
}

1
Bu gerçekten esnek ve yeniden kullanılabilir bir çözümdür, aşırı ayrıntılı olmadan özlü ve etkileyici. En iyi çözüm olarak oyum.
Jaans

2
Aslında% işlenenlerin çevresinde parantezlere ihtiyacınız yoktur.
ErikE

8
.. ama bence parens netlik sağlıyor.
orion elenzil

28
DateTime d = DateTime.Now;
d = d.AddMilliseconds(-d.Millisecond);

70
-1: Yalnızca DateTime değeri bir milisaniyelik kesir içermiyorsa çalışır.
Joe

7
Bu yöntemi kullanmak bazı birim testlerimin başarısız olmasına neden oldu: Beklenen: 2010-05-05 15: 55: 49.000 Ancak: 2010-05-05 15: 55: 49.000. Joe'nun milisaniyelik kesirlerden bahsettiklerinden dolayı tahmin ediyorum.
Seth Reno

6
Serileştirme için çalışmaz, örneğin 2010-12-08T11: 20: 03.000099 + 15: 00 çıktıdır, milisaniyeyi tamamen kesmez.
joedotnot

5
MillisecondÖzelliği , bir veren tam sayı , 0 ve 999 (dahil) arasında. Yani, ameliyattan önceki günün saati, diyelim ki 23:48:49.1234567, o zaman tamsayı olur 123ve ameliyattan sonraki günün saati olur 23:48:49.0004567. Yani tam bir saniyede kısalmadı.
Jeppe Stig Nielsen

11

Bazen yıl veya ay gibi takvim tabanlı bir şeye kısaltmak istersiniz. İşte herhangi bir çözünürlük seçmenize izin veren bir uzantı yöntemi.

public enum DateTimeResolution
{
    Year, Month, Day, Hour, Minute, Second, Millisecond, Tick
}

public static DateTime Truncate(this DateTime self, DateTimeResolution resolution = DateTimeResolution.Second)
{
    switch (resolution)
    {
        case DateTimeResolution.Year:
            return new DateTime(self.Year, 1, 1, 0, 0, 0, 0, self.Kind);
        case DateTimeResolution.Month:
            return new DateTime(self.Year, self.Month, 1, 0, 0, 0, self.Kind);
        case DateTimeResolution.Day:
            return new DateTime(self.Year, self.Month, self.Day, 0, 0, 0, self.Kind);
        case DateTimeResolution.Hour:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerHour));
        case DateTimeResolution.Minute:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerMinute));
        case DateTimeResolution.Second:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerSecond));
        case DateTimeResolution.Millisecond:
            return self.AddTicks(-(self.Ticks % TimeSpan.TicksPerMillisecond));
        case DateTimeResolution.Tick:
            return self.AddTicks(0);
        default:
            throw new ArgumentException("unrecognized resolution", "resolution");
    }
}

9

Milisaniyeyi düşürüp karşılaştırmak yerine, farkı neden karşılaştırmıyorsunuz?

DateTime x; DateTime y;
bool areEqual = (x-y).TotalSeconds == 0;

veya

TimeSpan precision = TimeSpan.FromSeconds(1);
bool areEqual = (x-y).Duration() < precision;

3
ilk seçenek çalışmaz, çünkü TotalSeconds bir çifttir; milisaniyeyi de döndürür.
Jowen

1
Farkı karşılaştırmak, kesip sonra karşılaştırmakla aynı sonucu vermez. Örneğin 5.900 ve 6.100 saniyeden daha kısa bir aralıktadır, bu nedenle yönteminizle eşittir. Ancak kesik değerler 5 ve 6 farklıdır. Hangisi uygun sizin ihtiyacına bağlıdır.
Joe

7

Daha az belirgin ancak 2 kat daha hızlı:

// 10000000 runs

DateTime d = DateTime.Now;

// 484,375ms
d = new DateTime((d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

// 1296,875ms
d = d.AddMilliseconds(-d.Millisecond);

3
İkinci seçeneğin d.AddMilliseconds(-d.Millisecond)DateTime öğesinin tam olarak bir önceki tam saniyeye taşınması gerekmediğini unutmayın. d.Ticks % TimeSpan.TicksPerMillisecondikincinizin ötesindeki keneler (0 ile 9.999 arasında bir yerde) kalacaktır.
Technetium

5

İkinciye yuvarlamak için:

dateTime.AddTicks(-dateTime.Ticks % TimeSpan.TicksPerSecond)

Dakikaya TicksPerMinuteyuvarlamak için ile değiştirin .


Kodunuz performansa duyarlıysa,

new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second)

Uygulamam CPU zamanının% 12'sini System.DateTime.GetDatePart'ta harcıyordu .


3

Kolay okuma için bir yol ...

//Remove milliseconds
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH:mm:ss"), "yyyy-MM-dd HH:mm:ss", null);

Ve dahası...

//Remove seconds
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH:mm"), "yyyy-MM-dd HH:mm", null);

//Remove minutes
DateTime date = DateTime.Now;
date = DateTime.ParseExact(date.ToString("yyyy-MM-dd HH"), "yyyy-MM-dd HH", null);

//and go on...

4
Dizelere dönüştürme ve ayrıştırma performans açısından korkunç bir fikirdir.
Jeff Putz

2
Gerçek @JeffPutz ama olduğunu ama basit. Bir DB'den eklenen ve alınan bir değerin keneleri kaybettiği otomatik bir test için uygundur (kesin durumum). Ancak bu cevap, var now = DateTime.Parse(DateTime.Now.ToString())gayet iyi çalıştığı için olduğundan daha basit olabilir .
Grimm Opiner

1
@GrimmTheOpiner - "... gayet iyi çalışıyor", ancak çoğunlukla garanti edilmez. Yaptığı şey: "Bir DateTime'ı, geçerli kullanıcının Kontrol Paneli tercihlerinde olduğu gibi yapılandırılan 'Uzun süre' hassasiyetine yuvarlar". Hangi genellikle, ama ille de değil, saniye.
Joe

1
basitliği gibi, performans da otomatik test durumu için bir sorun değildir.
liang

1

Diadistis yanıtı ile ilgili. Bu benim için işe yaradı, ancak çarpmadan önce bölümün kesirli kısmını kaldırmak için Floor kullanmak zorundaydım. Yani,

d = new DateTime((d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

olur

d = new DateTime(Math.Floor(d.Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond);

İki Uzun değerin bölünmesinin bir Uzun ile sonuçlanmasını ve böylece ondalık kısmı kaldırmasını beklerdim, ancak çarpma işleminden sonra tam olarak aynı değeri bırakarak bir Çift olarak çözer.

Eppsy


1

2 Yukarıda belirtilen çözümler için genişletme yöntemleri

    public static bool LiesAfterIgnoringMilliseconds(this DateTime theDate, DateTime compareDate, DateTimeKind kind)
    {
        DateTime thisDate = new DateTime(theDate.Year, theDate.Month, theDate.Day, theDate.Hour, theDate.Minute, theDate.Second, kind);
        compareDate = new DateTime(compareDate.Year, compareDate.Month, compareDate.Day, compareDate.Hour, compareDate.Minute, compareDate.Second, kind);

        return thisDate > compareDate;
    }


    public static bool LiesAfterOrEqualsIgnoringMilliseconds(this DateTime theDate, DateTime compareDate, DateTimeKind kind)
    {
        DateTime thisDate = new DateTime(theDate.Year, theDate.Month, theDate.Day, theDate.Hour, theDate.Minute, theDate.Second, kind);
        compareDate = new DateTime(compareDate.Year, compareDate.Month, compareDate.Day, compareDate.Hour, compareDate.Minute, compareDate.Second, kind);

        return thisDate >= compareDate;
    }

kullanımı:

bool liesAfter = myObject.DateProperty.LiesAfterOrEqualsIgnoringMilliseconds(startDateTime, DateTimeKind.Utc);

1

En hızlı çözüm değil, basit ve anlaşılması kolay:

DateTime d = DateTime.Now;
d = d.Date.AddHours(d.Hour).AddMinutes(d.Minute).AddSeconds(d.Second)

0
DateID.Text = DateTime.Today.ToShortDateString();

Use ToShortDateString() //Date 2-02-2016
Use ToShortDateString() // Time 

Ve Kullanarak

ToLongDateString() // its show 19 February 2016.

: P


-1. Sorunun bir stringyerine üretmek istemesini nasıl yanlış yorumlamış olabileceğini görebiliyorum DateTime, ancak bu zaman bileşenlerini çıktıdan tamamen atlıyor . (Bu, Todaymülke erişmeyi de gereksiz kılar .)
BACON

0

Yeni Yöntem

String Date = DateTime.Today.ToString("dd-MMM-yyyy"); 

// string pass parametresini tanımla dd-mmm-yyyy return 24-feb-2016

Veya metin kutusunda gösterilir

txtDate.Text = DateTime.Today.ToString("dd-MMM-yyyy");

// PageonLoad'u tak


-1. Sorunun bir stringyerine üretmek istemesini nasıl yanlış yorumlamış olabileceğini görebiliyorum DateTime, ancak bu zaman bileşenlerini çıktıdan tamamen atlıyor . (Bu, Todaymülke erişmeyi de gereksiz kılar .)
BACON

0

Benim durumumda, saniye ve milisaniyeyi kaydetmeden datetimePicker aracından TimeSpan'ı kurtarmayı hedefliyordum ve işte çözüm.

İlk önce datetimePicker.value değerini istediğiniz biçime dönüştürün; benimki "SS: dd", ardından TimeSpan'a geri dönüştürün.

var datetime = datetimepicker1.Value.ToString("HH:mm");
TimeSpan timeSpan = Convert.ToDateTime(datetime).TimeOfDay;

Bunu yapmak için daha iyi bir yol (daha net bir amaç, biçimlendirmeyi ve a'dan ayrıştırmayı önler string) şöyle olurdu DateTime datetime = datetimepicker1.Value; TimeSpan timeSpan = new TimeSpan(datetime.Hour, datetime.Minute, 0); Veya Joe'nunTimeSpan değerler üzerinde çalışan ve TimeSpan timeSpan = datetime.TimeOfDay.Truncate(TimeSpan.FromSeconds(1));saniyeleri kısaltmak için kullanılan uzantı yönteminin bir varyasyonunu kullanabilirsiniz .
BACON

0

Bu, burada ve benzer sorularda yayınlanan uzantı yöntemlerinin versiyonum. Bu, keneler değerini kolay okunur bir şekilde doğrular ve özgün DateTime örneğinin DateTimeKind değerini korur. (Bunun MongoDB gibi bir veritabanında saklarken ince ama alakalı yan etkileri vardır.)

Gerçek hedef, bir DateTime değerini belirtilen bir değere (yani Saat / Dakika / Saniye / MS) kısaltmaksa, bunun yerine kodunuza bu uzantı yöntemini uygulamanızı öneririm. Yalnızca geçerli bir kesinliğe göre kısaltabilmenizi sağlar ve orijinal örneğinizin önemli DateTimeKind meta verilerini korur:

public static DateTime Truncate(this DateTime dateTime, long ticks)
{
    bool isValid = ticks == TimeSpan.TicksPerDay 
        || ticks == TimeSpan.TicksPerHour 
        || ticks == TimeSpan.TicksPerMinute 
        || ticks == TimeSpan.TicksPerSecond 
        || ticks == TimeSpan.TicksPerMillisecond;

    // /programming/21704604/have-datetime-now-return-to-the-nearest-second
    return isValid 
        ? DateTime.SpecifyKind(
            new DateTime(
                dateTime.Ticks - (dateTime.Ticks % ticks)
            ),
            dateTime.Kind
        )
        : throw new ArgumentException("Invalid ticks value given. Only TimeSpan tick values are allowed.");
}

Sonra bu yöntemi kullanabilirsiniz:

DateTime dateTime = DateTime.UtcNow.Truncate(TimeSpan.TicksPerMillisecond);

dateTime.Kind => DateTimeKind.Utc

-1

Cevabın çok geç olduğunu biliyorum, ancak milisaniyeden kurtulmanın en iyi yolu

var currentDateTime = DateTime.Now.ToString("s");

Değişkenin değerini yazdırmayı deneyin, milisaniye olmadan tarih saatini gösterecektir.


1
Bu ideal değil. Bir DateTime değil, bir dizeniz var.
Jeff Putz
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.