본문 바로가기

개발

31일에 다음 달 구하기

주어진 날짜 또는 현재 날짜의 다음 달을 구하는 경우가 있습니다.
보통 날짜에 한 달을 더해 다음 달을 구합니다.
주어진 날짜가 31일이면 한 달 더하는 방식은 문제가 있습니다.

날짜가 31일이면 한 달이 아닌 하루나 이틀 같은 날짜를 이용하여 다음 달을 구해야 합니다.

var target = new Date('2021-02-01');
target.setMonth(target.getMonth() + 1);
console.log(target); // Date Mon Mar 01 2021 09:00:00 GMT+0900 (대한민국 표준시)

target = new Date('2021-02-28');
target.setMonth(target.getMonth() + 1);
console.log(target); // Date Sun Mar 28 2021 09:00:00 GMT+0900 (대한민국 표준시)

target = new Date('2021-03-01');
target.setMonth(target.getMonth() + 1);
console.log(target); // Date Thu Apr 01 2021 09:00:00 GMT+0900 (대한민국 표준시)

target = new Date('2021-03-31');
target.setMonth(target.getMonth() + 1);
console.log(target); // Date Sat May 01 2021 09:00:00 GMT+0900 (대한민국 표준시)

// Date Sat May 01 2021 09:00:00 GMT+0900 (대한민국 표준시)

마지막에서 볼 수 있듯이, 3월 31일에 한 달을 더하면 5월 1일이 됩니다.

정확하게 표현하면 4월 31일이 되지만 존재하지 않는 날짜입니다.

달력을 일렬로 늘어놓았다고 생각하면 4월 31일 위치에 있는 날짜는 5월 1일입니다.
3월 31일 + 1달 = 4월 31일은 존재하지 않으므로, 해당하는 위치의 날짜는 5월 1일


한 달을 더하는 방식은 주어진 날짜에 해당하는 다음 달 날짜를 구하는 방식입니다. 3월 31일에 다음 달인 4월을 구하고자 하였으나 예기치 않게 5월을 얻습니다.

1월 특정 날짜에서 다음 달을 구할 때는 특히 심합니다.  2월은 보통 28일(윤년 29일)까지만 있습니다.

날짜가 28일을 넘어가면 현재 날짜에 남은 날짜를 더하여 다음 달을 구합니다.
날짜를 확인하여 분기 처리합니다.

var target = new Date('2021-03-31');
if (target.getDate() > 28) {
    var dayPlus = 32 - target.getDate();
    target.setDate(target.getDate() + dayPlus);
} else {
    target.setMonth(target.getMonth() + 1);
}

PHP에서도 같은 현상을 확인할 수 있습니다.

$target = DateTime::createFromFormat('Y-m-d', '2021-03-31');
$target->add(new DateInterval('P1M'));
var_dump($target);

object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2021-05-01 11:35:27.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Seoul"
}

object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2021-05-01 11:35:27.000000"

  ...
}

날짜를 확인하여 분기 처리합니다.

$target = DateTime::createFromFormat('Y-m-d', '2021-03-31');
if ($target->format('d') > '28') {
    $dayPlus = 32 - $target->format('d');
    $target->add(new DateInterval("P{$dayPlus}D"));
} else {
    $target->add(new DateInterval('P1M'));
}

 

반응형