(Python) 그 날짜가 몇 주째인지 계산하기

특정한 년,월,일을 입력 받았을 때 그 날짜가 해당 월의 몇 째 주에 속하는지를 알려주는 함수를 작성해보자. 예를 들어 2020년 4월 27일 월요일은 4월의 5주차에 해당하는 날이다. 2020년 5월 15일은 2주차에 해당하는 날이다. 27, 15일은 각각 7로 나누는 것만으로도 3주나 차이가 나는데, 정확한 계산방법은 무엇일까?

1주일은 7일이니 일자값을 7로 나눈 몫이 주의 차수에 해당한다고 생각하기 쉽지만 여기에는 함정이 하나 있다. 바로 1일의 요일에 의해서 첫째주가 달라지는 것이다. 매월의 1주가 목요일 이후인 경우에는 그 다음주가 해당 월의 첫째주가 된다. 그 외에도 몇 주차인지의 값은 언제 일요일을 지나느냐에 따라서 달라지기 때문에 간단한 덧셈이나 나눗셈으로는 계산하기 어려운 문제이다.

# 1일의 요일에 따라 1주가 언제 시작하는지가 나뉘게 된다.
# 기준은 목요일
              |
S   M   T   W | T   F   S
28  29  30  1 | 2   3   4    <-- 1주 
5   6   7   8 | 9   10  11


S   M   T   W | T   F   S
27  28  29  30| 31  1   2
3   4   5   6 | 7   8   9   <--- 1주

따라서 특정일의 주 차수를 구하기 위해서는 그 달의 1일이 무슨 요일인지를 확인해야 한다. 날짜를 datetime 타입으로 만든 후 weekday() 메소드를 사용하면 요일을 알 수 있다. 이 값은 월요일은 시작으로 하는 인덱스 값이므로 월요일이 0, 일요일이 6인 값이 된다.

먼저 간단하게 년,월,일을 입력받아서 날짜를 생성하는 함수를 작성하자.

from datetime import datetime, timedelta

def get_date(y, m, d):
  '''y: year(4 digits)
   m: month(2 digits)
   d: day(2 digits'''
  s = f'{y:04d}-{m:02d}-{d:02d}'
  return datetime.strptime(s, '%Y-%m-%d')

생성한 날짜에 대해 해당월의 1일은 replace() 메소드를 사용한다.

> d = get_date(2020, 4, 20)
> f = d.replace(day=1)
# datetime.datetime(2020, 4, 1, 0, 0)

1일이 목요일 이후인지 확인은 weekday() 값이 3~5에 있는지 보면 된다. 6인 경우는 또 첫주가 있는 주이다. 그렇다면 기준일을 일요일로 잡아야 할 것이다.

  1. 1일이 목, 금, 토요일인 경우 (weekday() in (3, 4, 5)) – 1주의 시작은 그 다음 일요일이므로 (6 – weekday()) 값을 더해주어 구한다.
  2. 1일이 월, 화, 수요일인 경우에 1주의 시작은 그 이전 일요일이므로 (weekday() + 1)을 빼서 구할 수 있다.
  3. 1일이 일요일인 경우에는 기준점이 1일이다.

이제 1주가 시작하는 첫 일요일과 해당 날짜 사이의 날짜 차이를 7로 나누고 1을 더하면 원하는 결과를 얻게 된다.

def get_week_no(y, m, d):
    target = get_date(y, m, d)
    firstday = target.replace(day=1)
    if firstday.weekday() == 6:
        origin = firstday
    elif firstday.weekday() < 3:
        origin = firstday - timedelta(days=firstday.weekday() + 1)
    else:
        origin = firstday + timedelta(days=6-firstday.weekday())
    return (target - origin).days // 7 + 1