PHP DateTime округляется до ближайших 10 минут
Я извлекаю datetime из поля mysql, но мне нужно округлить его вверх до ближайших 10 минут.
Например, если дата-время - 2013-11-06 14:00:01, я хотел бы вернуть время как 6/11/2013 14:10.
Какой самый простой способ сделать это?
$datetime = new DateTime($mysqldata);
echo $datetime->format('d/m/Y G:i');
Любые советы приветствуются.
Спасибо.
Ответы
Ответ 1
1) Установите необходимое количество секунд в 0 (округляя до ближайшей минуты)
$second = $datetime->format("s");
if($second > 0)
$datetime->add(new DateInterval("PT".(60-$second)."S"));
2) Получите минутку
$minute = $datetime->format("i");
3) Преобразование modulo 10
$minute = $minute % 10;
4) Подсчитайте минуты до следующих 10-кратных минут при необходимости
if($minute != 0)
{
// Count difference
$diff = 10 - $minute;
// Add difference
$datetime->add(new DateInterval("PT".$diff."M"));
}
Отредактировано, спасибо @Ondrej Henek и @berturion
Ответ 2
Редактировать: я написал этот ответ давным-давно, некоторые люди обнаружили, что миллисекунды не учитываются. Чтобы найти идеальный метод округления времени, стоит попробовать другие ответы, кроме моего.
Ответ @and имеет ошибку, когда DateTime, который мы хотим округлить, уже кратен 10 минутам. Он будет прыгать на 10 минут позже, пока он не нужен.
Кроме того, переход к следующей минуте с ($datetime->add(new \DateInterval("PT".(60-$second)."S"));
нежелателен, когда секунды уже на нуле.
Эта расширенная функция исправляет эти ошибки и принимает параметр $ precision для охвата большего количества вариантов использования.
function roundTime(\DateTime &$datetime, $precision = 30, $round_lower = false) {
// 1) Set number of seconds to 0 (by rounding up to the nearest minute if necessary)
$second = (int) $datetime->format("s");
if ($second > 30 && $round_lower == false) {
// Jumps to the next minute
$datetime->add(new \DateInterval("PT".(60-$second)."S"));
} elseif ($second > 0) {
// Back to 0 seconds on current minute
$datetime->sub(new \DateInterval("PT".$second."S"));
}
// 2) Get minute
$minute = (int) $datetime->format("i");
// 3) Convert modulo $precision
$minute = $minute % $precision;
if ($minute > 0) {
if($round_lower) {
$datetime->sub(new \DateInterval("PT".$minute."M"));
} else {
// 4) Count minutes to next $precision-multiple minutes
$diff = $precision - $minute;
// 5) Add the difference to the original date time
$datetime->add(new \DateInterval("PT".$diff."M"));
}
}
}
Эта функция теперь идеально подходит для моего использования и, вероятно, для других людей.
Редактировать: теперь он позволяет устанавливать круглую верхнюю или нижнюю
Ответ 3
Я наткнулся на этот вопрос и задался вопросом, почему все решения настолько сложны. Это просто:
function roundToNext10Min(\DateTime $dt, $precision = 10) {
$s = $precision * 60;
$dt->setTimestamp($s * ceil($dt->getTimestamp() / $s));
return $dt;
}
$dt = roundToNext10Min(new DateTime($str));
echo $dt->format('d/m/Y G:i');
Ответ 4
Вы можете использовать функции "Потолок", "Пол" и "Круг", чтобы округлить DateTime либо вверх, вниз, либо до ближайшего минутного интервала следующим образом:
/**
* @param \DateTime $dateTime
* @param int $minuteInterval
* @return \DateTime
*/
public function roundUpToMinuteInterval(\DateTime $dateTime, $minuteInterval = 10)
{
return $dateTime->setTime(
$dateTime->format('H'),
ceil($dateTime->format('i') / $minuteInterval) * $minuteInterval,
0
);
}
/**
* @param \DateTime $dateTime
* @param int $minuteInterval
* @return \DateTime
*/
public function roundDownToMinuteInterval(\DateTime $dateTime, $minuteInterval = 10)
{
return $dateTime->setTime(
$dateTime->format('H'),
floor($dateTime->format('i') / $minuteInterval) * $minuteInterval,
0
);
}
/**
* @param \DateTime $dateTime
* @param int $minuteInterval
* @return \DateTime
*/
public function roundToNearestMinuteInterval(\DateTime $dateTime, $minuteInterval = 10)
{
return $dateTime->setTime(
$dateTime->format('H'),
round($dateTime->format('i') / $minuteInterval) * $minuteInterval,
0
);
}
Ответ 5
Я бы использовал временные метки:
<?php
function roundTime(\DateTime $datetime, $precision = 10) {
$ts = $datetime->getTimestamp();
$s = ($precision * 60);
$remainder = $ts % $s;
if ($remainder > 0) {
$datetime->setTimestamp($ts + $s - $remainder);
}
return $datetime;
}
echo roundTime(\DateTime::createFromFormat('Y-m-d H:i:s', '2016-03-04 09:00:00'))
->format('Y-m-d H:i:s');
// prints: 2016-03-04 09:00:00
echo roundTime(\DateTime::createFromFormat('Y-m-d H:i:s', '2016-03-04 09:00:01'))
->format('Y-m-d H:i:s');
// prints: 2016-03-04 09:10:00
Ответ 6
public function roundupMinutesFromDatetime(\Datetime $date, $minuteOff = 10)
{
$string = sprintf(
"%d minutes %d seconds",
$date->format("i") % $minuteOff,
$date->format("s")
);
return $date->sub(\DateInterval::createFromDateString($string));
}
И тест:
public function datetimeProvider()
{
return array(
array('2015-01-01 15:16:17', '2015-01-01 15:10:00'),
array('2015-01-01 09:59:39', '2015-01-01 09:50:00'),
);
}
/**
* @dataProvider datetimeProvider
*/
public function testRoundupToMinutesFromDatetime($dateStr, $dateStrAssert)
{
$format = 'Y-m-d H:i:s';
$date = \DateTime::createFromFormat($format, $dateStr);
$date = $this->repo->roundupMinutesFromDatetime($date);
$this->assertEquals($dateStrAssert, $date->format('Y-m-d H:i:s'));
}
получайте удовольствие.
Ответ 7
вам нужно использовать функцию strtotime()
для преобразования даты в метку времени, а затем добавить или удалить минуты, например
<?php
$datenow = date("Y-m-d H:i:s", time());
$maketime = strtotime($datenow );
$minutes = 10;
$after_10min = date("Y-m-d H:i:s", ( $maketime+(60*$minutes) ));
$before_10min = date("Y-m-d H:i:s", ( $maketime-(60*$minutes) ));
echo "Date now : $datenow <br />";
echo "Date After $minutes min : $after_10min <br />";
echo "Date Before $minutes min : $before_10min <br />";
?>
Ответ 8
Простой код
<?php
function next10Minutes() {
$time = time() + 600;
$nextMinutes = floor(date("i", $time) / 10) * 10;
if ($nextMinutes < 10) {
$nextMinutes = "00";
}
return strtotime(date("d-m-Y H:{$nextMinutes}:00", $time));
}
Ответ 9
Вот функция, которая округляет (вверх, вниз или просто округляет) до произвольного числа секунд, минут или часов. Вдохновленный @esqilin answer
Многие ботаны умерли, чтобы принести вам эту информацию.
PHP 7, хотя, вероятно, не трудно настроить на 5.
/**
* Round $datetime to nearest <$precision> <$unit>s - e.g nearest 10 seconds -- or always up/down per $calc
* @param \DateTimeInterface $datetime
* @param int $precision Round to the nearest number of {$unit}s
* @param string $unit 'h', 'm', or 's' for Hours, Minutes, or Seconds; or PHP 'G'/'H' (hours) or 'i' (minutes)
* @param int $calc -1, 0, or 1 = floor, round, or ceiling calculations
* @return \DateTimeInterface
* @throws \Exception
*/
function roundTime( \DateTimeInterface $datetime, int $precision = 1, string $unit = 's', int $calc = 0 ) :\DateTimeInterface {
$offset = $datetime->format( 'Z' );
$datetime = ( clone $datetime )->modify( "+$offset seconds" );
switch( $unit ) { // convert human readable units to PHP DateTime format string
case 'h':
case 'H':
$unit = 'G';
break;
case 'm':
$unit = 'i';
break;
// 's' -> 's', no change
}
$secs = [ 'G'=>3600, 'i'=>60, 's'=>1 ]; // seconds per hour/minute/second
if( ! in_array( $unit, array_keys( $secs ) ) ) {
trigger_error( "Unknown \$unit parameter '$unit' in " . __FUNCTION__ . ". Recognized formats are 'h', 'm', 's', (hours/minutes/seconds), or PHP 'G', 'H', or 'i'" );
return $datetime;
}
$precision = $precision * $secs[$unit];
if( -1 === $calc ) {
$round = 0;
} elseif( 1 === $calc ) {
$round = $precision;
} elseif( 1 == $precision && 's' == $unit ) { // in this -- and only this -- case, we need partial seconds
$round = ( (int) $datetime->format('v') ) >= 500 ? 1 : 0;
} else {
$round = ( ( (int) $datetime->format('U') ) % $precision >= ( $precision/2 ) ) ? $precision : 0;
}
$result = $datetime->setTimestamp( ( $precision * floor( $datetime->format('U') / $precision ) ) + $round - $offset );
return $result;
}
В случае, предложенном в Вопросе, вы должны использовать:
$datetime = new DateTime($mysqldata);
echo roundTime( $datetime, 10, 'm', 1 );
Ответ 10
Я перегрузил класс и создал метод:
class MyWish_DateTime extends DateTime {
public function formatToNearRangeMinutesSql($range) {
$midleRange = $range / 2;
$minutes = self::format("i");
$modulo = $minutes % $range;
if ($modulo > $midleRange) {
$this->addMinutes($range - $modulo);
}
else {
$this->addMinutes(-$modulo);
}
return self::format("H:i");
}
}