[첫화면으로]Perl/DateTime

마지막으로 [b]

Cpan:DateTime

1. 날짜를 더하거나 빼기

애초에 달력이란 게 달마다 날 수가 다르고 윤년에 서머타임에 여러 요소들이 섞여 있어서, 이게 뜻밖의 경우를 당하기 쉽다.

문서에 있는 [How DateTime Math Works 절]에서 눈에 들어오는 부분 위주로 옮겨보면:

DateTime은 덧셈이나 뺄셈을 할 때 일, 월, 분, 초, 나노초 순서로 연산한다. 오버플로우가 날 경우는 각 단계에서 조정한다. 따라서 2월 28일에 "1개월 1일"를 더하면 3월 29일이 되는 게 아니라 4월 1일이 된다. 반면에 1개월을 먼저 더한 후에 1일을 더하면 3월 29일이 된다.

my $dt = DateTime->new( year => 2003, month => 2, day => 28 );
$dt->add( months => 1, days => 1 );
# 2003-04-01 - the result

# 반면에, ($dt가 2월 28일인 상태에서)
$dt->add( months => 1 )->add( days => 1 );
# 2003-03-29

서머 타임의 경우도 유사한 문제가 발생한다.

my $dt = DateTime->new(
    year      => 2003,
    month     => 4,
    day       => 5,
    hour      => 1,
    minute    => 58,
    time_zone => "America/Chicago",
);

$dt->add( days => 1, minutes => 3 );
# 2003-04-06 02:01:00

$dt->add( minutes => 3 )->add( days => 1 );
# 2003-04-06 03:01:00

이런 경우는 datetime 객체를 UTC 타임존으로 변환한 후 연산하면 결과를 미리 예상하기 쉽다.

Cpan:DateTime::Duration 모듈에는, 개월 단위로 더할 때 "달의 마지막 날"을 계산하는 세 가지 알고리즘을 제공한다. 이 알고리즘은 덧셈의 결과가 그 달의 마지막 날을 넘어갈 때(1월 30일에 1개월을 더하는 경우처럼) 영향을 미친다.

# 2010-08-31 + 1 month = 2010-10-01
$dt->add( months => 1, end_of_month => 'wrap' );

# 2010-01-30 + 1 month = 2010-02-28
$dt->add( months => 1, end_of_month => 'limit' );

# 2010-04-30 + 1 month = 2010-05-31
$dt->add( months => 1, end_of_month => 'preserve' );

디폴트 동작은 양의 duration 값에 대해서는 "wrap"을, 음의 duration 값에 대해서는 "preserve"를 적용한다고 한다.

저 gist에 있는 "다음 달 10일"을 찾는 코드는 주인장이 실제로 업무용 코드를 작성하며 문제가 되었던 코드이다. 1월 31일을 기준으로 다음 달 10일을 찾기 위해 "다음 달(1개월 더하기)"를 먼저 했더니 3월 3일이 되고 그 다음 "10일"로 설정하면 결과적으로 3월 10일이 되어 버린다. 이런 경우는 "10일"로 설정하는 걸 먼저 해주어야 한다.

2. Comments

이름:  
Homepage:
내용:
 

컴퓨터분류

마지막 편집일: 2015-2-3 9:39 pm (변경사항 [d])
1149 hits | Permalink | 변경내역 보기 [h] | 페이지 소스 보기