같은 주인지 확인하기

🕒 읽는 데 0분 예상
forest_articles
forest_날짜
forest_분류
문서
예를 들어, 아래와 같이 달력이 있다고 해보자.
notion image
3월 31일은 ‘3월 N주차’라 할 수 있고, 4월 1일은 ‘4월 1주차’라고 할 수 있을 것이다.
그런데 문제는 3월 31일과 4월 1일이 같은 주인지를 어떻게 확인할 수 있는지이다. 위에서 생성한 M월 N주차 형식만으로는 달이 넘어가는 시기에 대해서는 제대로 계산할 수 없다.
const getCurrentWeekLabel = (date: Date): string => { const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7)); const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); const weekNo = Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); return `${d.getUTCFullYear()}-W${String(weekNo).padStart(2, '0')}`; };
 
이 JavaScript 코드는 ISO 8601 표준에 따라 주어진 날짜의 주차(week number)를 계산하여 YYYY-W16 형태(이번 년도 16주차라는 뜻)의 문자열로 반환하는 함수를 정의한다. 여기서는 '월요일'을 한 주의 시작으로 간주한다. 코드를 단계별로 자세히 살펴보자.
1. 함수 정의:
const getCurrentWeekLabel = (date: Date): string => { ... };
  • getCurrentWeekLabel이라는 이름의 상수(const) 함수를 선언한다.
  • 이 함수는 Date 객체 형태의 날짜를 입력 인자(date)로 받는다. TypeScript 타입 정의에 따라 dateDate 타입이다.
  • 함수는 문자열(string)을 반환한다. TypeScript 타입 정의에 따라 함수의 반환 값은 string 타입이다.
2. UTC 기준 날짜 생성:
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
  • 입력된 날짜(date)를 기반으로 새로운 Date 객체 d를 생성한다.
  • Date.UTC() 메서드를 사용하여 날짜를 UTC(Coordinated Universal Time, 협정 세계시) 기준으로 생성한다. 이는 시간대 문제를 방지하고 일관된 결과를 얻기 위함이다.
  • date.getFullYear(), date.getMonth(), date.getDate() 메서드를 사용하여 입력 날짜의 년, 월, 일을 각각 가져온다.
3. ISO 8601 주차 기준일 설정 (목요일):
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7)); // 목요일로 이동
  • ISO 8601 표준은 해당 주의 목요일이 속하는 날짜를 기준으로 주차를 결정한다. 따라서 주어진 날짜 d를 해당 주의 목요일로 이동시킨다.
  • d.getUTCDay()는 UTC 기준 요일(0: 일요일, 1: 월요일, ..., 6: 토요일)을 반환한다.
  • (d.getUTCDay() || 7)d.getUTCDay()가 0(일요일)일 경우 7로 대체한다. JavaScript에서 || 연산자는 왼쪽 값이 falsy(null, undefined, 0, '', NaN, false)일 경우 오른쪽 값을 반환한다.
  • 4 - (d.getUTCDay() || 7)는 현재 날짜에서 목요일까지 며칠을 더해야 하는지 계산한다. 예를 들어, 현재 날짜가 월요일(1)이면 4 - 1 = 3이 되므로 3일을 더하여 목요일로 이동한다. 일요일(0)이면 4 - 7 = -3이 되므로 3일을 빼서 목요일로 이동한다.
  • d.setUTCDate() 메서드를 사용하여 d의 날짜를 조정한다.
4. 해당 연도의 시작일 설정:
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
  • 해당 연도(목요일 날짜 d의 연도)의 1월 1일을 UTC 기준으로 yearStart라는 Date 객체로 생성한다. 이 날짜는 주차 계산의 기준점이 된다.
  • d.getUTCFullYear() 메서드를 사용하여 d의 연도를 가져온다.
  • 월은 0부터 시작하므로 1월은 0으로 지정한다.
5. 주차 계산:
const weekNo = Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
  • 주차 번호(weekNo)를 계산한다.
  • d.getTime() - yearStart.getTime()은 목요일 날짜 d와 해당 연도의 시작일 yearStart 간의 밀리초 차이를 계산한다.
  • / 86400000는 밀리초를 일(day) 단위로 변환한다 (86400000 밀리초 = 1일).
  • + 1은 연도의 첫 날을 포함하기 위해 1을 더한다.
  • / 7은 일 수를 주 수로 나눈다.
  • Math.ceil()은 결과를 올림하여 주차 번호를 얻는다. ISO 8601 표준에서는 1월 1일이 있는 주부터 1주차로 시작하며, 4일 이상이 해당 연도에 속해야 그 주를 해당 연도의 첫 주로 간주한다.
6. 주차 레이블 반환:
return `${d.getUTCFullYear()}-W${String(weekNo).padStart(2, '0')}`;
  • YYYY-Www 형태의 주차 레이블 문자열을 생성하여 반환한다.
  • d.getUTCFullYear()는 해당 연도를 가져온다.
  • W는 연도와 주차 번호를 구분하는 문자열이다.
  • String(weekNo).padStart(2, '0')는 주차 번호(weekNo)를 문자열로 변환하고, 길이가 2자리가 안 될 경우 앞자리를 '0'으로 채운다. padStart(2, '0') 메서드는 문자열의 길이가 지정된 길이보다 짧을 경우, 문자열의 시작 부분에 다른 문자열을 반복하여 채워준다.
 
이제, 이렇게 생성된 Label이 같은 지를 비교하면, 두 날짜가 같은 주에 속하는 지를 확인할 수 있다.
물론, 외부 라이브러리를 사용하거나 위에서 Label을 생성하는 과정 없이 아이디어만 가져와서 직접 계산해주면 더 쉽거나 효율적으로 구현할 수 있을 것이다. 하지만 필자의 경우 특정 날짜가 몇 주차 인지까지 계산해야 했기 때문에 프로젝트에 쓰인 코드를 그대로 사용해 설명했다.

댓글 0