Saturday, November 27, 2010

研究一下PHP如何計算時間﹙二〇三八年問題:PHP5.2解決方案﹚


多年前,Unix電腦設定一九七〇年一月一日上午〇時〇分〇秒開始計算時間,加上二〇一〇年現時之普及電腦容量只能計算到二〇三八年,要用PHP舊有程式功能﹙function﹚計算一九〇〇年之前的時間,或者計算二〇三八年之後的時間皆不支援。

不支援的程式功能,解說如下:
PHP實例:
____________________

<?php

$date = "2010-11-27";
$newdate = strtotime ( '-3 day' , strtotime ( $date ) ) ;
$newdate = date ( 'Y-m-d' , $newdate );

echo $newdate; // 2010-11-24 //顯示正確

echo '<BR/>';

$date = "2039-11-27";
$newdate = strtotime ( '-3 day' , strtotime ( $date ) ) ;
$newdate = date ( 'Y-m-d' , $newdate );

echo $newdate; // 1969-12-28 // 顯示錯誤

echo '<BR/>';

$date = "1900-11-27";
$newdate = strtotime ( '-3 day' , strtotime ( $date ) ) ;
$newdate = date ( 'Y-m-d' , $newdate );

echo $newdate; // 1969-12-28 // 顯示錯誤

?>

____________________



不過,PHP5.2版本已有/DateTime 類﹙class﹚方便上述換算。

網上另有計算功能,可參考
http://xwisdomhtml.com/dateclass.html
不過,既然PHP5.2版本已有datetime class,此功能不必用了。

以下的實例,支援計算〇〇〇〇至九九九九年。公元四年,用0004表示。公元二二一年,用0221表示。

PHP實例:
____________________

<?php

function getDateTime ( $date, $format, $timeZone, $modification ) {
// ------ using dateTime in a simple way
// ------- e.g. getDateTime ( '2010-11-12', 'l', 'Asia/Hong_Kong', null )
// ------- e.g. getDateTime ( '2010-11-12', 'Y-m-d', 'Asia/Hong_Kong', null)
// ------- e.g. getDateTime ( 'today', 'Y-m-d', 'Asia/Hong_Kong', null )
// ------- e.g. getDateTime ( 'next Monday', 'Y-m-d', 'Asia/Hong_Kong', null )
// ------- e.g. getDateTime ( 'next Monday', 'Y-m-d', 'Asia/Hong_Kong', '14 days ago' )

$timeZone = new DateTimeZone ( $timeZone );
$dateObject = new DateTime( $date, $timeZone );
if ($modification == null) {
$formatted = $dateObject->format($format);
}

if ($modification != null) {

$dateObjectClone = clone $dateObject;
$dateObjectClone->modify ( $modification );
$formatted = $dateObjectClone->format($format);
}

unset ( $timeZone );
unset ( $dateObject );
unset ( $dateObjectClone );
return $formatted; // this is a string of time

} // function getDateTime ( $date, $format, $timeZone, $modification ) {


echo getDateTime ( '0000-01-01', 'Y-m-d', 'Asia/Hong_Kong', null );
echo '<BR/>';
echo getDateTime ( '0000-01-01', 'l', 'Asia/Hong_Kong', null );
echo '<BR/>';
echo getDateTime ( '0000-01-01', 'Y-m-d', 'Asia/Hong_Kong', '2 days' );
echo '<BR/>';
echo getDateTime ( '2010-11-27', 'Y-m-d', 'Asia/Hong_Kong', '2 days' );
echo '<BR/>';
echo getDateTime ( '2057-11-27', 'Y-m-d', 'Asia/Hong_Kong', '2 days' );
echo '<BR/>';
echo getDateTime ( '2010-11-27', 'Y-m-d', 'Asia/Hong_Kong', 'last Thursday' );
echo '<BR/>';
echo getDateTime ( '2010-11-27', 'Y-m-d', 'Asia/Hong_Kong', 'next Friday' );
echo '<BR/>';
echo getDateTime ( 'today', 'Y-m-d', 'Asia/Hong_Kong', null ); // 今天的日期:何年、何月、何日
echo '<BR/>';
echo getDateTime ( 'now', 'H:i:s', 'Asia/Hong_Kong', null );
?>
____________________


建議放棄用strtotime 及放棄date,改用DateTime類﹙class﹚。這就可以及時避免二〇三八年問題。長遠計劃,諸如三十年後的人口預測,皆需要程式功能,支援計算二〇三八年後的時間。趁未遇問題時,及早防範,以免空費時日,更正錯誤。


======================================
----This posting in English----

The "Year 2038 Problem" is a problem which occurs when the computer (as of 2010) cannot calculate the time after 2038. The memory inside the computer can only calculate the time up to 2038. Unix computers were designed to calculate the time starting from 00:00:00 on 1 January, 1970. The old functions in PHP do not support processing the years after 2038.

Those old functions include "strtotime" and "date".

____________________


<?php

$date = "2010-11-27";
$newdate = strtotime ( '-3 day' , strtotime ( $date ) ) ;
$newdate = date ( 'Y-m-d' , $newdate );

echo $newdate; // 2010-11-24 // That's correct

echo '<BR/>';

$date = "2039-11-27";
$newdate = strtotime ( '-3 day' , strtotime ( $date ) ) ;
$newdate = date ( 'Y-m-d' , $newdate );

echo $newdate; // 1969-12-28 // That's an error

echo '<BR/>';

$date = "1900-11-27";
$newdate = strtotime ( '-3 day' , strtotime ( $date ) ) ;
$newdate = date ( 'Y-m-d' , $newdate );

echo $newdate; // 1969-12-28 // That's an error

?>

____________________




PHP 5.2 supports the DateTime class. This class can calculate a range of dates from 0000-01-01 to 9999-12-31.

The DateTime class solves the problem.


____________________
<?php

function getDateTime ( $date, $format, $timeZone, $modification ) {
// ------ using dateTime in a simple way
// ------- e.g. getDateTime ( '2010-11-12', 'l', 'Asia/Hong_Kong', null )
// ------- e.g. getDateTime ( '2010-11-12', 'Y-m-d', 'Asia/Hong_Kong', null)
// ------- e.g. getDateTime ( 'today', 'Y-m-d', 'Asia/Hong_Kong', null )
// ------- e.g. getDateTime ( 'next Monday', 'Y-m-d', 'Asia/Hong_Kong', null )
// ------- e.g. getDateTime ( 'next Monday', 'Y-m-d', 'Asia/Hong_Kong', '14 days ago' )

$timeZone = new DateTimeZone ( $timeZone );
$dateObject = new DateTime( $date, $timeZone );
if ($modification == null) {
$formatted = $dateObject->format($format);
}

if ($modification != null) {

$dateObjectClone = clone $dateObject;
$dateObjectClone->modify ( $modification );
$formatted = $dateObjectClone->format($format);
}

unset ( $timeZone );
unset ( $dateObject );
unset ( $dateObjectClone );
return $formatted; // this is a string of time

} // function getDateTime ( $date, $format, $timeZone, $modification ) {


echo getDateTime ( '0000-01-01', 'Y-m-d', 'Asia/Hong_Kong', null );
echo '<BR/>';
echo getDateTime ( '0000-01-01', 'l', 'Asia/Hong_Kong', null );
echo '<BR/>';
echo getDateTime ( '0000-01-01', 'Y-m-d', 'Asia/Hong_Kong', '2 days' );
echo '<BR/>';
echo getDateTime ( '2010-11-27', 'Y-m-d', 'Asia/Hong_Kong', '2 days' );
echo '<BR/>';
echo getDateTime ( '2057-11-27', 'Y-m-d', 'Asia/Hong_Kong', '2 days' );
echo '<BR/>';
echo getDateTime ( '2010-11-27', 'Y-m-d', 'Asia/Hong_Kong', 'last Thursday' );
echo '<BR/>';
echo getDateTime ( '2010-11-27', 'Y-m-d', 'Asia/Hong_Kong', 'next Friday' );
echo '<BR/>';
echo getDateTime ( 'today', 'Y-m-d', 'Asia/Hong_Kong', null ); // Date today in the form of "year-month-date"
echo '<BR/>';
echo getDateTime ( 'now', 'H:i:s', 'Asia/Hong_Kong', null );
?>

____________________


Another class may solve the same problem. That class is located at
http://xwisdomhtml.com/dateclass.html
However, the DateTime class is available in PHP 5.2. It is not necessary to use another class to do the same thing.

It is advisable to use the DateTime class instead of the "date" or "strtotime" functions. It saves the time spent on fixing the errors caused by the 2038 problem. For example, when forecasting the population thirty years from now, we may need to calculate the years after 2040.


======================================




參考 Reference
http://en.wikipedia.org/wiki/Year_2038_problem

http://xwisdomhtml.com/dateclass.html

http://php.net/manual/en/class.datetime.php

No comments: