(이미지 출처, CC0 license)
날짜와 요일의 관계
율리우스력에서 그레고리력으로의 전환
율리우스 카이사르가 도입한 율리우스력은 다음과 같은 규칙으로 날짜를 계산하였다.
- 평년은
일 이고, 4년마다 윤년 (366일)을 배치한다.
이는 굉장히 부정확한 날짜 계산법이었는데, 그 이유는 지구가 태양을 한 바퀴 돌아 제자리로 올 때까지 걸리는 시간이 약 일 이기 때문이다. 얼핏 보기에는 굉장히 작은 오차 같지만, 계산해보면 약 128년 마다 하루가 뒤쳐지게 된다.
카이사르는 기원전 1세기 사람이므로, 16세기 (약 1600년 뒤)에 들어서는 10일 정도가 원래의 날짜보다 뒤쳐지게 된다. 계속된 오차 누적을 감당할 수 없었던 교황청은 새로운 날짜 계산법을 고안해냈고, 교황 그레고리오 13세는 그 유명한 그레고리력을 제정하고 공포한다. 이는 다음과 같이 요약된다.
1582년 10월 4일 목요일 다음날을 1582년 10월 15일 금요일로 하여 오차를 한 번에 제거한다.
4의 배수인 해(년)를 윤년으로 한다. 단, 100의 배수 중 400으로 나누어 떨어지지 않는 해는 평년으로 한다.
즉 윤년을 정하는 조건을 2개 더 집어 넣어 강제로 오차를 제거한 것이다. 예를 들어서, 1700년은 100의 배수이지만 400으로 나누어 떨어지지 않기에 윤년이 아니라 평년이 된다. 마찬가지로 1800, 1900년 도 평년이지만, 2000년은 윤년이 된다.
날짜와 요일의 관계
율리우스력에서 날짜와 요일의 관계는 어떻게 될 까? 이는 다시말하면, 특정한 날짜가 주어지면 그 날짜의 요일을 대답해주는 관계식이 있냐고 묻는 것과 같다. 없을 이유가 없다...ㅋㅋ. 일단 이러한 관계식을 찾기 위해서는 요일을 문자그대로가 아닌 숫자로 인식해야 한다. 요일은 7일마다 반복되므로, 다음과 같이 인식하면 충분하다.
일요일 = 0, 월요일 = 1, 화요일 = 2, ..., 토요일 = 6
이렇게 인식하는 이유는, 0부터 6까지의 정수는 7로 나눈 나머지를 이루기 때문이다. 따라서 날짜
YYYY / MM / DD
가 주어지면, 요일수
0부터 6까지의 정수 중 하나인
를 출력하는 공식을 만들면 된다.
평년과 요일의 관계
먼저 YYYY 꼴의 년도를 앞 두자리와, 뒤 두자리로 나눈다. 예를 들어서, 2010년이면,
앞 두자리 20과 뒤 두자리 10 으로,
1908년이면
앞 두자리 19와 뒤 두자리 08(=8) 로 나누는 것이다.
이제 앞 두자리 수를 , 뒤 두자리수를
로 놓자. 평년은 365일 이고,
이므로, 년도가 하나 넘어갈때마다, 요일은 전년도 동일 날짜보다 하루 (나머지가 1)만큼 앞서간다. 따라서 일단
를 더하자.
윤년과 요일의 관계
4의 배수인 해는 원칙적으로 윤년이므로, 위 공식은 수정되야 한다. 어떤 수가 4의 배수일 조건은 뒤 두자리가 4의 배수일 조건과 동치이므로, 우리는 관계식에
을 더해주어야한다. 수정한 공식은 다음과 같다.
다음으로, 그레고리력에 추가된 100의 배수 조건을 살펴봐야 한다. 100의 배수인 해는 뒤의 두 자리가 00으로 끝나는 년도만 해당된다. 00으로 끝나는 해는 100년마다 돌아오고, 100년이 지날 때 마다 요일은 24번의 윤년 때문에
만큼 앞서간다. (100년전 동일 날짜에 비해) 요일이 124 만큼 앞서간다는 것은
2일 뒤쳐진다는 것과 동치이다 (-2에 주목). 100년을 앞서갔을때, 뒤의 두자리는 변하지 않으므로, 앞의 두 자리에 -2를 곱한 값을 더해
로 수정할 수 있다. 이제 400의 배수들을 생각해주자. 400의 배수인 해는 뒤 두자리는 00으로 고정되고, 앞 두자리가 4의 배수이어야 한다. 이들은 윤년이므로, 을 더해서
로 수정할 수 있다.
달과 요일의 관계
년도와 요일의 관계를 살펴봤으니, 달과 요일의 관계도 알아봐야 한다. 윤년은 2월달에 하루를 더하므로 편의상 3월을 년도의 시작으로 보고, 2월을 그 해의 마지막으로 보아, 달을 나타내는 숫자 을 다음과 같이 약속한다.
3월 = 1, 4월 = 2, 5월 = 3, ..., 12월 = 10, 1월 = 11, 2월 = 12
모든 달은 28일보다 크거나 같고, 28은 7의 배수여서 요일에 변화를 주지 않으므로, 28일보다 얼만큼 더 가지고 있는지 살펴보면 다음과 같다.
| m | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 달 | 3월 | 4월 | 5월 | 6월 | 7월 |
| 일 수 | 31 | 30 | 31 | 30 | 31 |
| 더 가진 일 수 | 3 | 2 | 3 | 2 | 3 |
| 누적 | 0 | 3 | 5 | 8 | 10 |
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
|---|---|---|---|---|---|---|
| 8월 | 9월 | 10월 | 11월 | 12월 | 1월 | 2월 |
| 31 | 30 | 31 | 30 | 31 | 31 | 28 |
| 3 | 2 | 3 | 2 | 3 | 3 | 0 |
| 13 | 16 | 18 | 21 | 23 | 26 | 29 |
누적 0, 3, 5, 8, 10, 13, 16, 18, 21, 23, 26, 29 를 각 달의 숫작 1,2,3,..., 12와 연관지어야 한다. 5달 마다 누적 13 만큼 늘어나므로, 이들을 연관짓는 관계식은
꼴일 것이다. 이제 상수 를 구해보자.
5개의 부등식을 모두 만족하는 의 범위는
로 좁혀진다. 편의상
로 놓자. 따라서 수정된 관계식은
이 된다.
하루와 요일의 관계
각 달 내에서, 하루하루가 지날때마다 당연히 요일도 앞당겨지므로, 일 수를 로 놓는 다면, 다음과 같이 수정해야 한다.
이제 마지막으로, 특정 값을 집어넣어서 준 식이 성립하게끔 조정해주면 끝난다. 오늘인 2018년 9월 5일은 수요일 이므로,
를 넣으면
이 나온다. 그런데 수요일은 숫자 3에 대응되므로, 관계식에 2를 더해야지만이 완성된다. 따라서,
이 완전한 관계식이다.
실제 계산
요약하면 YYYY / MM / DD 꼴의 년월일 정보로부터
: 일 수
: 달 (3월 = 1, 4월 = 2, ..., 1월 = 11, 2월 = 12)
,
: 각각 년도의 앞, 뒤 두자리 (만약 1월, 2월이라면 전년도를 대신 사용한다.)
를 계산하고, 이를 식
에 대입하면 요일을 알 수 있다.
- 2020년 2월 28일:
이고 2월이므로 전년도
년을 이용하여
를 계산한다.
이므로 그 날은 금 요일이다.
- 2018년 12월 25일: 올해 크리스마스는 무슨 요일일까?
이므로
즉 화 요일이다.
질문점
만약 연도가 5자리 이상 넘어가면 어떻게 되나요? 걱정할 필요가 없다. 그때에는 년도의 앞 3자리를 로 정의하면 충분하다. 그런데, 그레고리력도 3300년 마다 1일의 오차가 존재하므로, 연도가 5자리가 되기 전 어느 시점에서는 날짜 계산의 조정이 있어야 한다. 따라서, 날짜계산의 조정이 이루어지는 시점부터 다시 위의 계산을 반복하면 된다.
출처 / 인용
[1] Burton's Elementary Number Theory 6장
마지막으로 위의 식을 파이썬 코드로 짜보면 다음과 같다.
def date_of_week(Y, M, D):
# Find m ,c, d
if int(M) < 3:
m = int(M) + 10
d = int(str(int(Y) - 1)[-2:])
c = int(str(int(Y) - 1)[0:2])
else:
m = int(M) -2
d = int(Y[-2:])
c = int(Y[0:2])
# Find k
k = int(D)
L = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday']
w = (d + (d//4) + (c//4) + (13*m-1)//5 -2*c + k) % 7
return 'The date of the week is ' + L[w] + '!'
#---------------------
Y, M, D = input('Enter the date in YYYY/MM/DD form: ').split('/')
print (date_of_week(Y,M,D))