PHP에서 날짜/시간 관련 작업 수행
-
09-06-2019 - |
문제
날짜 추가, 차이 찾기, 간격에서 주말을 제외한 일수 확인과 같은 날짜/시간 작업을 실제로 어떻게 수행합니까?저는 개인적으로 이러한 작업 중 일부를 postgresql dbms에 전달하기 시작했습니다. 일반적으로 답변을 얻으려면 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;
해결책
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부터 사용할 수 있습니다.
두 날짜의 차이를 찾으려면 날짜시간::diff 방법;하지만 두 날짜 사이의 근무일을 계산하는 방법은 없는 것 같습니다.
PEAR::Date
뭔가 유용한 기능이 있을 것 같습니다.
PEAR::Calendar
유용할 수도 있습니다.
다음의 조합을 사용할 수 있습니다. strtotime, mktime 그리고 날짜 연산을 해라
다음은 콤보를 사용하여 일부 산술을 수행하는 예입니다. 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가 반환된다는 사실을 발견했습니다.
귀하가 제공한 코드가 '두 날짜가 주어지면 두 날짜 사이에 근무일 수는 몇 일입니까?'라는 예제 문제를 해결하지 못하는 것 같습니다.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를 사용하는 것이 좋습니다. 날짜/시간 객체, 날짜 계산을 수행할 때 UNIX 타임스탬프를 사용하는 대신.UNIX 타임스탬프를 반환하는 PHP 날짜 함수를 사용하면 작업 범위가 매우 제한됩니다(예:1970년 이전에는 아무것도 없었습니다.)
당신이 살펴 본다면 http://php.net/날짜 , 당신은 사용의 몇 가지 예를 찾을 수 있습니다 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/