오늘은 몇 주차

오늘은 몇 주차

'주'란 무엇인가?

란 과연 무엇일까요? 주에 대해 자세히 생각해보면 이런 저런 의문이 듭니다.

  • 한 주의 시작은 월요일일까, 일요일일까?
  • 365.25일을 7로 나누면 약 52.178이다. 즉 1년은 평균 52.178주이다?
  • 올해(2022년)의 첫날은 토요일이다. 그렇다면 올해의 첫주는 1월 1일을 포함할까?

세상에는 혼돈을 줄이기 위하여 별에 별 것들에 대해 표준이 있습니다. 날짜와 시간과 관련된 데이터에 대한 국제 표준으로 ISO 8601이 있으며, 이에 대응하는 국내 표준으로 KS X ISO8601이 있습니다. 표준이력사항을 읽어보면 국내산업에서 개정의 요구가 없어 국제 표준과 동일한 내용으로 국내에 적용하고 있는 것을 확인할 수 있습니다.

해당 표준 내용을 정리하면 아래와 같습니다.

  • 한 주는 7일로 이루어져있다.
  • 한 주는 월요일로 시작하여 일요일로 끝난다.
  • 1년은 52주 또는 53주이다. (즉 364일 또는 371일이다.)
  • 1년의 첫번째 주는 해당 해의 첫번째 목요일이 포함된 주이다.
  • 1년의 마지막 주는 해당 해의 마지막 목요일이 포함된 주이다.
  • 주를 표현할 때에는 2018-W12 혹은 날짜를 포함하여 2018-W12-4처럼 한다. 이 경우 2018년의 13번째 주의 4번째 날이므로 2018년 03월 22일이 된다.

요약하면, 한 주의 시작은 월요일, 끝은 일요일이지만, 주차에 대한 판단 기준은 목요일에 있습니다.

예시를 들어보면 아래와 같다. 아래처럼 첫해의 첫 목요일이 포함된 주가 그 해의 첫번째 주가 됩니다.

WeekMonTueWedThuFriSatSun
W0101020304050607

만약 첫 목요일이 아래와 같이 되어 있다면 해당 연도의 1월 1일부터 1월 3일은 그 해의 첫주가 아니라, 그 전 해의 마지막 주가 됩니다.

WeekMonTueWedThuFriSatSun
W53010203
W0104050607080910

Week Number 계산하기

주차를 직접 계산해봅시다. 깔끔하게 week_number(date: Date) -> i32 같은 함수가 나오는 것이 목표입니다. 주차란 올해의 첫번째 주로부터 몇 주가 지났는지를 의미합니다. 오늘이 올해의 몇 번째 날인지만 알면 7로 나누어 알아낼 수 있습니다. 오늘이 올해의 몇번째 날인지를 ordinal(date)\text{ordinal}(\text{date})라고 하겠습니다. 이는 컴퓨터로는 오늘에서 1월 1일을 빼면 알 수 있고, 손으로는 오늘 날짜와 지나간 월들의 날을 모두 합하여 알아낼 수 있습니다.

가장 간단하게 1월 1일이 월요일인 해를 가정해봅시다. 다음은 해당 해의 첫주이므로 week(1)\text{week}(1)부터 week(7)\text{week}(7)까지의 값이 1로 같아야합니다.

WeekMonTueWedThuFriSatSun
W0101020304050607

다음과 같이 6을 더하고 버림 함수를 사용하면 이 구간에서는 의도한 대로 나옵니다.

week(date)=ordinal(date)+67\text{week}(\text{date}) = \left\lfloor\frac{\text{ordinal}(\text{date}) + 6}{7} \right\rfloor

다른 예시를 살펴봅시다. 1일이 작년으로 넘어가면 올바른 결과가 나오지 않습니다. 해가 금요일부터 시작하여 1월 1일이 W01이 아닌 예를 봅시다.

WeekMonTueWedThuFriSatSun
W53010203
W0104050607080910

week(1)=52\text{week}(1)=52, week(4)=1\text{week}(4)=1이 되어야 하지만, 위의 식을 적용하면, 둘 다 1이 되어버립니다.

주의 시작이 월~목일 때와 이를 넘어갈 때를 구분할 방법이 필요하다. 오늘 날짜에서 요일을 빼면 가능할 것 같다. ISO 8601에 요일을 수로 표현할 때는 월요일을 1로, 일요일을 7로 하여 나열한다고 나와있다. (이를 week day라고 한다.) 이렇게 하면 같은 주의 ordinal(date)weekday(date)\text{ordinal}(\text{date}) - \text{weekday}(\text{date})의 값은 일정해진다.

값을 명확히 보기 위해 바닥함수는 제외하고 wN = (date) => (ordinal(date) - weekday(date) + 6) / 7이라고 하자. 1월 1일이 월요일일 때와 금요일일 때의 wN값을 확인해보자

WeekMonTueWedThuFriSatSun
요일01020304050607
W0101020304050607
wN0.860.860.860.860.860.860.86
W53010203
wN0.290.290.29
W0104050607080910
wN1.291.291.291.291.291.291.29

wN이 1보다 작은 경우는 전년도의 마지막 주로 처리하면 깔끔할 것 같다. 그런데 1월 1일이 월요일일 경우 wN이 1.0을 넘지 않는다. 위에서 주었던 보정값 +6 대신 다른 상수를 더해서 해결해보자.

\left\lfloor\frac{\text{ordinal}(\text{date}) - \text{weekday}(\text{date}) + C}{7} \right\rfloor$$ - 1월 1일이 **월요일**이라면 이날은 **첫번째 주**이므로 $1-1+C$가 구간 $[7, 14)$ 안에 있어야 한다. - 1월 1일이 화요일이라면 이날은 첫번째 주이므로 $1-2+C$가 구간 $[7, 14)$ 안에 있어야 한다. - 1월 1일이 수요일이라면 이날은 첫번째 주이므로 $1-3+C$가 구간 $[7, 14)$ 안에 있어야 한다. - 1월 1일이 목요일이라면 이날은 첫번째 주이므로 $1-4+C$가 구간 $[7, 14)$ 안에 있어야 한다. - 1월 1일이 **금요일**이라면 이날은 **마지막 주**이므로 $1-5+C$가 구간 $[0, 7)$ 안에 있어야 한다. - 1월 1일이 토요일이라면 이날은 마지막 주이므로 $1-6+C$가 구간 $[0, 7)$ 안에 있어야 한다. - 1월 1일이 일요일이라면 이날은 마지막 주이므로 $1-7+C$가 구간 $[0, 7)$ 안에 있어야 한다. 위의 조건에 만족하려면 $C=10$이여야한다. 상수가 바뀌었으니 새로운 보정값에 맞춰 `wN`을 계산해보자. 이때 `wN = (date) => (ordinal(date) - weekday(date) + 10) / 7`이다. | Week | Mon | Tue | Wed | *Thu* | Fri | Sat | Sun | | ------- | ------ | ------ | ------ | ------ | ------ | ------ | ------ | | **W53** | 28 | 29 | 30 | **31** | 01 | 02 | 03 | | `wN` | `53` | `53` | `53` | `53` | `0.86` | `0.86` | `0.86` | | **W01** | 04 | 05 | 06 | **07** | 08 | 09 | 10 | | `wN` | `1.86` | `1.86` | `1.86` | `1.86` | `1.86` | `1.86` | `1.86` | 따라서 오늘의 날짜를 알고 있다면 위의 공식으로 오늘의 *주수*(week number)를 알 수 있습니다.