Как округлять часы на основе минут (часов + 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"));
}
Ответы
Ответ 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-решении