[백업][가리사니] 마지막 일 구하기 [ 윤년 구하기 (leap year) ]
other

이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.

어떤 커뮤니티에 해당 월의 마지막 날짜 구하기가 질문으로 올라와서 뻘글을... 작성해봤습니다.

조건 1. 연도 범위 0 보다 큼 https://en.wikipedia.org/wiki/Gregorian_calendar 찾아봤지만 기원전 윤년 계산법은 모르겠습니다. 0년이 없기대문에 -1년이 윤년인가?..;;; 조건 2. 월 1 ~ 12 범위로 들어옴.

int lastDay(int year, int month);

1. 먼저 월을 통해서 마지막 날짜를 구해보겠습니다.

31일 : 1, 3, 5, 7, 8, 10, 12 30일 : 4, 6, 9, 11 28/29일 : 2 여기서 떠오르는건!! 30이라는 숫자(일 : 28/29/30/31)는 2진법으로 아래와 같습니다. 0b...11110 // 30 그리고 1부터 12까지의 숫자를 보면 0b...0001 // 1 ++ 0b...0010 // 2 0b...0011 // 3 ++ 0b...0100 // 4 0b...0101 // 5 ++ 0b...0110 // 6 0b...0111 // 7 ++ 0b...1000 // 8 ++ (0 -> 1 올림 발생) 0b...1001 // 9 0b...1010 // 10 ++ 0b...1011 // 11 0b...1100 // 12 ++ 0b...1101 // 13 아래 설명 그렇다면!! 30 | (month + (month > 7 ? 1 : 0)) // 언어에따라 true가 1이 반환되고 false가 0이 반환된다면 삼항식 필요없음. 혹은 30 | (month + ((month & 8) != 0 ? 1 : 0)) // 1000 8 ++ (0 -> 1 올림 발생 마찬가지로 if문에 숫자가 허용될경우 8 != 0 필요없음 month & 8) 이렇게 할 경우 2월을 제외하고 구할 수 있습니다.

int lastDay(int year, int month)
{
	return (30 | (month + (month > 7 ? 1 : 0)));
	// month = 2 : 30 error
}

그럼 2월을 구해봐야겠군요. 조건 1 : 4로 나누어 떨어지면 29일 그렇지 않으면 28일이다. 조건 2 : 100으로 나누어 떨어질 경우 28일이다. 마이크로소프트의 엑셀의 경우 처음 이 규칙을 몰라서 1900-02-29 일이 존재하며, 이것이 데이터엔 정수로 기록되기 때문에 기존문서의 호환성을 위해 유지중. (외국에선 빌게이츠력이라고...) : 회사에서 엑셀 변환 만들다가 1900년도 초기에서 숫자가 이상한걸 발견하고 조사해서 알아낸 사실...;; 조건 2 : 400으로 나누어 떨어질 경우 29일이다. 여기서 재미있는 건 2진법은 << 가되면 * 2 >> 가되면 / 2 가 됩니다. (물론 넘치거나 부족하면 다 버려집니다........) 4는 : 0b100 -> 양의정수 & 3(0b11) == 0 -> 4의 배수 16은 : 0b10000 -> 양의정수 & 15(0b1111) == 0 -> 16의 배수 양의정수 & 3 에서 % 25 를 사용한 경우 25 / 50 / 75는 & 3에 걸림으로 100 단위로 떨어지게 됩니다. 즉 (year & 3) == 0 && (year % 25) == 100 년 단위로 가져오게됨. 여기서 좀 더 응용하게 될 경우. (year & 3 == 0) && (year & 15 == 0) 1에서 400 까지 16,32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,272,288,304,320,336,352,368,384,400 : 즉 100단위와 겹치는 것은 400 뿐이다.

int lastDay(int year, int month)
{
	return ( // HACK : 어떤 스크립트는 return 줄 비워두면 없는줄알고 아무것도 리턴안함....... ;;;;; 그래서 ( 씀.
		month != 2
		? (30 | (month + (month > 7 ? 1 : 0)))
		:
		(
			// 4의 배수 이면서 (25로 나누어 떨어지지 않거나 16의 배수)
			// - 25로 나누어 떨어지지 않거나 : 100의 배수가 아님.
			// - 100의 로 떨어지는 or조건으로 16의 배수 : 즉 400의 배수
			(year & 3) == 0 &&
			((year % 25) != 0 || (year & 15) == 0)
			? 29
			: 28
		)
	);
}

정리

int lastDay(int year, int month)
{
	return month != 2 ? (30 | (month + (month > 7 ? 1 : 0))) :
	(
		(year & 3) == 0 &&  ((year % 25) != 0 || (year & 15) == 0) ? 29  : 28
	);
}

C언어 스타일로 하면 다음과 같이 작성할 수 있습니다.

int lastDay(int year, int month)
{
	return month != 2 ? (30 | (month + (month > 7))) :  ( !(year & 3) &&  (year % 25 || !(year & 15)) ? 29  : 28 );
}