7

您如何实际执行日期时间操作,例如添加日期,查找差异,找出间隔中不包括周末的天数?我个人开始将其中一些操作传递给我的 postgresql dbms,因为通常我只需要发出一条 sql 语句即可获得答案,但是,要以 PHP 方式进行,我必须编写更多代码,这意味着更多机会发生错误...

PHP 中是否有任何库以不需要大量代码的方式执行日期时间操作?在“给定两个日期,两个日期之间有多少个工作日?在 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;
4

12 回答 12

5

虽然对于大多数日期时间操作,我通常会转换为 Unixtime 并对 Unixtime 整数执行加减法等,但您可能需要查看 Zend 框架 Zend_Date 类。

这有很多你描述的功能。尽管 Zend 被标榜为一个“框架”,但它作为一个类库工作得非常好,可以从中挑选元素。我们通常将其包含在项目中,然后在需要时将其拉入。

于 2008-09-11T13:55:16.717 回答
2

PEAR::Date看起来它可能有一些有用的功能。

PEAR::Calendar也可能有用。

于 2008-09-11T16:23:04.943 回答
2

strtotime() 很有用,但如果您不只是使用它来转换格式化的日期/时间字符串,它确实会不时弹出一些奇怪的行为。

像“+1 个月”或“-3 天”这样的东西有时不能给你你期望它输出的东西。

于 2008-09-11T21:32:47.240 回答
2

PHP5+ 的 DateTime 对象很有用,因为它可以感知闰时间和夏令时,但它需要一些扩展才能真正解决问题。我写了以下内容来解决类似的问题。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
于 2008-10-14T13:39:41.900 回答
2

要添加日期,您可以使用从 php 5.3.0 开始的方法DateTime::add向 DateTime 对象添加天、月、年、小时、分钟和秒的数量)。

要找出两个日期之间的差异,可以使用DateTime::diff方法;但似乎没有一种方法可以计算两个日期之间的工作日。

于 2010-01-06T14:20:14.190 回答
1

最简单的方法是使用时间戳,表示自 2008 年 1 月 1 日以来的秒数。使用时间戳类型,您可以执行以下操作...

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

在 php 网页上查看time()date()mktime()的文档。这是我最常使用的三种方法。

于 2008-09-11T13:58:35.960 回答
0

您可以使用strtotimemktimedate的组合来进行算术运算

这是一个使用组合 todo 一些算术的示例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";
}
?>
于 2008-09-11T15:33:37.417 回答
0

@Rushi我个人不喜欢 strtotime() ..我不知道为什么,但我今天早上发现将像这样的字符串 '2008-09-11 9:5 AM' 传递给 strtotime 会返回错误...

我认为您提供的代码不能解决示例问题'给定两个日期,两个日期之间有多少个工作日?在 SQL 或 $pet_lang' 中实现,我没有考虑是否有公共假期列表......

于 2008-09-11T15:39:58.540 回答
0

如果您查看http://php.net/date,您会发现一些使用mktime()来执行操作的示例。

一个简单的例子是锻炼明天的日期。您可以通过简单地将 1 添加到 day 值来做到这一点,mktime()如下所示:

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

因此,在这里,您将收到 YYYY-MM-DD 格式的日期,其中包含明天的日期。您也可以通过简单地将“+”替换为“-”来减去天数。mktime()让生活变得更轻松,并且使您不必进行嵌套 if 语句和其他此类麻烦的编码。

于 2008-09-12T09:22:57.173 回答
0

您可以获得两个日期之间的天数,如下所示:

$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 并且你总是有相同格式的日期,你可以使用 explode 函数,比如

list($year, $month, day) = explode("-", $date);
于 2008-09-12T10:13:39.563 回答
0

我强烈建议在进行日期计算时使用 PHP 5.2 的DateTime 对象,而不是使用 UNIX 时间戳。当您使用返回 UNIX 时间戳的 PHP 日期函数时,您可以使用的范围非常有限(例如 1970 年之前没有)。

于 2008-09-13T17:09:10.913 回答
-1

要获得工作日/假期,postgresql CTE ftw - 请参阅http://osssmb.wordpress.com/2009/12/02/business-days-working-days-sql-for-postgres-2/

于 2011-07-11T04:43:30.850 回答