LanguagePHP

`strtotime`에선 `-1 months` 또는 `1 month ago`는 30일 이전이 아니다.

PHP에서 strtotime 함수를 참으로 많이 사용한다. 날짜를 계산 할 때 이보다 편한 건 없기 때문이라고 할까나.
그런데 오늘 중요한 사실을 알았다. 머 이전부터 매뉴얼에는 나와 있었지만 그걸 오늘 알았다고 해야 하나.
아무튼 중요한 사실은 strtotime("-1 months"); 하면 무조건 이전 달을 구해 오는 건 아니라는 것이다.
이게 무슨 소리냐 하면, 우리가 오늘을 기준으로 이전 달을 구하고 싶을 때가 있을 것이다. 이달이 2월이니 이전 달인 1월을 원할 경우.

간단히 아래처럼

<?php
$time = time();
$prev_month = strtotime("-1 month", $time);

echo date("Y-m-d", $prev_month);
// 결과값 : 2013-01-25
?>

오늘이 2013년 02월 25일 이니 결과 값은 2013-01-25일 것이다. 그래 이거 맞다. 정확하게 내가 원하는 값을 가져왔다. – 몇일을 해서 가져오든 값은 맞으니 통과 그런데. 오늘이 3월 30일이라면? 이전달은 언제일까?
일단 2월에는 30일이 없으니 2월 28일이 나오지 않을까 하는 생각을 했다. 그리고 확인을 해보았다.

<?
$date = "2013-03-30";
$time = strtotime($date);
$prev_month = strtotime("1 months ago", $time);
echo date("Y-m-d", $prev_month);
//결과값 : 2013-03-02
?>

두둥!!. 결과는 2013-03-02.. 내가 원하는 값이 아니다. 한달 전을 구했는데 2월이 아니라 3월이 나온다. 무슨 문제일까? 그래서 아래와 같은 코드로 확인 해봤다.

<?php
$date = "2013-03-30";
$time = strtotime($date);

echo "기준일자 : ".date("Y-m-d", $time);
echo "<br />";
echo "Timestamp : ".$time;
echo "<br />";
echo "<br />";

// 한달전
$month1 = strtotime("1 months ago", $time);
echo "한달전 : ".date("Y-m-d", $month1);
echo "<br />";
echo "Timestamp : ".$month1;
echo "<br />";
echo "차이 : ".($time - $month1)." (".(($time - $month1) / 86400)."일)";
echo "<br />";
echo "<br />";

// 두달전
$month2 = strtotime("-2 months", $time);
echo "두달전 : ".date("Y-m-d", $month2);
echo "<br />";
echo "Timestamp : ".$month2;
echo "<br />";
echo "차이 : ".($time - $month2)." (".(($time - $month2) / 86400)."일)";
echo "<br />";
echo "<br />";
?>

결과는 아래와 같이 나왔다.

기준일자 : 2013-03-30
Timestamp : 1364569200

한달전 : 2013-03-02
Timestamp : 1362150000
차이 : 2419200 (28일)

두달전 : 2013-01-30
Timestamp : 1359471600
차이 : 5097600 (59일)

즉. 한달 전을 구하는거 이전달의 최대 일수 만큼 뺀다는 것이였다. 3월에서 한달 이전이면 2월달의 총일수인 28, 또는 29일을 빼는 것이다. 마찬가지로 2월달에서 한달전을 구하는 것은 1월달 총일수인 31을 빼는 것이다. 그래서 2013-03-30일을 기준으로 두달 전을 구하면 2월달의 총 월수인 28일 +1월달의 총 월수인 31일을 빼서 총 59일을 뺀 일이 나오는 것이다.

만약 반대로 한달 이전이 아닌 한달 후라고 한다면 이와 동일하게 할까?
그래서 아래와 같이 코드를 짜서 확인해 봤다.

<?php
$date = "2013-01-31";
$time = strtotime($date);

echo "기준일자 : ".date("Y-m-d", $time);
echo "<br />";
echo "Timestamp : ".$time;
echo "<br />";
echo "<br />";

// 한달후
$month1 = strtotime("+1 months", $time);
echo "한달후 : ".date("Y-m-d", $month1);
echo "<br />";
echo "Timestamp : ".$month1;
echo "<br />";
echo "차이 : ".($month1 - $time)." (".(($month1 - $time) / 86400)."일)";
echo "<br />";
echo "<br />";

// 두달후
$month2 = strtotime("+2 months", $time);
echo "두달후 : ".date("Y-m-d", $month2);
echo "<br />";
echo "Timestamp : ".$month2;
echo "<br />";
echo "차이 : ".($month2 - $time)." (".(($month2 - $time) / 86400)."일)";
echo "<br />";
echo "<br />";
?>

결과는 예상과 다르게 아래와 같이 나왔다.

기준일자 : 2013-01-31

Timestamp : 1359558000
한달후 : 2013-03-03

Timestamp : 1362236400
차이 : 2678400 (31일)
두달후 : 2013-03-31

Timestamp : 1364655600
차이 : 5097600 (59일)

2013-01-31일을 기준으로 한달 후를 구했으니 예상으로는 2013-02-28일이 나올줄 알았다. 하지만 결과는 31일 뒤인 2013-03-03을 출력하였다. 전달을 구할 때는 전달의 총일 만큼을 빼더니 다음 달을 구할 때는 기준달의 총일만큼을 더했다.

기존 strtotime 을 통하여 작업한 것중에 달을 구해야 하는 것들이 간간히 있었다. 실행일을 기준으로 전달과 다음 달을 구하고 할 때 단순히 +1 months-1 months를 사용하고는 했는데 이제는 그렇게 하지 말아야 겠다.
항시 그달의 1일을 구해서 다음 달과 이전달을 구해야 정확하게 구해질듯 하다.

덧1.
php.net 에서 댓글들으 보니 first day of 를 사용해서 하면 정확한 달이 나온다. 그런데 날짜는 매월 1일로 고정이 된다. 왜냐? first day 니깐.
지금으로 부터 이전달과 다음달을 구할때는 -1 months+1 months 만 쓰지 말고 first day of -1 monthfirst day of +1 months 를 쓰도록 하자.
그럼 날짜는 1일로 고정된다 해도 정확히 이전달과 다음 달을 구할수 있을 것이다. 하지만 날짜까지 정확히 구해야 한다면?????
그건 아직 필요하지 않으니 좀더 생각해보자. ㅎㅎ
기존 테스트 소스에 first day of 를 넣어서 다시 코드를 작성 하였다.

이전달을 구할 때

<?
$date = "2013-03-30";
$time = strtotime($date);

echo "기준일자 : ".date("Y-m-d", $time);
echo "<br />";
echo "Timestamp : ".$time;
echo "<br />";
echo "<br />";

// 한달전
echo "-1 months 사용<br />";
$month1 = strtotime("1 months ago", $time);
echo "한달전 : ".date("Y-m-d", $month1);
echo "<br />";
echo "Timestamp : ".$month1;
echo "<br />";
echo "차이 : ".($time - $month1)." (".(($time - $month1) / 86400)."일)";
echo "<br />";
echo "<br />";

// 두달전
$month2 = strtotime("-2 months", $time);
echo "두달전 : ".date("Y-m-d", $month2);
echo "<br />";
echo "Timestamp : ".$month2;
echo "<br />";
echo "차이 : ".($time - $month2)." (".(($time - $month2) / 86400)."일)";
echo "<br />";
echo "<br />";


echo "first day of -1 months 사용 (php 5.3.3 이상)<br />";
// 한달전
$month1 = strtotime("first day of 1 months ago", $time);
echo "한달전 : ".date("Y-m-d", $month1);
echo "<br />";
echo "Timestamp : ".$month1;
echo "<br />";
echo "차이 : ".($time - $month1)." (".(($time - $month1) / 86400)."일)";
echo "<br />";
echo "<br />";

// 두달전
$month2 = strtotime("first day of -2 months", $time);
echo "두달전 : ".date("Y-m-d", $month2);
echo "<br />";
echo "Timestamp : ".$month2;
echo "<br />";
echo "차이 : ".($time - $month2)." (".(($time - $month2) / 86400)."일)";
echo "<br />";
echo "<br />";
?>
기준일자 : 2013-03-30
Timestamp : 1364569200

-1 months 사용
한달전 : 2013-03-02
Timestamp : 1362150000
차이 : 2419200 (28일)

두달전 : 2013-01-30
Timestamp : 1359471600
차이 : 5097600 (59일)

first day of -1 months 사용 (php 5.3.3 이상)
한달전 : 2013-02-01
Timestamp : 1359644400
차이 : 4924800 (57일)

두달전 : 2013-01-01
Timestamp : 1356966000
차이 : 7603200 (88일)

다음 달을 구할 때

<?
$date = "2013-01-31";
$time = strtotime($date);

echo "기준일자 : ".date("Y-m-d", $time);
echo "<br />";
echo "Timestamp : ".$time;
echo "<br />";
echo "<br />";

echo "+1 months 사용<br />";
// 한달후
$month1 = strtotime("first day of +1 months", $time);
echo "한달후 : ".date("Y-m-d", $month1);
echo "<br />";
echo "Timestamp : ".$month1;
echo "<br />";
echo "차이 : ".($month1 - $time)." (".(($month1 - $time) / 86400)."일)";
echo "<br />";
echo "<br />";

// 두달후
$month2 = strtotime("+2 months", $time);
echo "두달후 : ".date("Y-m-d", $month2);
echo "<br />";
echo "Timestamp : ".$month2;
echo "<br />";
echo "차이 : ".($month2 - $time)." (".(($month2 - $time) / 86400)."일)";
echo "<br />";
echo "<br />";


echo "first day of 1 months 사용 (php 5.3.3 이상)<br />";
// 한달후
$month1 = strtotime("first day of +1 months", $time);
echo "한달후 : ".date("Y-m-d", $month1);
echo "<br />";
echo "Timestamp : ".$month1;
echo "<br />";
echo "차이 : ".($month1 - $time)." (".(($month1 - $time) / 86400)."일)";
echo "<br />";
echo "<br />";

// 두달후
$month2 = strtotime("first day of +2 months", $time);
echo "두달후 : ".date("Y-m-d", $month2);
echo "<br />";
echo "Timestamp : ".$month2;
echo "<br />";
echo "차이 : ".($month2 - $time)." (".(($month2 - $time) / 86400)."일)";
echo "<br />";
echo "<br />";
?>
기준일자 : 2013-01-31
Timestamp : 1359558000

+1 months 사용
한달후 : 2013-02-01
Timestamp : 1359644400
차이 : 86400 (1일)

두달후 : 2013-03-31
Timestamp : 1364655600
차이 : 5097600 (59일)

first day of 1 months 사용 (php 5.3.3 이상)
한달후 : 2013-02-01
Timestamp : 1359644400
차이 : 86400 (1일)

두달후 : 2013-03-01
Timestamp : 1362063600
차이 : 2505600 (29일)

덧2.
덧1에 추가한 내용인 first day oflast day of같은 방식으로 출력하는 것은 php 5.3.3 이상 부터 가능한 것 같다. 5.2.17버전에서 확인해보니 작동이 안된다. ㅠㅠ 젠장.