关于sql:在PHP中执行与日期时间相关的操作

关于sql:在PHP中执行与日期时间相关的操作

performing datetime related operations in PHP

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

PHP中是否有不需要大量代码即可进行日期时间操作的库? 在"给定两个日期,两个日期之间有多少个工作日? 以SQL还是$ pet_lang'实现,通过查询来解决?

1
2
3
4
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;

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

它具有您描述的许多功能。尽管Zend被称为"框架",但它可以作为类库来从中进行选择,并表现得异常出色。我们通常将其包含在项目中,然后在需要时将其插入。


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

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


PHP5 +的DateTime对象很有用,因为它是跳跃时间,
知道夏令时,但实际上需要一些扩展
解决这个问题。我写了下面的文章来解决类似的问题。
find_WeekdaysFromThisTo()方法是蛮力的,但是,如果您的时间跨度小于2年,则可以相当快速地工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
$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) ."
"
;
/*  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) ."
"
;
/*  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.'."
"
;
/*  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

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

诸如" +1个月"或" -3天"之类的内容有时无法提供预期的输出。


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

PEAR::Calendar也可能有用。


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

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

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


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


您可以像这样获得两个日期之间的天数:

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

而且您可以使功能类似(我的工作计算机中未安装php,所以我不能保证语法是100%正确)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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并且始终使用相同格式的日期,则可以使用爆炸功能,例如

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

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

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

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

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


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

我认为您提供的代码无法解决示例问题"给定两个日期,两个日期之间有多少个工作日?用SQL或$ pet_lang'实现,我没有考虑是否有公共假期清单...


您可以结合使用strtotime,mktime和date进行算术运算

这是一个使用组合来做一些算术运算的例子http://rushi.wordpress.com/2008/04/13/php-print-out-age-of-date-in-words/我将在这里重现代码为简单起见

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 = ($total_months - $months)/12;
      echo $years ." Years";
   }
   if($months > 0)
      echo $months ." Months";
}
?>


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


推荐阅读