Как округлить часы на основе минут (часы + 0, если мин <30, часы + 1 в противном случае)? - PullRequest
13 голосов
/ 23 марта 2010

Мне нужно округлить часы на основе минут в переменной DateTime. Условие таково: если минуты меньше 30, то минуты должны быть установлены на ноль и не должны меняться на часы, иначе, если минуты> = 30, то часы должны быть установлены на часы + 1, а минуты снова установлены на ноль. Секунды игнорируются.

Пример:
11/08/2008 04:30:49 должно стать 11/08/2008 05:00:00
и 11/08/2008 04:29:49 должны стать 11/08/2008 04:00:00

Я написал код, который прекрасно работает, но просто хотел узнать лучший метод, если можно было бы написать, а также был бы признателен за альтернативный метод (ы).

string date1 = "11/08/2008 04:30:49";
DateTime startTime;
DateTime.TryParseExact(date1, "MM/dd/yyyy HH:mm:ss", null,     
    System.Globalization.DateTimeStyles.None, out startTime);

if (Convert.ToInt32((startTime.Minute.ToString())) > 29)
{
    startTime = DateTime.Parse(string.Format("{0}/{1}/{2} {3}:{4}:{5}",
        startTime.Month.ToString(), startTime.Day.ToString(), 
        startTime.Year.ToString(), startTime.Hour.ToString(), "00", "00"));
    startTime = startTime.Add(TimeSpan.Parse("01:00:00"));
    Console.WriteLine("startTime is :: {0}", 
        startTime.ToString("MM/dd/yyyy HH:mm:ss"));
}
else
{
    startTime = DateTime.Parse(string.Format("{0}/{1}/{2} {3}:{4}:{5}", 
        startTime.Month.ToString(), 
        startTime.Day.ToString(), startTime.Year.ToString(), 
        startTime.Hour.ToString(), "00", "00"));

        Console.WriteLine("startTime is :: {0}", 
        startTime.ToString("MM/dd/yyyy HH:mm:ss"));
}

Ответы [ 9 ]

29 голосов
/ 23 марта 2010

Как альтернатива:

public static DateTime Round( DateTime dateTime )
{
    var updated = dateTime.AddMinutes( 30 );
    return new DateTime( updated.Year, updated.Month, updated.Day,
                         updated.Hour,  0, 0, dateTime.Kind );
}
22 голосов
/ 23 марта 2010

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

static DateTime RoundToHour(DateTime dt){
    long ticks = dt.Ticks + 18000000000;
    return new DateTime(ticks - ticks % 36000000000, dt.Kind);
}

Это также довольно простой и простой способ сделать это.

Чтобы объяснить,Структура DateTime на самом деле не имеет полей, в которых хранятся год, месяц, день, час, минута и т. Д. Она хранит одно значение long, число «тиков» с определенной эпохи (1 января 1 года нашей эры).Тик равен 100 наносекундам, или одной 10000000 тысячной секунды.

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

Итак, здесь мы добавляемконстанту, равную 30 минутам (30 * 60 * 1e7 = 18000000000 тиков), затем вычтите остаток после деления на константу, равную одному часу (60 * 60 * 1e7 = 36000000000 тиков).

6 голосов
/ 23 марта 2010

А как же:

public static DateTime RoundToHours(DateTime input)
{
DateTime dt = new DateTime(input.Year, input.Month, input.Day, input.Hour, 0, 0);

    if (input.Minute > 29)
      return dt.AddHours(1);
    else
      return dt;
}

Нет необходимости конвертировать в строку и обратно!

EDIT:
Использование input.Hour+1 в конструкторе не удастся, если значение часа равно 23. .AddHours(1) будет корректно приводить к «0:00» на следующий день.

4 голосов
/ 21 апреля 2015

А вот и все!

var rounded = date.AddMinutes(30).Date.AddHours(date.AddMinutes(30).Hour);

А для тех, кто хочет, это залило

var floored = date.Date.AddHours(date.Hours)
3 голосов
/ 25 мая 2010

Чтобы улучшить некоторые другие методы, вот метод, который также сохранит тип DateTime:

/// <summary>
/// Rounds a DateTime to the nearest hour.
/// </summary>
/// <param name="dateTime">DateTime to Round</param>
/// <returns>DateTime rounded to nearest hour</returns>
public static DateTime RoundToNearestHour(this DateTime dateTime)
{
  dateTime += TimeSpan.FromMinutes(30);

  return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 0, 0, dateTime.Kind);
}
3 голосов
/ 23 марта 2010

Расширение Hans Kestings хороший Ответ:

public DateTime RoundToHours(DateTime input)
{
      DateTime dt = new DateTime(input.Year, input.Month, input.Day, input.Hour, 0, 0);
      return dt.AddHours((int)(input.Minutes / 30));
}

(int) Приведение может не потребоваться.

EDIT: Адаптированы исправления, внесенные Hans Kesting в его Ответ.

3 голосов
/ 23 марта 2010
  DateTime s = DateTime.Now;
  if (s.Minute > 30) s = s.AddHours(1); //only add hours if > 30
  if (s.Minute == 30 && s.Second > 0) s = s.AddHours(1); //add precision as needed
  s = new DateTime(s.Year, s.Month, s.Day, s.Hour, 0, 0);
1 голос
/ 01 июня 2017

Основываясь на решении P Daddy, я предлагаю не жестко кодировать такое большое количество тиков до одного часа.Жесткое кодирование - это зло, не правда ли?С этим модифицированным решением вы можете округлять любое заданное время на любое количество минут:

    public DateTime RoundToMinutes(DateTime dt, int NrMinutes)
    {
        long TicksInNrMinutes = (long)NrMinutes * 60 * 10000000;//1 tick per 100 nanosecond
        long ticks = dt.Ticks + TicksInNrMinutes / 2;
        return new DateTime(ticks - ticks % TicksInNrMinutes, dt.Kind);
    }

Я использую это для округления до ближайших 5 минут, например, 22:23 становится 22: 25.

Несколько лет назад я использовал тот же метод для округления денежных сумм до ближайших 25 центов, например, 22,23 доллара становится 22,25 доллара.Но менеджер проекта иногда передумал, но изменение округления до ближайших 10 или 5 центов было бы тривиально.Так что теперь мне не нужно нервничать, когда мой проект mgr хочет, чтобы время округления до очередного nr минут.

Так что этот метод округления является и быстрым, и гибким.Мой метод уже был найден и опубликован в этом 2008 SO решении

1 голос
/ 23 марта 2010
DateTime dtm = DateTime.Now;
if (dtm.Minute < 30)
{
     dtm = dtm.AddMinutes(dtm.Minute * -1);
}
else
{    
     dtm = dtm.AddMinutes(60 - dtm.Minute);
}
dtm = dtm.AddSeconds(dtm.Second * -1);
...