So schneiden Sie Millisekunden von einer .NET DateTime ab


333

Ich versuche, einen Zeitstempel von einer eingehenden Anforderung mit einem in der Datenbank gespeicherten Wert zu vergleichen. SQL Server behält natürlich eine gewisse Genauigkeit von Millisekunden bei, und wenn es in eine .NET DateTime eingelesen wird, enthält es diese Millisekunden. Die eingehende Anforderung an das System bietet jedoch nicht diese Genauigkeit, sodass ich einfach die Millisekunden löschen muss.

Ich habe das Gefühl, dass mir etwas Offensichtliches fehlt, aber ich habe keinen eleganten Weg gefunden, dies zu tun (C #).


(3. Versuch ...) Da 20% der Antworten ( 1 , 2 , 3 ) beschreiben, wie die Millisekunden-Komponente aus der formatierten stringDarstellung von a weggelassen oder entfernt wird DateTime, ist möglicherweise eine Bearbeitung erforderlich, um zu verdeutlichen, dass "abgeschnitten" werden muss. / „Drop“ Millisekunden bedeuten „erzeugt einen DateTimeWert , bei dem alle Datum / Zeit - Komponenten gleich sind , außer TimeOfDay.TotalMillisecondsist 0.“ Die Leute lesen natürlich nicht, sondern nur, um Unklarheiten zu beseitigen.
Speck

Antworten:


555

Das Folgende funktioniert für eine DateTime mit Millisekundenbruchteilen und behält auch die Kind-Eigenschaft (Local, Utc oder Undefined) bei.

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

oder das Äquivalent und kürzer:

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

Dies könnte in eine Erweiterungsmethode verallgemeinert werden:

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));
}

welches wie folgt verwendet wird:

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
...

Ich gebe Ihnen dies, weil Sie technisch korrekt sind. Damit Benutzer Daten aus SQL Server lesen können, um sie mit verteilten Daten zu vergleichen (in meinem Fall eine webbasierte Anforderung), ist diese Auflösung nicht erforderlich.
Jeff Putz

1
Nett. Es ist klar, dass jemand der DateTime-Klasse einige Erweiterungsmethoden geben muss, um sie auf das nächste zu runden, damit diese Art der guten Codierung wiederverwendet wird.
chris.w.mclean

Dies ist sehr unwahrscheinlich, aber bricht dieser Ansatz nicht ab, wenn Ticks = 0 sind?
Adotout

@adotout, die obige Truncate-Methode löst eine DivideByZeroException aus, wenn der Parameter timeSpan Null ist. Ist dies das, was Sie mit "Annäherungsunterbrechungen bei Ticks = 0" meinen? Es ist besser, eine ArgumentException auszulösen, wenn timeSpan Null ist.
Joe

145
var date = DateTime.Now;

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

34
Denken Sie daran, am Ende des Konstruktors ein ", date.Kind" hinzuzufügen, um sicherzustellen, dass Sie keine wichtigen Informationen verlieren.
JMcDaniel

9
Seien Sie vorsichtig mit dieser Lösung in leistungsempfindlichem Code. Meine App verbrachte 12% der CPU-Zeit in System.DateTime.GetDatePart .
Colonel Panic

3
Es ist einfach, aber langsamer als die als beste Antwort gekennzeichnete Frage. Nicht, dass dies ein Engpass sein könnte, aber es ist ungefähr 7-8x langsamer.
Jonas

Die "viel langsameren" Anweisungen sind nicht genau korrekt, der Unterschied liegt je nach Laufzeit zwischen 50% und etwa 100%. netto 4,7,2: 0,35 µs gegen 0,62 µs und Kern 3,1: 0,18 µs gegen 0,12 µs , das sind
Mikrosekunden

61

Hier ist eine Erweiterungsmethode, die auf einer vorherigen Antwort basiert und es Ihnen ermöglicht, auf eine beliebige Auflösung zu kürzen ...

Verwendungszweck:

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

Klasse:

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
Dies ist eine wirklich flexible und wiederverwendbare Lösung, die präzise und ausdrucksstark ist, ohne übermäßig ausführlich zu sein. Meine Stimme als beste Lösung.
Jaans

2
Sie brauchen die Klammern um die% -Operanden nicht wirklich.
ErikE

8
.. aber die Parens sorgen meiner Meinung nach für Klarheit.
Orion Elenzil

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

70
-1: Funktioniert nur, wenn der DateTime-Wert keine Bruchteile einer Millisekunde enthält.
Joe

7
Die Verwendung dieser Methode führte dazu, dass einige meiner Komponententests fehlschlugen: Erwartet: 2010-05-05 15: 55: 49.000 Aber war: 2010-05-05 15: 55: 49.000. Ich vermute aufgrund dessen, was Joe über Bruchteile einer Millisekunde erwähnte.
Seth Reno

6
Funktioniert nicht für die Serialisierung, z. B. 2010-12-08T11: 20: 03.000099 + 15: 00 ist die Ausgabe, schneidet die Millisekunden nicht vollständig ab.
Joedotnot

5
Die MillisecondEigenschaft gibt eine Ganzzahl zwischen 0 und 999 (einschließlich) an. Wenn also beispielsweise die Tageszeit vor der Operation war, 23:48:49.1234567dann ist diese Ganzzahl 123und die Tageszeit nach der Operation 23:48:49.0004567. Es wurde also nicht auf eine ganze Anzahl von Sekunden gekürzt.
Jeppe Stig Nielsen

11

Manchmal möchten Sie auf etwas Kalenderbasiertes wie Jahr oder Monat kürzen. Hier ist eine Erweiterungsmethode, mit der Sie eine beliebige Auflösung auswählen können.

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

Warum nicht den Unterschied vergleichen, anstatt die Millisekunden fallen zu lassen und dann zu vergleichen?

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

oder

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

3
Die erste Option funktioniert nicht, da TotalSeconds ein Double ist. es gibt auch die Millisekunden zurück.
Jowen

1
Das Vergleichen des Unterschieds führt nicht zum gleichen Ergebnis wie das Abschneiden und Vergleichen. ZB 5.900 und 6.100 sind weniger als eine Sekunde voneinander entfernt, würden also als gleichwertig mit Ihrer Methode verglichen. Die abgeschnittenen Werte 5 und 6 sind jedoch unterschiedlich. Was angemessen ist, hängt von Ihrer Anforderung ab.
Joe

7

Weniger offensichtlich, aber mehr als zweimal schneller:

// 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
Beachten Sie, dass die zweite Option d.AddMilliseconds(-d.Millisecond)die DateTime nicht unbedingt genau auf die vorherige volle Sekunde verschiebt. d.Ticks % TimeSpan.TicksPerMillisecondTicks (irgendwo zwischen 0 und 9.999) jenseits Ihrer Sekunde bleiben erhalten.
Technetium

5

Um auf die Sekunde abzurunden:

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

Ersetzen Sie durch TicksPerMinute, um auf die Minute abzurunden.


Wenn Ihr Code leistungsabhängig ist, seien Sie vorsichtig

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

Meine App verbrachte 12% der CPU-Zeit in System.DateTime.GetDatePart .


3

Ein Weg zum einfachen Lesen ist ...

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

Und mehr...

//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
Das Konvertieren in Strings und das Parsen ist eine schreckliche Idee in Bezug auf die Leistung.
Jeff Putz

2
@ JeffPutz stimmt, aber es ist nur einfach. Geeignet für einen automatisierten Test, bei dem ein Wert, der in eine Datenbank eingefügt und aus dieser gezogen wird, die Ticks verliert (meine genaue Situation). Diese Antwort könnte jedoch noch einfacher sein als sie ist, da sie var now = DateTime.Parse(DateTime.Now.ToString())einwandfrei funktioniert.
Grimm The Opiner

1
@GrimmTheOpiner - "... funktioniert einwandfrei", meistens, aber nicht garantiert. Was es tut, ist: "Rundet eine DateTime mit der Genauigkeit, die" Lange Zeit "wie in den Systemsteuerungseinstellungen des aktuellen Benutzers konfiguriert ist". Das sind normalerweise, aber nicht unbedingt Sekunden.
Joe

1
Wie seine Einfachheit ist die Leistung kein Problem für automatisierte Testsituationen.
Liang

1

In Bezug auf Diadistis Antwort. Dies funktionierte für mich, außer dass ich Floor verwenden musste, um den Bruchteil der Division vor der Multiplikation zu entfernen. Damit,

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

wird

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

Ich hätte erwartet, dass die Division von zwei Long-Werten zu einem Long führt, wodurch der Dezimalteil entfernt wird, aber es wird als Double aufgelöst, wobei nach der Multiplikation genau derselbe Wert erhalten bleibt.

Eppsy


1

2 Erweiterungsmethoden für die oben genannten Lösungen

    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;
    }

Verwendungszweck:

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

1

Nicht die schnellste Lösung, aber einfach und leicht zu verstehen:

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 

Und durch Verwendung von

ToLongDateString() // its show 19 February 2016.

: P.


-1. Ich kann sehen, wie die Frage möglicherweise falsch interpretiert wurde, als würde man fragen, ob a stringstatt a erzeugt werden soll DateTime, aber dies lässt Zeitkomponenten vollständig aus der Ausgabe aus . (Das macht auch den Zugriff auf die TodayImmobilie unnötig.)
BACON

0

Neue Methode

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

// String-Pass-Parameter definieren TT-MMM-JJJJ Rückgabe 24.02.2016

Oder im Textfeld angezeigt

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

// PageonLoad aufsetzen


-1. Ich kann sehen, wie die Frage möglicherweise falsch interpretiert wurde, als würde man fragen, ob a stringstatt a erzeugt werden soll DateTime, aber dies lässt Zeitkomponenten vollständig aus der Ausgabe aus . (Das macht auch den Zugriff auf die TodayImmobilie unnötig.)
BACON

0

In meinem Fall wollte ich TimeSpan aus dem datetimePicker-Tool speichern, ohne die Sekunden und Millisekunden zu speichern. Hier ist die Lösung.

Konvertieren Sie zuerst den datetimePicker.value in das gewünschte Format. Meines ist "HH: mm" und konvertieren Sie ihn dann zurück in TimeSpan.

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

Ein besserer Weg (klarere Absicht, vermeidet das Formatieren und Parsen von a string) wäre dies. DateTime datetime = datetimepicker1.Value; TimeSpan timeSpan = new TimeSpan(datetime.Hour, datetime.Minute, 0); Oder Sie könnten eine Variation von Joes Erweiterungsmethode verwenden , die mit TimeSpanWerten arbeitet und TimeSpan timeSpan = datetime.TimeOfDay.Truncate(TimeSpan.FromSeconds(1));zum Abschneiden von Sekunden verwendet wird.
Speck

0

Dies ist meine Version der hier und in ähnlichen Fragen veröffentlichten Erweiterungsmethoden. Dadurch wird der Ticks-Wert auf leicht lesbare Weise überprüft und das DateTimeKind der ursprünglichen DateTime-Instanz beibehalten. (Dies hat subtile, aber relevante Nebenwirkungen beim Speichern in einer Datenbank wie MongoDB.)

Wenn das eigentliche Ziel darin besteht, eine DateTime auf einen bestimmten Wert (dh Stunden / Minuten / Sekunden / MS) zu kürzen, empfehle ich, diese Erweiterungsmethode stattdessen in Ihrem Code zu implementieren. Es stellt sicher, dass Sie nur mit einer gültigen Genauigkeit abschneiden können, und behält die wichtigen DateTimeKind-Metadaten Ihrer ursprünglichen Instanz bei:

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.");
}

Dann können Sie die Methode wie folgt verwenden:

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

dateTime.Kind => DateTimeKind.Utc

-1

Ich weiß, dass die Antwort ziemlich spät ist, aber der beste Weg, Millisekunden loszuwerden, ist

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

Versuchen Sie, den Wert der Variablen zu drucken. Es wird die Datums- und Uhrzeit ohne Millisekunden angezeigt.


1
Das ist nicht ideal. Sie haben dann eine Zeichenfolge und keine DateTime.
Jeff Putz
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.