Подтвердить что ты не робот

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

Мне нужно округлить часы на основе минут в переменной 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"));
}
4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

Чтобы объяснить, структура DateTime фактически не имеет полей, в которых хранятся год, месяц, день, час, минута и т.д. В нем хранится одно значение long, количество "тиков" с определенной эпохи ( 1 января, 1 г. н.э.). Тик - 100 наносекунд, или один 10 000 000-й секунды.

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

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

Ответ 3

Как насчет:

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

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

Ответ 5

Расширение Ханса Кестинтинга хорошего ответа:

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) Cast может не понадобиться.

EDIT: Адаптированные исправления Ганса Кестинга сделаны в его ответе.

Ответ 6

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

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

Ответ 7

Вот так!

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

И для тех, кто хочет его настилать

var floored = date.Date.AddHours(date.Hours)

Ответ 8

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

Ответ 9

Основываясь на решении 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 центов было бы тривиальным. Так что теперь мне тоже не нужно нервничать, когда мой проект-мрг хочет округлить время до другого раунда минут минут.

Таким образом, этот метод округления является быстрым и гибким.


Мой метод уже был найден и опубликован в этом 2008 SO-решении