PHP で日時関連の操作を実行する
-
09-06-2019 - |
質問
日付の追加、差異の検出、間隔内の週末を除いた日数の確認などの日時操作を実際に実行するにはどうすればよいでしょうか?私は個人的にこれらの操作の一部を postgresql DBMS に渡し始めました。通常は 1 つの SQL ステートメントを発行するだけで答えが得られます。ただし、これを PHP で実行するには、より多くのコードを記述する必要があり、より多くのチャンスを意味します。エラーが発生するために...
PHP には、多くのコードを必要としない方法で日時操作を行うライブラリはありますか?これは、「2 つの日付が与えられた場合、2 つの日付の間には何営業日ありますか?」という状況では 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;
解決
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
他のヒント
一方、ほとんどの日時操作では、通常は Unixtime に変換して加算減算などを実行します。Unixtime 整数については、Zend フレームワークの Zend_Date クラスを参照してください。
これには、あなたが説明した機能がたくさんあります。Zend は「フレームワーク」として宣伝されていますが、要素を選択するためのクラス ライブラリとして非常にうまく機能します。私たちは定期的にそれをプロジェクトに組み込み、必要に応じてビットを取り込むだけです。
strtotime() は便利ですが、書式設定された日付/時刻文字列を変換するためだけに使用しているわけではない場合、時々ポップアップする奇妙な動作がいくつかあります。
「+1 か月」や「-3 日」などでは、期待どおりの出力が得られない場合があります。
日付を追加するには、メソッドを使用できます 日時::追加 (DateTime オブジェクトに日、月、年、時、分、秒を追加します。)、php 5.3.0以降で利用可能です。
2 つの日付の違いを見つけるには、次の方法があります。 日時::差分 方法;しかし、2 つの日付間の営業日数をカウントする方法はないようです。
PEAR::Date
便利な機能がありそうです。
PEAR::Calendar
も役立つかもしれません。
次の組み合わせを使用できます。 ストラトタイム, エムクタイム そして 日付 算数をやります
これはコンボを使用して算術演算を行う例です。 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 = ($total_months - $months)/12;
echo $years . " Years ";
}
if($months > 0)
echo $months . " Months";
}
?>
@Rushi個人的にはstrtotime()が好きではありません。理由はわかりませんが、「2008-09-11 9:5 AM」のような文字列を strtotime に渡すと false が返されることを今朝発見しました...
あなたが提供したコードは、「2 つの日付が与えられた場合、2 つの日付の間には何営業日ありますか?」という例の問題を解決するとは思えません。SQL か $pet_lang で実装しますが、祝日のリストがあるかどうかは考慮していません...
次のように 2 つの日付の間の日数を取得できます。
$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);
PHP 5.2 の使用を強くお勧めします。 DateTime オブジェクト, 、日付計算を行うときに、UNIX タイムスタンプを使用するのではなく。UNIX タイムスタンプを返す PHP 日付関数を使用する場合、操作できる範囲は非常に限られています (例:1970 年以前には何もありませんでした)。
見ていただければ http://php.net/date 、使用例がいくつか見つかります。 mktime()
操作を実行します。
簡単な例としては、明日の日付が何になるかを計算することが挙げられます。これは、日の値に 1 を加算するだけで実行できます。 mktime()
次のように:
$tomorrow = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") + 1, date("Y")));
ここでは、明日の日付を含む YYYY-MM-DD の形式で日付を受け取ります。「+」を「-」に置き換えるだけで日数を減算することもできます。 mktime()
これにより作業が大幅に楽になり、ネストされた if ステートメントやその他の面倒なコーディングを行う必要がなくなります。
営業日/休日を取得するには、postgresql CTE ftw -- を参照してください。 http://osssmb.wordpress.com/2009/12/02/business-days-working-days-sql-for-postgres-2/