??? : 주차별 조회할 때, ISO 8601를 베이스로 삼으면서 시작 요일을 일요일로 만들어주세요.

탄이·2023년 8월 7일
1

기존 문제 상황

  • 주차별 조회 ( = 주간 조회)를 했을 때의 표기에 대한 정확한 기준이 없었다.
  • 기준이 없다보니 조회 방식에 따라 결과값이 다른, 즉 데이터 정합성의 문제가 있었다.

날짜와 시간을 다루는 부분이 의외로(?) 까다롭다는 얘길 들어서 도전해보고 싶었다.
자원해서 수정 작업에 들어갔다.

요구사항

  • ISO 8601에 따라, N월 1일이 목요일보다 이전 요일에 있으면 그 주는 N월의 1주차 이다.
    • 위 이미지에서 22주차는 6월 1주차이다. (5월 5주차가 아니다)
  • 하지만 대한민국의 달력은 주로 일요일부터 시작한다는 점에 따라, 한 주의 시작 요일은 일요일 이다.
    • ISO 8601은 월요일부터 시작이지만, 우리는 일요일을 시작 요일로 본다.

즉, 일요일부터 한 주를 시작하지만 특정 월의 첫 주를 결정짓는 것은 1일이 일 ~ 목 중에 있는지 여부이다.


+) ISO 8601과 월요일 시작의 차이

ISO 8601 국제 표준법

  • 주수는 목요일을 카운트 해서 기술한다.
  • 한 주의 시작 요일은 월요일이다.
  • 월요일 ~ 일요일까지 중에서 1일이 목요일보다 이전 요일 (월 ~ 목)에 있으면, 한 주의 과반 이상이 N월에 속하기 때문에 N월 첫번째 주로 본다.
    - 즉, 6월 1일이 목요일이면 한 주 중 총 4일(목,금,토,일)이 6월이므로 6월의 1주차로 보는 것

미국식 표기법

  • 한 주의 시작은 일요일이다.
  • 첫 째주로 보기 위해서는 최소 하루만 있어도 된다.
    • 월보다는 Year로 바꿔서 생각하면 쉽다.
    • 2023년 1월 1일이 무슨 요일에 있던, 그 주는 2023년 1월 1주차로 카운트 한다.

해결 방법

description

  • 주차별 조회할 때, WeekFields를 우리 기준에 맞게 재정의 했다.
case WEEKLY:
	LocalDateTime dateTime = getWeekStartDay(dateDescription);
    // 한 주의 시작일을 일요일로 설정
    // 1일이 일 ~ 목요일 일 때 1일이 포함된 월의 첫 주로 설정하기 위해 최소 필요한 일수는 3일로 설정함
    TemporalField weekOfMonth = WeekFields.of(DayOfWeek.SUNDAY, 3).weekOfMonth();

	LocalDateTime thursday = dateTime.plusDays(4);
    int dayOfMonth = thursday.getDayOfMonth();
    int year = thursday.getYear();
    formatter = DateTimeFormatter.ofPattern("MM월 ");
    String thursdayMonth = thursday.format(formatter);

	int week;
    if(dayOfMonth < 6) {
    	week = 1;
    } else {
    	week = thursday.get(weekOfMonth);
    }

	return new StringBuilder().append(year)
    	.append("년 ")
        .append(thursdayMonth)
        .append(week)
        .append("주차")
        .toString();

+) 참고로 ISO 8601은 WeekFields.of(DayOfWeek.MONDAY, 4) 이고 미국식은 WeekFields.of(DayOfWeek.SUNDAY, 1)이었다.

startDate, endDate 추출

  • 프론트에서는 해당 연도의 주차를 String으로 넘기는 중이었다. (ex. 202322)
  • 주차에서 startDate와 endDate를 아래 메소드를 통해서 뽑아 냈다.
// KST 시작일자 뽑기
public static LocalDateTime getWeekStartDay(String yyyyuu) {
	int year = Integer.parseInt(yyyyuu.substring(0,4));
    // 그 해의 몇 일째인지 (2023년 56일째 || 2023년 300일째)
    int day = Integer.parseInt(yyyyuu.substring(4)) == 0 ? 1 : Integer.parseInt(yyyyuu.substring(4)) * 7;
    LocalDate ld = LocalDate.ofYearDay(year, day);
    // 월요일 == 1, 일요일 == 7
    int turn = ld.getDayOfWeek().getValue() == 7 ? 0 : ld.getDayOfWeek().getValue();
    // DayofWeek는 월요일이 한 주의 시작이므로 뒤집어 줌(일요일을 시작일이 되게)
    return ld.minusDays(turn).atTime(0,0,0);
}

// KST 종료일자 뽑기
public static LocalDateTime getWeekEndDay(String yyyyuu) {
	int year = Integer.parseInt(yyyyuu.substring(0,4));
    int day = Integer.parseInt(yyyyuu.substring(4)) == 0 ? 1 : Integer.parseInt(yyyyuu.substring(4)) * 7;
    LocalDate ld = LocalDate.ofYearDay(year, day);
    int turn = ld.getDayOfWeek().getValue() == 7 ? 0 : ld.getDayOfWeek().getValue();
    // 종료일자는 토요일이 되도록 뽑아야 하므로 plus 해줌
    return ld.plusDays(6-turn).atTime(23,59, 59);
}
  • 202322 (2023년 22주차)를 startDate Param으로 받았을 때
    • LocalDate ld = 2023-05-29 (월)
      -> ld.minusDays(1).atTime(0,0,0) == 2023 05 28 00:00:00
  • 202322 (2023년 22주차)를 endDate Param으로 받았을 때
    • LocalDate ld = 2023-05-29 (월)
      -> ld.plusDate(6-1).atTime(23,59,59) == 2023 06 03 23:59:59

우리는 ES에서 조회해오고, 네트워크 이슈 등으로 ES 조회 실패할 때만 DB에서 쿼리하도록 돼 있다.
ES에서 조회할 때 시작일/종료일에 맞게 결과가 안 나오는 상태였다.

Date histogram aggregation이 Calendar intervals week (1w)로 설정돼 있었고,
ES는 별도 설정이 없다면 월요일이 한 주의 시작이었기 때문에 start,endDate는 의미가 없었다.

해결 해야할 이슈

  • 지정한 startDate, endDate에 맞게 주 단위의 데이터를 조회해오기
  • 한 주의 시작은 일요일이고, 종료 요일은 토요일일 것

해결 방법

다만 offSet은 주차별 조회할 때만 적용돼야 하기 때문에 아래처럼 분기문을 태웠다.

private String getOffSetData(StatisticsDateType dateType) {
	// ES 조회 시, 일요일을 주의 시작점으로 잡기 위해 설정
    if(StatisticsDateType.WEEKLY.equals(dateType)) {
    	return DateHistogramInterval.days(-1).toString();
    } else {
    	return "0";
    }
}

offSet을 적용하기 전에 calendar intervals 를 fixed intervals로 변경해보기도 하고, 주 단위를 -7d로 검색하도록 해보기도 했으나 방법이 아니었다.
+) fixed intervals 관련 공식 문서 : https://www.elastic.co/guide/en/elasticsearch/reference/7.15/search-aggregations-bucket-datehistogram-aggregation.html#fixed_intervals

ES에서 Date histogram aggregation 포함 aggregation 쪽도 공부해볼 부분이 많은 곳이라 기회가 된다면 포스팅 해보려고 한다.

배포가 나간 뒤..

2022년 12월 마지막 주로도 검색해보고, 과거 일자로 조회 해보고 다양하게 조회해봐도 이슈 없이 현재까지 정상 조회 돼서 뿌듯했다.

날짜, 특히 주차별 조회 이슈를 해결하면서 공식 문서들을 뒤져보고 공부한 게 든든한 자산이 될 것 같다.

profile
백엔드 개발자의 로그

0개의 댓글