감 잃지말고 개발하기

[JS] Date 객체를 이용한 특정 날짜의 주 구하기 본문

프론트엔드/JS

[JS] Date 객체를 이용한 특정 날짜의 주 구하기

persii 2023. 7. 12. 17:35

코드 목표

Date 객체를 이용해 특정 날짜의 주를 구할 수 있다.

 

1주의 첫날 및 첫 번째 주(週)에 대한 정의

코드를 작성하기 앞서, 특정 달의 주를 계산하기 위해 필요한 정의를 살펴보자. 

 

월요일 : 1주의 첫날

국제표준화기구인 ISO에서 제정한 국제표준 ISO 8601 (날짜 및 시각의 표기)에 따르면, 월요일을 한 주의 첫 번째 날로 규정하고 있다.

때문에 우리나라 국가표준(KS) 중 하나인 '데이터 요소 및 교환 포맷-정보교환-날짜 및 시각의 표기(분류번호 : KS X ISO 8601)'에서 역시 ISO의 규정에 따라 월요일을 1주의 첫날로 규정하고 있다. 

 

주 구분 기준

[KS X ISO 8601]에 따르면, 일 년의 첫 주는 일 년의 첫 번째 목요일을 포함하는 주로 정의하고 있다.

 

 

접근 방법

구해야 하는 것은 '주어진 날짜가 해당 월의 몇 번째 주에 위치하는가'  것이다.

여기서 말한 주란, 위의 주 구분 기분에서 설명한 것처럼 목요일을 포함하는 주를 말한다. 

이에 따라

1일이 속한 주가 첫 번째 주가 되기 위해선 1일이 월 ~ 목요일 중에 있어야 하며,

마지막 날이 속한 주가 마지막 주가 되기 위해선 마지막 날이 목 ~ 일요일 중에 있어야 한다.

 

2023년 6월의 경우, 

1일은 목요일, 30일은 금요일이므로 각각의 주는 6월의 첫 주와 마지막 주가 된다.

하지만 2023년 7월의 경우,

1일은 토요일, 31일은 월요일이므로 각각의 주는 6월의 마지막 주와 8월의 첫 주가 된다.

 

그렇다면, 1일이 속한 주와 월의 마지막 날이 속한 주의 모습을 몇 가지 경우로 나누어 특정 날짜의 주를 구해보자.

구하고자 하는 날짜는 23일이다.

경우 1. 1일이 속한 주가 첫 주인 경우

즉, 1일이 월 ~ 목요일 사이에 있는 경우이다.

이 같은 경우, 1일이 월요일인지 아닌지에 따라 다시 구분할 수 있다.

월요일을 기준으로 한 이유는 월요일이 한 주의 시작이기 때문이다.

(1) 1일이 월요일인 경우

1일이 속한 주가 첫 주가 되면서 월요일인 경우이다.

보통 이 경우, 2023년 5월처럼 마지막 날이 속한 주는 다음 달의 첫 주가 된다.

하지만 2021년 2월처럼 마지막 날이 속한 주가 해당 월의 마지막 주가 되는 경우도 있다.

경우 1-1

 

  [1] 마지막 날이 속한 주가 다음 달의 첫 주가 되는 경우 (Ex. 2023년 5월)

  • 첫 번째 월요일인 1일이 속한 주가 첫 번째 주가 된다.
  • 1일을 기준으로 23일은 3주 후에 있다.
  • 5월 23일은 5월 4번째 주에 있다.

  [2] 마지막 날이 속한 주가 해당 월의 마지막 주가 되는 경우 (Ex. 2021년 2월)

  • 첫 번째 월요일인 1일이 속한 주가 첫 번째 주가 된다.
  • 1일을 기준으로 23일은 3주 후에 있다.
  • 2월 23일은 2월 4번째 주에 있다.

 

(2) 1일이 월요일이 아닌 경우

1일이 속한 주가 첫 주가 되지만 1일이 월요일이 아닌 경우이다.

보통 이 경우, 2023년 6월처럼 마지막 날이 속한 주는 해당 월의 마지막 주가 된다.

하지만 2023년 2월처럼 마지막 날이 속한 주가 다음 달의 첫 주가 되는 경우도 있다.

경우 1-2

 

  [1] 마지막 날이 속한 주가 다음 달의 첫 주가 되는 경우 (Ex. 2023년 2월)

  • 첫 번째 월요일인 6일이 속한 주가 두 번째 주가 된다.
  • 6일을 기준으로 23일은 2주 후에 있다.
  • 2월 23일은 2월 4번째 주에 있다.

  [2] 마지막 날이 속한 주가 해당 월의 마지막 주가 되는 경우 (Ex. 2023년 6월)

  • 첫 번째 월요일인 5일이 속한 주가 두 번째 주가 된다.
  • 5일을 기준으로 23일은 2주 후에 있다.
  • 6월 23일은 6월 4번째 주에 있다.

 

경우 2. 1일이 속한 주가 첫 주가 아닌 경우

즉, 1일이 금 ~ 일요일 사이에 있는 경우이다.

보통 이 경우, 2023년 7월처럼 마지막 날이 속한 주는 다음 달의 첫 주가 된다.

하지만 2022년 4월처럼 마지막 날이 속한 주가 해당 월의 마지막 주가 되는 경우도 있다.

경우 2

 

  [1] 마지막 날이 속한 주가 다음 달의 첫 주가 되는 경우 (Ex. 2023년 7월)

  • 첫 번째 월요일인 3일이 속한 주가 첫 번째 주가 된다.
  • 3일을 기준으로 23일은 2주 후에 있다.
  • 7월 23일은 7월 3번째 주에 있다.

  [2] 마지막 날이 속한 주가 해당 월의 마지막 주가 되는 경우 (Ex. 2022년 4월)

  • 첫 번째 월요일인 4일이 속한 주가 첫 번째 주가 된다.
  • 4일을 기준으로 23일은 2주 후에 있다.
  • 4월 23일은 4월 3번째 주에 있다.

 

 

코드 작성

위 경우의 수를 코드로 작성해 보자.

1. 변수 설정

먼저, 필요한 변수명과 저장할 값을 정의한다.

 

☞ 구하고자 하는 특정 날짜와 관련해 저장할 값

  • year : 특정 날짜의 해당 연도
  • month : 특정 날짜의 해당 월
  • today : 특정 날짜

☞ 1일이 속한 주와 관련해 저장할 값

  • firstDate : 해당 월의 1일
  • firstDay : 1일의 요일
  • firstMondayOfMonth : 해당 월의 첫 월요일 날짜

☞ 마지막 날짜가 속한 주와 관련해 저장할 값

  • lastDate : 해당 월의 마지막 날짜
  • lastDay : 마지막 날짜의 요일
  • lastMondayOfMonth:  해당 월의 마지막 월요일 날짜

☞ 주와 관련해 저장할 값

  • weekOfToday : 특정 날짜가 속한 주

 

2. 1일이 속한 주 및 마지막 날이 속한 주에서 필요한 값 추출

☞ Date 객체

  • Date 객체에서의 월(Month) : JS에서 Date 객체의 월은 0부터 11까지의 값을 가지는데, 0은 1월을, 11은 12월을 나타낸다.
  • Date 객체에서의 요일(Day) : JS에서 Date 객체의 요일은 일요일부터 시작하여 0부터 6까지의 값을 가지는데, 0은 일요일, 1은 월요일, 6은 토요일을 나타낸다.
  • new Date(year, month, 0) : year년 (month+1)월의 0번째 날, month달의 마지막 날을 나타내는 Date 객체를 생성한다.
const date = new Date('2023-07-23');
const year = date.getFullYear();
const month = date.getMonth()+1;
const today = date.getDate();

// 1일이 속한 주
const firstDate = new Date(year, month-1, 1);
const firstDay = firstDate.getDay() === 0 ? 7 : firstDate.getDay();
const firstMondayOfMonth = firstDay === 1 ? 1 : firstDate.getDate() + (7-firstDay+1);

// 마지막 날짜가 속한 주
const lastDate = new Date(year, month, 0).getDate();
const lastDay = 
	new Date(year, month-1, lastDate).getDay() === 0 ? 7 : new Date(year, month-1, lastDate).getDay();
const lastMondayOfMonth = lastDate-lastDay+1;

console.log(`오늘은 ${month}월 ${today}일 입니다.`);
console.log(`${month}월의 첫 번째 날짜는 ${firstDate.getDay()}일이며, 요일은 ${firstDay}입니다.`);
console.log(`${month}월의 첫 월요일은 ${firstMondayOfMonth}일 입니다.`);
console.log(`${month}월의 마지막 날짜는 ${lastDate}일이며, 요일은 ${lastDay}입니다.`);
console.log(`${month}월의 마지막 월요일은 ${lastMondayOfMonth}일 입니다.`);

 

☞ const month = date.getMonth()+1;

  • getMonth() : Date 객체에서 실제 월에 대응하는 월 값(0 ~ 11)을 가져온다.
  • 위 코드에서 date.getMonth()은 7월에 해당하는 월 값인 6을 반환하기 때문에 1을 더해준다.
  • 변수 month에는 7이 할당된다.

const firstMondayOfMonth = firstDay === 1 ? 1 : firstDate.getDate() + (7-firstDay+1);

  • 해당 월의 첫 월요일 날짜가 저장된다.
  • firstDay가 1(월요일)인 경우엔 1(일)이, 1(월요일)이 아닌 경우엔 첫 번째 월요일 날짜가 저장되도록 한다.

☞ const lastDate = new Date(year, month, 0).getDate();

  • 위 코드는 특정 연도와 월에 대한 마지막 날짜를 가져온다.
  • month 변수에 7이 저장되어 있으므로 월 값인 7에 대응하는 8월의 0번째 날, 즉 7월의 마지막 날짜가 저장된다.

☞ const lastDay =

    new Date(year, month-1, lastDate).getDay() === 0 ? 7 : new Date(year, month-1,  lastDate).getDay();

  • 날짜의 요일을 계산할 때 계산의 편리를 위해 일요일인 경우 7이 변수에 할당되도록 한다.

const lastMondayOfMonth = lastDate - lastDay + 1;

  • 해당 월의 마지막 월요일 날짜가 저장된다.

콘솔 확인


3. 경우의 수에 따른 특정 날짜의 주 계산

let weekOfToday = Math.floor((today-firstMondayOfMonth) / 7);

if (firstDay <= 4) {
	// 1일이 월~목요일에 존재 : 1일 있는 주가 첫 주인 경우
	if(firstMondayOfMonth == 1) {
  		// 1일이 속한 주가 첫 주면서 첫 번째 월요일인 경우
  		console.log("경우 1-(1)");
  		weekOfToday += 1;
	} else {
  		// 1일이 속한 주가 첫 주이지만 1일이 첫 번째 월요일이 아닌 경우 
        	// 첫 번째 월요일이 속한 주가 두 번째 주가 된다.
  		console.log("경우 1-(2)");
  		weekOfToday += 2;
	}
} else {
	// 1일이 금~일요일에 존재 : 1일 있는 주가 해당 월의 주가 되지 못하는 경우 
   	// 첫 월요일이 있는 주가 첫 주가 된다.
	console.log("경우 2");
	weekOfToday += 1;
}

if(lastDay < 4) {
	// 마지막 날이 속한 주가 다음 달의 첫 주가 되는 경우
	console.log("경우 [1]");
	if(lastMondayOfMonth <= today) {
    		// 구하고자 하는 특정 날짜가 해당 월의 마지막 월요일 날짜 이후에 있는 경우 
		weekOfToday = 7;
	}    
} else console.log("경우 [2]");

console.log(`weekOfToday : ${weekOfToday}`);

 

☞  let weekOfToday = Math.floor((today-firstMondayOfMonth) / 7);

  • 구하고자 하는 특정 날짜가 첫 월요일을 기준으로 몇 주 후에 있는지를 구한다.
  • Math 객체의 floor() 함수 : 소수점 이하를 버리고 가장 가까운 작은 정수로 내림한다.
  • 만약 1일이 속한 주가 첫 주가 아닌 경우(경우 2), 변수 weekOfToday에는 -1이 할당된다.

 if (firstDay <= 4) { }

  • 1일이 속한 주가 해당 월의 첫 번째 주인 경우(경우 1)이다.
  • 여기서 다시 두 가지 경우로 나뉜다. 
  • 1일이 월요일인 경우(경우 (1)), 1일이 첫 번째 월요일이 되므로 weekOfToday에 1을 더한다.
  • 만약 1일이 월요일이 아닌 경우(경우(2)), firstMondayOfMonth의 날짜가 첫 번째 월요일이자 두 번째 주의 시작이 되므로 weekOfToday에 2를 더한다.

  else { }

  • 1일이 속한 주가 해당 월의 주가 되지 못하는 경우(경우 2)이므로 weekOfToday에 1을 더한다.

☞  if(lastDay < 4) { }

  • 마지막 날이 속한 주가 다음 달의 첫 주가 되는 경우(경우 [1])이다.

콘솔 확인


4. weekOfToday 처리

switch(weekOfToday) {
case 0:
  console.log(`${month}월의 ${today}일은 ${month-1}월 마지막 주에 있습니다.`);
  break;
case 7:
  console.log(`${month}월의 ${today}일은 ${month+1}월 첫 주에 있습니다.`);
  break;
default:
  console.log(`${month}월의 ${today}일은 ${weekOfToday}번째 주에 있습니다.`);
}

콘솔 확인


 

 

코드 확인

이렇게 주어진 날짜가 속한 주를 구하기 위한 코드가 완성되었다.

위에서 코드를 작성하면서 경우 2에 대한 확인을 해봤으니 다른 경우 역시 확인해 보자.

경우 1. 1일이 속한 주가 첫 주인 경우

(1) 1일이 월요일인 경우

[1] 마지막 날이 속한 주가 다음 달의 첫 주가 되는 경우 (Ex. 2023년 5월 30일)

 

[2] 마지막 날이 속한 주가 해당 월의 마지막 주가 되는 경우 (Ex. 2021년 2월 27일)

 

(2) 1일이 월요일이 아닌 경우

[1] 마지막 날이 속한 주가 다음 달의 첫 주가 되는 경우 (Ex. 2023년 2월 26일)

 

[2] 마지막 날이 속한 주가 해당 월의 마지막 주가 되는 경우 (Ex. 2023년 6월 16일)

 

경우 2. 1일이 속한 주가 첫 주가 아닌 경우

[2] 마지막 날이 속한 주가 해당 월의 마지막 주가 되는 경우 (Ex. 2022년 4월 2일)

 

 

전체 코드

function getWeekOfMonth(date) {
  
  const year = date.getFullYear();
  const month = date.getMonth()+1;
  const today = date.getDate();
  
  // 1. 해당 월의 1일과 마지막 일의 요일 및 날짜 계산
  const firstDate = new Date(year, month-1, 1);
  const firstDay = firstDate.getDay() === 0 ? 7 : firstDate.getDay();
  const firstMondayOfMonth = firstDay === 1 ? 1 : firstDate.getDate() + (7-firstDay+1);  // 첫 월요일 날짜
  
  const lastDate = new Date(year, month, 0).getDate();
  const lastDay = new Date(year, month-1, lastDate).getDay() === 0 ? 7 : new Date(year, month-1, lastDate).getDay();
  const lastMondayOfMonth = lastDate-lastDay+1;

  // 2. 경우의 수에 따른 특정 날짜의 주 계산
  let weekOfToday = Math.floor((today-firstMondayOfMonth) / 7); // 1일이 속한 주가 첫 주가 아닌 경우 -1이 할당됨
  
  if (firstDay <= 4) {
    // 1일이 월~목요일에 존재 : 1일 있는 주가 첫 주인 경우
    if(firstMondayOfMonth == 1) {
      // 1일이 속한 주가 첫 주면서 첫 번째 월요일인 경우
      console.log("경우 1-(1)");
      weekOfToday += 1;
    } else {
      // 1일이 속한 주가 첫 주이지만 1일이 첫 번째 월요일이 아닌 경우 : 첫 번째 월요일이 속한 주가 두 번째 주가 된다.
      console.log("경우 1-(2)");
      weekOfToday += 2;
    }
  } else {
    // 1일이 금~일요일에 존재 : 1일 있는 주가 해당 월의 주가 되지 못하는 경우 : 첫 월요일이 있는 주가 첫 주가 된다.
    console.log("경우 2");
    weekOfToday += 1;
  }
  
  if(lastDay < 4) {
    // 마지막 날이 속한 주가 다음 달의 첫 주가 되는 경우
    console.log("경우 [1]");
    if(lastMondayOfMonth <= today) {
      weekOfToday = 7;
    }    
  } else console.log("경우 [2]");

  console.log(`weekOfToday : ${weekOfToday}`);
  
  // 3. weekOfToday 처리
  switch(weekOfToday) {
    case 0:
      console.log(`${month}월의 ${today}일은 ${month-1}월 마지막 주에 있습니다.`);
      break;
    case 7:
      console.log(`${month}월의 ${today}일은 ${month+1}월 첫 주에 있습니다.`);
      break;
    default:
      console.log(`${month}월의 ${today}일은 ${weekOfToday}번째 주에 있습니다.`);
  }
}

getWeekOfMonth(new Date('2022-04-02'));

 

 


 

 

Date 객체를 이용한 특정 날짜의 주 구하기 끝.