2009-06-17 2 views
249

Я пытаюсь сравнить метку времени от входящего запроса к сохраненной ценности базы данных. SQL Server, конечно же, сохраняет некоторую точность миллисекунд в то время, а при чтении в .NET DateTime он включает эти миллисекунды. Однако входящий запрос в систему не дает такой точности, поэтому мне нужно просто сбросить миллисекунды.Как урезать миллисекунды с .NET DateTime

Я чувствую, что мне не хватает чего-то очевидного, но я не нашел элегантный способ сделать это (C#).

ответ

442

Ниже будет работать на DateTime, который имеет дробные миллисекунды, а также сохраняет свойство Kind (Local, Utc или Undefined).

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

или эквивалент и короче:

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

Это может быть обобщен в метод расширения:

public static DateTime Truncate(this DateTime dateTime, TimeSpan timeSpan) 
{ 
    if (timeSpan == TimeSpan.Zero) return dateTime; // Or could throw an ArgumentException 
    return dateTime.AddTicks(-(dateTime.Ticks % timeSpan.Ticks)); 
} 

, который используется следующим образом:

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

Хотя я дам вам это, потому что вы технически верны, так как люди, читающие данные из SQL Server, сравниваются с некоторыми распределенными данными (в моем случае - это веб-запрос), этот размер разрешения не требуется. –

+0

Ницца. Очевидно, кто-то должен предоставить классу DateTime некоторые методы расширения для округления до ближайшего, чтобы этот тип хорошего кодирования снова использовался. –

+0

Это очень маловероятно, но разве этот подход не срабатывает, когда тики = 0? – adotout

27
DateTime d = DateTime.Now; 
d = d.AddMilliseconds(-d.Millisecond); 
+64

-1: Будет работать только в том случае, если значение DateTime не содержит долей миллисекунды. – Joe

+7

Использование этого метода привело к сбою некоторых моих модульных тестов: Ожидаемое: 2010-05-05 15: 55: 49.000 Но было: 2010-05-05 15: 55: 49.000. Я предполагаю, что Джо упомянул о долях миллисекунды. –

+6

Не работает для сериализации, например. 2010-12-08T11: 20: 03.000099 + 15: 00 - это выход, не полностью отбивающий миллисекунды. – joedotnot

114
var date = DateTime.Now; 

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

Простой и простой, просто не забудьте добавить «, date.Kind» в конец конструктора, чтобы убедиться, что вы не потеряете важную часть информации. – JMcDaniel

+6

Единственный волшебный ответ здесь. – Den

+0

Будьте осторожны в отношении этого решения в чувствительном к производительности коде. Мое приложение тратило 12% времени процессора в [System.DateTime.GetDatePart] (https://referencesource.microsoft.com/#mscorlib/system/datetime.cs,ff06f271f088f1a8). –

8

Вместо падения миллисекунды, то сравнение, почему бы не сравнить разницу?

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

или

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

первый вариант не работает, потому что TotalSeconds является двойным; он также возвращает миллисекунды. – Jowen

+1

Сравнение разницы не дает того же результата, что и усечение, а затем сравнение. Например. 5.900 и 6.100 меньше, чем на секунду, поэтому сравните их с вашим методом. Но усеченные значения 5 и 6 различны. Это зависит от ваших требований. – Joe

7

Менее очевидно, но более чем в 2 раза быстрее:

// 10000000 runs 

DateTime d = DateTime.Now; 

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

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

Обратите внимание, что второй параметр, 'd.AddMilliseconds (-d.Millisecond)', необязательно переводит DateTime точно в предыдущую, полную секунду. 'd.Ticks% TimeSpan.TicksPerMillisecond' тики (где-то между 0 и 9999) за пределами вашей второй останется. – Technetium

1

Что касается Диадистис. Это сработало для меня, за исключением того, что мне пришлось использовать «Пол» для удаления дробной части деления до умножения. Так,

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

становится

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

я ожидал бы деление двух длинных значений приведет к Лонгу, таким образом удаляя дробную часть, но он решает проблему, как двойные оставляя таж значение после умножения.

Eppsy

38

Вот метод расширения на основе предыдущего ответа, который позволит вам укоротить любое разрешение ...

Использование:

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

Класс:

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

Это действительно гибкое и многоразовое решение, которое является кратким и выразительным, не будучи чрезмерно многословным. Мое голосование - лучшее решение. – Jaans

+0

Вам действительно не нужны скобки вокруг операндов%. – ErikE

+3

.. но parens добавляют ясность, на мой взгляд. –

1

2 Методы расширения для решений, упомянутых выше

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

использование:

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

Иногда вы хотите усечь что-то на основе календаря, например, год или месяц. Ниже приведен метод расширения, позволяющий выбрать любое разрешение.

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

Простой ...

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

И еще ...

//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... 
+3

Преобразование в строки и синтаксический анализ - это ужасная идея с точки зрения производительности. –

+1

@JeffPutz true, но это * есть * но простой. Подходит для автоматизированного тестирования, когда значение, вставленное и извлеченное из БД, теряет тики (моя точная ситуация). Однако этот ответ может быть даже проще, чем он есть, поскольку «var now = DateTime.Parse (DateTime.Now.ToString()) работает отлично. –

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

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

И при использовании

ToLongDateString() // its show 19 February 2016. 

: P

1

Новый метод

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

// определить строки передать параметр возврата дд-ммм-гггг 24-февралем-2016

Или показано на текстовое поле

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

// помещаем на PageonLoad

0

К округлу к второму:

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

Заменить на TicksPerMinute, чтобы округлить до минуты.


Если ваш код производительности чувствителен, быть осторожными

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

Мое приложение тратит 12% процессорного времени в System.DateTime.GetDatePart.

Смежные вопросы