【问题标题】:PHP DateTime round up to nearest 10 minutesPHP DateTime 向上舍入到最接近的 10 分钟
【发布时间】:2013-11-17 19:53:06
【问题描述】:

我正在从 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');

任何建议表示赞赏。

谢谢。

【问题讨论】:

标签: php datetime


【解决方案1】:

1) 如有必要,将秒数设置为 0(通过四舍五入到最接近的分钟)

$second = $datetime->format("s");
if($second > 0)
    $datetime->add(new DateInterval("PT".(60-$second)."S"));

2) 获取分钟

$minute = $datetime->format("i");

3) 模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

【讨论】:

  • 时间已经可以被 10 分钟整除或为 0 秒时,并不精确。它分别增加了 10 分钟或 60 秒,因此它不可靠。下面有一个改进的版本。
  • 谢谢,现在应该修好了。
【解决方案2】:

我偶然发现了这个问题,想知道为什么所有的解决方案都如此复杂。就是这样:

function roundToNext10Min(\DateTime $dt, $precision = 10) {
    $s = $precision * 60;
    $dt->setTimestamp($s * (int) ceil($dt->getTimestamp() / $s));
    return $dt;
}

$dt = roundToNext10Min(new DateTime($str));
echo $dt->format('d/m/Y G:i');

【讨论】:

  • 未定义变量:str
【解决方案3】:

编辑:我很久以前写了这个答案,有些人发现不考虑毫秒。值得尝试除我之外的其他答案来找到完美的时间舍入方法。

@rand 的答案有一个错误,当我们要舍入的 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"));
        }
    }
}

这个功能现在非常适合我使用,可能也适合其他人使用。

编辑:现在它允许设置圆形上或下

【讨论】:

  • 试过了,@berturion 是对的。这应该是公认的答案。
  • 这不会舍入微秒,因此舍入关闭日期时间以查看它们是否彼此相等是行不通的。
  • 有人可以向我解释一下这是如何工作的而不返回任何东西吗?我从测试中看到它正在改变原始的 Datetime 对象,但是如果你没有通过引用传递它是如何工作的,例如&$datetime
  • @StephenR $datetime 是一个对象,所以默认是通过引用传递的
  • @IterAtor 我不知道。谢谢
【解决方案4】:

您可以使用 ceil、floor 和 round 函数将 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
    );
}

【讨论】:

  • 为避免对传递给这些函数的DateTime 对象进行意外更改,我建议先克隆该对象,或者使用DateTimeImmutable 而不是DateTime
  • 请注意,这依赖于手册中“超出范围的值被添加到其父值”的奇怪行为。即设置分钟 72 转换为 1H + 12 分钟。 (我猜这将是一个名为“setTime”的函数中的验证错误,而我可以理解为“addTime”)。此答案中的 roundUp 函数使用它来设置 current-hour, 60s = hour+1, 0s,这是在每个小时的最后一个 $minuteInterval 中正确舍入和溢出所需要的。
【解决方案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 's 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's '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's '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");
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-29
          • 2013-07-15
          • 1970-01-01
          • 1970-01-01
          • 2016-02-26
          • 1970-01-01
          相关资源
          最近更新 更多