Как округлить временную метку unix вверх и вниз до ближайшего получаса?

хорошо, поэтому я работаю над календарным приложением в моей CRM-системе, и мне нужно найти верхнюю и нижнюю границы получаса, окружающего метку времени, в которой кто-то ввел событие в календаре, чтобы запустить некоторый SQL на БД, чтобы определить, есть ли у них уже что-то забронировано в пределах этого временного интервала.

например, у меня есть отметка времени 1330518155 = 29 февраля 2012 16: 22: 35 GMT+4 поэтому мне нужно получить 1330516800 и 1330518600, которые равны 16: 00 и 16:30.

Если у кого-нибудь есть какие-либо идеи или думаю, что я приближаюсь к разработке календаря глупым способом, дайте мне знать! Это мой первый раз на такой задаче, связанной с такой большой работой со временем и датами, поэтому любой совет ценится!

9 ответов


использовать по модулю.

$prev = 1330518155 - (1330518155 % 1800);
$next = $prev + 1800;

оператор по модулю дает оставшуюся часть деления.


PHP имеет класс DateTime и целый ряд методов, которые он предоставляет. Вы можете использовать их, если хотите, но мне проще использовать встроенный date() и strtotime() функции.

вот мое решение:

// Assume $timestamp has the original timestamp, i.e. 2012-03-09 16:23:41

$day = date( 'Y-m-d', $timestamp ); // $day is now "2012-03-09"
$hour = (int)date( 'H', $timestamp ); // $hour is now (int)16
$minute = (int)date( 'i', $timestamp ); // $minute is now (int)23

if( $minute < 30 ){
  $windowStart = strtotime( "$day $hour:00:00" );
  $windowEnd   = strtotime( "$day $hour:30:00" );
} else {
  $windowStart = strtotime( "$day $hour:30:00" );
  if( ++$hour > 23 ){
    // if we crossed midnight, fix the date and set the hour to 00
    $day = date( 'Y-m-d', $timestamp + (24*60*60) );
    $hour = '00';
  }
  $windowEnd   = strtotime( "$day $hour:00:00" );
}

// Now $windowStart and $windowEnd are the unix timestamps of your endpoints

есть несколько улучшений, которые можно сделать на этом, но это основное ядро.

[Edit:исправлены имена переменных!]

[Edit: я вернулся к этому ответу, потому что, к моему смущению, я понял, что это последние полчаса дня я вел себя неправильно. Я исправил эту проблему. Обратите внимание, что $day фиксируется путем добавления секунд на день к метке времени - это означает, что нам не нужно беспокоиться о пересечении границ месяца, високосных дней и т. д. потому что PHP будет форматировать его правильно для нас независимо.]


вы можете использовать оператор по модулю.

$time -= $time % 3600; // nearest hour (always rounds down)

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


Я не читал вопросы четко, но этот код будет округляться до ближайших получаса, для тех, кому не нужен диапазон между ними. Использует код Сеньорамора. Реквизит и его безумное элегантное решение правильного вопроса.

$time = 1330518155; //Or whatever your time is in unix timestamp

//Store how many seconds long our rounding interval is
//1800 equals one half hour
//Change this to whatever interval to round by
$INTERVAL_SECONDS = 1800;  //30*60

//Find how far off the prior interval we are
$offset = ($time % $INTERVAL_SECONDS); 

//Removing this offset takes us to the "round down" half hour
$rounded = $time - $offset; 

//Now add the full interval if we should have rounded up
if($offset > ($INTERVAL_SECONDS/2)){
  $nearestInterval = $rounded + $INTERVAL_SECONDS;
}
else{
  $nearestInterval = $rounded 
}

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

$now = date('U');
$offset = ($now % 1800);
$now = $now-$offset;
for ($i = 0;$i < 24; $i++)
{
    echo date('g:i',$now);
    $now += 1800;
}

или вы можете округлить, добавив смещение, и сделать что-то большее, чем просто эхо времени. Затем цикл for отображает 12 часов инкрементов. Я использовал вышеизложенное в недавнем проекте.


Как вы, вероятно, знаете, метка времени UNIX-это несколько секунд, поэтому substract/add 1800 (количество секунд в 30 минут), и вы получите желаемый результат.


Я бы использовать localtime и


далеко от моей лучшей работы... но вот некоторые функции для работы со строкой или меткой времени unix.

/**
 * Takes a timestamp like "2016-10-01 17:59:01" and returns "2016-10-01 18:00"
 * Note: assumes timestamp is in UTC
 * 
 * @param $timestampString - a valid string which will be converted to unix with time()
 * @param int $mins - interval to round to (ex: 15, 30, 60);
 * @param string $format - the format to return the timestamp default is Y-m-d H:i
 * @return bool|string
 */
function roundTimeString( $timestampString, $mins = 30, $format="Y-m-d H:i") {
    return gmdate( $format, roundTimeUnix( time($timestampString), $mins ));
}

/**
 * Rounds the time to the nearest minute interval, example: 15 would round times to 0, 15, 30,45
 * if $mins = 60, 1:00, 2:00
 * @param $unixTimestamp
 * @param int $mins
 * @return mixed
 */
function roundTimeUnix( $unixTimestamp, $mins = 30 ) {
    $roundSecs = $mins*60;
    $offset = $unixTimestamp % $roundSecs;
    $prev = $unixTimestamp - $offset;
    if( $offset > $roundSecs/2 ) {
        return $prev + $roundSecs;
    }
    return $prev;
}

это решение с использованием DateTimeInterface и держать информацию о часовом поясе etc. Будет также обрабатывать часовые пояса, которые не кратны 30 минутам смещения от GMT (например,Asia/Kathmandu).

/**
 * Return a DateTimeInterface object that is rounded down to the nearest half hour.
 * @param \DateTimeInterface $dateTime
 * @return \DateTimeInterface
 * @throws \UnexpectedValueException if the $dateTime object is an unknown type
 */
function roundToHalfHour(\DateTimeInterface $dateTime)
{
    $hours = (int)$dateTime->format('H');
    $minutes = $dateTime->format('i');

    # Round down to the last half hour period
    $minutes = $minutes >= 30 ? 30 : 0;

    if ($dateTime instanceof \DateTimeImmutable) {
        return $dateTime->setTime($hours, $minutes);
    } elseif ($dateTime instanceof \DateTime) {
        // Don't change the object that was passed in, but return a new object
        $dateTime = clone $dateTime;
        $dateTime->setTime($hours, $minutes);
        return $dateTime;
    }
    throw new UnexpectedValueException('Unexpected DateTimeInterface object');
}

вам нужно сначала создать объект DateTime, хотя-возможно, с чем-то вроде $dateTime = new DateTimeImmutable('@' . $timestamp). Вы также можете установить часовой пояс в конструкторе.