[Flutter] N월 N주차(몇주차) 구하기

CHOI·2022년 1월 14일
5

[Flutter] 기능

목록 보기
1/1
post-thumbnail

N월 N주차

프로젝트 진행 중 월 주차를 구해야 되는 기능이 있어서 시작했습니다.
보통 매월 1일이 있는 주를 1주로 계산하고 있었는데 월별 주차를 구하는 방법에도 국제 기준이 있다고 합니다.

국제 표준 ISO-8601에 따르면 매주의 시작일은 월요일이며, 매월의 첫 주는 4일 이상이 포함된 주를 기준으로 합니다.
쉽게 말해 계산의 기준은 목요일입니다.

1월 1일을 보면, 토요일입니다. 그렇다고 하면 1월 1일은 5주 차가 됩니다.
2월 1일은 화요일입니다. 2월 1일은 4일 이상을 포함하고 있기 때문에 1주 차입니다.

이제 1월 1일을 입력을 하면 5를 출력해 주는 기능을 만들겠습니다!


계산 방법

저는 우선 위에서 설명한 국제 표준 방법이 아닌 1일이 있는 주를 1주 차로 계산하고
1일의 요일에 따라 계산을 변형하는 방법을 이용했습니다.

전체적인 흐름

해당 날짜가 1일과 동일한 주인지 확인
-> true: 1일이 목요일보다 빠른지 확인
         -> true: 1주차
         -> false: 이전 달의 마지막날의 주차 계산
-> false: 해당 날짜가 마지막 날과 동일한 주인지 확인
          -> true: 해당 달의 마지막 날이 목요일이 느린지 확인
                   -> true: 단순 주차 계산 + (1일이 목요일보다 빠르면 0 / 느리면 -1)
                   -> false: 1주차
          -> false: 단순 주차 계산 + (1일이 목요일보다 빠르면 0 / 느리면 -1)

단순 계산

국제 표준 방식이 아닌 단순하게 1일이 있는 주를 1주 차로 계산하는 방식을 베이스로 깔고 간다고 말씀드렸는데, 이 방식을 1일과 월의 첫 번째 월요일을 비교하는 방법으로 메서드를 만들었습니다.

// 월 주차. (단순하게 1일이 1주차 시작).
int weekOfMonthForSimple(DateTime date) {
  // 월의 첫번째 날짜.
  DateTime _firstDay = DateTime(date.year, date.month, 1);

  // 월중에 첫번째 월요일인 날짜.
  DateTime _firstMonday = _firstDay.add(Duration(days: (DateTime.monday + 7 - _firstDay.weekday) % 7));

  // 첫번째 날짜와 첫번째 월요일인 날짜가 동일한지 판단.
  // 동일할 경우: 1, 동일하지 않은 경우: 2 를 마지막에 더한다.
  final bool isFirstDayMonday = _firstDay == _firstMonday;

  final _different = calculateDaysBetween(from: _firstMonday, to: date);

  // 주차 계산.
  int _weekOfMonth = (_different / 7 + (isFirstDayMonday ? 1 : 2)).toInt();
  return _weekOfMonth;
}

isFirstDayMonday 변수를 생성한 이유는 보통의 월 같은 경우는 (_different / 7 + 2)를 하면 정확한 결괏값이 나오지만
1일이 그 월의 첫 번째 월요일인 경우에는 (_different / 7 + 1) 을 해야지만 정확한 결괏값이 나오기 때문에 판단 후에 다른 값들을 더하기 위해서 생성했습니다.

단순 계산 메서드에서 calculateDaysBetween를 사용했는데 이것은 두 날짜의 d-day를 나타내는 메서드입니다.

d-day 계산

DateTime.difference()를 이용하면 리턴 값이 Duration으로 나오기 때문에 날짜를 return 해주는 메서드를 만들었습니다.

// D-Day 계산.
int calculateDaysBetween({required DateTime from, required DateTime to}) {
  return (to.difference(from).inHours / 24).round();
}

동일한 주 인지 계산

전체 계산하는 흐름에서 첫 번째로 판단하는 것이 동일한 주인지 판단하는 것입니다.
동일한 주를 판단하기 위해서 위에서 만든 단순 주차 계산 방식을 이용해서 리턴 값이 같으면 동일한 주로 판단하고 있습니다.

// 동일한 주차인지 확인.
bool isSameWeek(DateTime dateTime1, DateTime dateTime2) {
  final int _dateTime1WeekOfMonth = weekOfMonthForSimple(dateTime1);
  final int _dateTime2WeekOfMonth = weekOfMonthForSimple(dateTime2);
  return _dateTime1WeekOfMonth == _dateTime2WeekOfMonth;
}

종합

위에서 생성한 3가지의 메서드를 결합해서 표준 방식의 주차를 구하는 메서드를 만들 수 있습니다.
보기에는 복잡해 보이지만 달력을 보면서 if를 판단해 나가면 이해하기 쉬울 겁니다.

// 월 주차. (정식 규정에 따라서 계산)
int weekOfMonthForStandard(DateTime date) {
  // 월 주차.
  late int _weekOfMonth;

  // 선택한 월의 첫번째 날짜.
  final _firstDay = DateTime(date.year, date.month, 1);

  // 선택한 월의 마지막 날짜.
  final _lastDay = DateTime(date.year, date.month + 1, 0);

  // 첫번째 날짜가 목요일보다 작은지 판단.
  final _isFirstDayBeforeThursday = _firstDay.weekday <= DateTime.thursday;

  // 선택한 날짜와 첫번째 날짜가 같은 주에 위치하는지 판단.
  if (isSameWeek(date, _firstDay)) {
    // 첫번째 날짜가 목요일보다 작은지 판단.
    if (_isFirstDayBeforeThursday) {
      // 1주차.
      _weekOfMonth = 1;
    }

    // 저번달의 마지막 날짜의 주차와 동일.
    else {
      final _lastDayOfPreviousMonth = DateTime(date.year, date.month, 0);

      // n주차.
      _weekOfMonth = weekOfMonthForStandard(_lastDayOfPreviousMonth);
    }
  } else {
    // 선택한 날짜와 마지막 날짜가 같은 주에 위치하는지 판단.
    if (isSameWeek(date, _lastDay)) {
      // 마지막 날짜가 목요일보다 큰지 판단.
      final _isLastDayBeforeThursday = _lastDay.weekday >= DateTime.thursday;
      if (_isLastDayBeforeThursday) {
        // 주차를 단순 계산 후 첫번째 날짜의 위치에 따라서 0/-1 결합.
        // n주차.
        _weekOfMonth =
            weekOfMonthForSimple(date) + (_isFirstDayBeforeThursday ? 0 : -1);
      }

      // 다음달 첫번째 날짜의 주차와 동일.
      else {
        // 1주차.
        _weekOfMonth = 1;
      }
    }

    // 첫번째주와 마지막주가 아닌 날짜들.
    else {
      // 주차를 단순 계산 후 첫번째 날짜의 위치에 따라서 0/-1 결합.
      // n주차.
      _weekOfMonth =
          weekOfMonthForSimple(date) + (_isFirstDayBeforeThursday ? 0 : -1);
    }
  }

  return _weekOfMonth;
}

📌 어떻게 보면 메서드 결괏값을 정확하지만 보기 좋지 못한 메서드 일 수도 있습니다. 더 좋은 방법 혹은 궁금한 점이 있으시면 댓글 달아주세요!


Github
https://github.com/cyb9701/week_of_month

profile
Mobile App Developer

0개의 댓글