выполнение операций, связанных с датой и временем, в PHP

StackOverflow https://stackoverflow.com/questions/56568

  •  09-06-2019
  •  | 
  •  

Вопрос

Как вы на самом деле выполняете операции с датой и временем, такие как добавление даты, поиск разницы, определение количества дней без учета выходных в интервале?Я лично начал передавать некоторые из этих операций в свою базу данных postgresql, поскольку обычно мне нужно было выполнить только один оператор sql, чтобы получить ответ, однако, чтобы сделать это на PHP, мне пришлось бы написать гораздо больше кода, что означает больше шансов чтобы не было ошибок...

Существуют ли в PHP какие-либо библиотеки, которые выполняют операции с датой и временем таким образом, чтобы не требовалось много кода?это превосходит sql в ситуации, когда «Учитывая две даты, сколько рабочих дней между этими двумя датами?»Реализовать либо в SQL, либо в $pet_lang'е, что решается выполнением этого запроса?

SELECT  COUNT(*) AS total_days
FROM    (SELECT date '2008-8-26' + generate_series(0,
          (date '2008-9-1' - date '2008-8-26')) AS all_days) AS calendar
WHERE   EXTRACT(isodow FROM all_days) < 6;
Это было полезно?

Решение

Объект DateTime PHP5+полезен, потому что это трясовое время и дневное сбережения, но ему нужно некоторое расширение, чтобы действительно решить проблему.Я написал следующее, чтобы решить аналогичную проблему.Метод find_WeekdaysFromThisTo() является грубым, но он работает достаточно быстро, если ваш временной интервал составляет менее 2 лет.

$tryme = new Extended_DateTime('2007-8-26');
$newer = new Extended_DateTime('2008-9-1');

print 'Weekdays From '.$tryme->format('Y-m-d').' To '.$newer->format('Y-m-d').': '.$tryme -> find_WeekdaysFromThisTo($newer) ."\n";
/*  Output:  Weekdays From 2007-08-26 To 2008-09-01: 265  */
print 'All Days From '.$tryme->format('Y-m-d').' To '.$newer->format('Y-m-d').': '.$tryme -> find_AllDaysFromThisTo($newer) ."\n";
/*  Output:  All Days From 2007-08-26 To 2008-09-01: 371   */
$timefrom = $tryme->find_TimeFromThisTo($newer);
print 'Between '.$tryme->format('Y-m-d').' and '.$newer->format('Y-m-d').' there are '.
      $timefrom['years'].' years, '.$timefrom['months'].' months, and '.$timefrom['days'].
      ' days.'."\n";
/*  Output: Between 2007-08-26 and 2008-09-01 there are 1 years, 0 months, and 5 days. */

class Extended_DateTime extends DateTime {

    public function find_TimeFromThisTo($newer) {
        $timefrom = array('years'=>0,'months'=>0,'days'=>0);

        // Clone because we're using modify(), which will destroy the object that was passed in by reference
        $testnewer = clone $newer;

        $timefrom['years'] = $this->find_YearsFromThisTo($testnewer);
        $mod = '-'.$timefrom['years'].' years';
        $testnewer -> modify($mod);

        $timefrom['months'] = $this->find_MonthsFromThisTo($testnewer);
        $mod = '-'.$timefrom['months'].' months';
        $testnewer -> modify($mod);

        $timefrom['days'] = $this->find_AllDaysFromThisTo($testnewer);
        return $timefrom;
    } // end function find_TimeFromThisTo


    public function find_YearsFromThisTo($newer) {
        /*
        If the passed is:
        not an object, not of class DateTime or one of its children,
        or not larger (after) $this
        return false
        */
        if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
            return FALSE;
        $count = 0;

        // Clone because we're using modify(), which will destroy the object that was passed in by reference
        $testnewer = clone $newer;

        $testnewer -> modify ('-1 year');
        while ( $this->format('U') < $testnewer->format('U')) {
            $count ++;
            $testnewer -> modify ('-1 year');
        }
        return $count;
    } // end function find_YearsFromThisTo


    public function find_MonthsFromThisTo($newer) {
        /*
        If the passed is:
        not an object, not of class DateTime or one of its children,
        or not larger (after) $this
        return false
        */
        if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
            return FALSE;

        $count = 0;
        // Clone because we're using modify(), which will destroy the object that was passed in by reference
        $testnewer = clone $newer;
        $testnewer -> modify ('-1 month');

        while ( $this->format('U') < $testnewer->format('U')) {
            $count ++;
            $testnewer -> modify ('-1 month');
        }
        return $count;
    } // end function find_MonthsFromThisTo


    public function find_AllDaysFromThisTo($newer) {
        /*
        If the passed is:
        not an object, not of class DateTime or one of its children,
        or not larger (after) $this
        return false
        */
        if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
            return FALSE;

        $count = 0;
        // Clone because we're using modify(), which will destroy the object that was passed in by reference
        $testnewer = clone $newer;
        $testnewer -> modify ('-1 day');

        while ( $this->format('U') < $testnewer->format('U')) {
            $count ++;
            $testnewer -> modify ('-1 day');
        }
        return $count;
    } // end function find_AllDaysFromThisTo


    public function find_WeekdaysFromThisTo($newer) {
        /*
        If the passed is:
        not an object, not of class DateTime or one of its children,
        or not larger (after) $this
        return false
        */
        if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
            return FALSE;

        $count = 0;

        // Clone because we're using modify(), which will destroy the object that was passed in by reference
        $testnewer = clone $newer;
        $testnewer -> modify ('-1 day');

        while ( $this->format('U') < $testnewer->format('U')) {
            // If the calculated day is not Sunday or Saturday, count this day
            if ($testnewer->format('w') != '0' && $testnewer->format('w') != '6')
                $count ++;
            $testnewer -> modify ('-1 day');
        }
        return $count;
    } // end function find_WeekdaysFromThisTo

    public function set_Day($newday) {
        if (is_int($newday) && $newday > 0 && $newday < 32 && checkdate($this->format('m'),$newday,$this->format('Y')))
            $this->setDate($this->format('Y'),$this->format('m'),$newday);
    } // end function set_Day


    public function set_Month($newmonth) {
        if (is_int($newmonth) && $newmonth > 0 && $newmonth < 13)
            $this->setDate($this->format('Y'),$newmonth,$this->format('d'));
    } // end function set_Month


    public function set_Year($newyear) {
        if (is_int($newyear) && $newyear > 0)
            $this->setDate($newyear,$this->format('m'),$this->format('d'));
    } // end function set_Year
} // end class Extended_DateTime

Другие советы

Хотя для большинства операций с датой и временем я обычно конвертирую в Unixtime, выполняю сложение и вычитание и т. д.Что касается целого числа Unixtime, вы можете взглянуть на класс Zend_Date фреймворка Zend.

У этого есть много функций, которые вы описываете.Хотя Zend позиционируется как «фреймворк», он исключительно хорошо работает как библиотека классов, из которой можно выбирать элементы.Мы регулярно включаем его в проекты, а затем просто добавляем его по мере необходимости.

strtotime() полезен, но у него есть странное поведение, которое может время от времени всплывать, если вы не используете его просто для преобразования форматированной строки даты/времени.

такие вещи, как «+1 месяц» или «-3 дня», иногда могут не дать вам того, что вы ожидаете.

Для добавления даты вы можете использовать метод ДатаВремя::добавить (Добавляет количество дней, месяцев, лет, часов, минут и секунд к объекту DateTime.), доступный начиная с версии PHP 5.3.0.

Чтобы найти разницу между двумя датами, есть ДатаВремя::diff метод;но, похоже, не существует метода подсчета рабочих дней между двумя датами.

PEAR::Date похоже, у него может быть какая-то полезная функция.

PEAR::Calendar тоже может быть полезно.

Самый простой способ — использовать временную метку, обозначающую количество секунд с 1 января 2008 года.С типом метки времени вы можете делать такие вещи, как...

now = time();
tomorrow = now + 24 * 60 * 60; // 24 hours * 60 minutes * 60 seconds

Ознакомьтесь с документацией по время(), дата() и мктайм() на веб-страницах PHP.Это три метода, которые я использую чаще всего.

Вы можете использовать комбинацию стртотайм, мктайм и дата заняться арифметикой

Вот пример, в котором используется комбинация для выполнения некоторых арифметических действий. http://rushi.wordpress.com/2008/04/13/php-print-out-age-of-date-in-words/ Я воспроизведу код здесь для простоты.

if ($timestamp_diff < (60*60*24*7)) {
   echo floor($timestamp_diff/60/60/24)." Days";
} elseif ($timestamp_diff > (60*60*24*7*4)) {
   echo floor($timestamp_diff/60/60/24/7)." Weeks";
} else {
   $total_months = $months = floor($timestamp_diff/60/60/24/30);
   if($months >= 12) {
      $months = ($total_months % 12);
      $years&nbsp; = ($total_months - $months)/12;
      echo $years . " Years ";
   }
   if($months > 0)
      echo $months . " Months";
}
?>

@Rushi Лично мне не нравится strtotime()..я не знаю почему, но сегодня утром я обнаружил, что передача строки типа «2008-09-11 9:5 AM» в strtotime возвращает ложь...

Я не думаю, что предоставленный вами код решает задачу примера: «Для данных двух дат, сколько рабочих дней между этими двумя датами?»Реализуйте либо в SQL, либо в $pet_lang', и я не задумывался, есть ли у меня список государственных праздников...

Вы можете получить количество дней между двумя датами следующим образом:

$days = (strtotime("2008-09-10") - strtotime("2008-09-12")) / (60 * 60 * 24);

И вы можете сделать функцию примерно такой (на моем рабочем компьютере не установлен PHP, поэтому я не могу гарантировать, что синтаксис на 100% правильный)

function isWorkDay($date)
{
 // check if workday and return true if so
}

function numberOfWorkDays($startdate, $enddate)
{
  $workdays = 0;
  $tmp = strtotime($startdate);
  $end = strtotime($enddate);
  while($tmp <= $end)
  {
    if ( isWorkDay( date("Y-m-d",$tmp) ) ) $workdays++;
    $tmp += 60*60*24;
  }
  return $workdays;
}

Если вам не нравится strtotime и у вас всегда есть дата в одном и том же формате, вы можете использовать функцию разнесения, например

list($year, $month, day) = explode("-", $date);

Я настоятельно рекомендую использовать PHP 5.2. Объекты DateTime, а не использовать временные метки UNIX при расчете дат.Когда вы используете функции даты PHP, которые возвращают временные метки UNIX, у вас очень ограниченный диапазон для работы (например,ничего ранее 1970 года).

Если вы посмотрите на http://php.net/date , вы найдете несколько примеров использования mktime() для выполнения операций.

Простым примером может быть определение того, какое будет завтрашнее число.Вы можете сделать это, просто добавив 1 к значению дня в mktime() следующее:

$tomorrow = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") + 1, date("Y")));

Итак, вы получите дату в формате ГГГГ-ММ-ДД, содержащую завтрашнюю дату.Вы также можете вычесть дни, просто заменив «+» на «-». mktime() делает жизнь намного проще и избавляет вас от необходимости выполнять вложенные операторы if и другое подобное неприятное кодирование.

чтобы получить рабочие дни/праздники, postgresql CTE ftw - см. http://osssmb.wordpress.com/2009/12/02/business-days-working-days-sql-for-postgres-2/

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top