요일을 숫자로 표현하려면?
요일을 숫자로 표현하려면 어떻게 해야 할까? 월요일부터 일요일까지 일곱 개가 있고, 적당히 0부터 6까지 혹은 1부터 7까지 붙이면 된다. 참 쉽죠? 그런데 문제는 "적당히"가 사람마다 다르다는 것이다.
누군가에게 한 주는 월요일에 시작한다. 누군가에게 한 주는 일요일에 시작한다. 누군가에게 일요일은 주말의 끝이고, 누군가에게는 한 주의 시작이다. 문화에 따라 관습에 따라 습관에 따라 한 주는 월요일에 시작할수도 일요일에 시작할수도 있다.
이것에 대한 표준이 있을까? ISO-8601에서는 calendar week가 월요일에 시작해서 일요일에 끝난다고 정의한다.1
3.1.2.12
calendar day of week
day amongst the sequence of week calendar (3.1.1.23) days, namely, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday or Sunday
Note 1 to entry: The week calendar is defined in 4.2.2.
3.1.2.16
calendar week
time scale unit (3.1.1.7) of seven calendar days (3.1.2.11) which begins on Monday and ends on Sunday, according to the week calendar (3.1.1.23)
그래서 요일 숫자도 월요일이 1, 화요일이 2, ..., 일요일이 7이다.
Monday 1
Tuesday 2
Wednesday 3
Thursday 4
Friday 5
Saturday 6
Sunday 7
여기서 일주일은 월요일부터 시작한다. 근데, 1부터 시작한다. 그럼 0은?
리눅스의 date(1) 명령어만 봐도 두 가지 방식이 같이 있다.2
FORMAT controls the output. Interpreted sequences are:
%u day of week (1..7); 1 is Monday
%w day of week (0..6); 0 is Sunday
%u는 ISO-8601 방식이다. 월요일이 1이고 일요일이 7이다. 반면 %w는 일요일이 0이고 토요일이 6이다.
둘 다 틀린 것은 아니다. 단지 서로 다른 약속이다. 그리고 프로그래밍에서 제일 귀찮은 문제들은 보통 이렇게 "틀린 것은 아닌데 서로 다른 약속"에서 나온다.
예를 들어 어떤 작업이 평일에만 돌아야 한다고 하자. ISO 방식이라면 1부터 5까지가 평일이다.
1 <= dayOfWeek <= 5
그런데 일요일이 0인 방식에서도 1부터 5까지는 월요일부터 금요일이다. 여기까지는 운 좋게 같다. 하지만 주말을 체크하는 순간 달라진다.
%u (ISO-8601)
Saturday = 6
Sunday = 7
%w
Saturday = 6
Sunday = 0
토요일은 둘 다 6인데 일요일만 다르다. 대충 5가 금요일이니 5보다 크면 주말 이런식으로 짰다면 u와 w가 섞이는 순간 버그가 된다.
linux strftime(3) 에서도 ISO-8601 방식을 쓴다.3
ISO 8601 week dates
%G, %g, and %V yield values calculated from the week-based year
defined by the ISO 8601 standard. In this system, weeks start on
a Monday, and are numbered from 01, for the first week, up to 52
or 53, for the last week. Week 1 is the first week where four or
more days fall within the new year (or, synonymously, week 01 is:
the first week of the year that contains a Thursday; or, the week
that has 4 January in it). When three or fewer days of the first
calendar week of the new year fall within that year, then the ISO
8601 week-based system counts those days as part of week 52 or 53
of the preceding year. For example, 1 January 2010 is a Friday,
meaning that just three days of that calendar week fall in 2010.
Thus, the ISO 8601 week-based system considers these days to be
part of week 53 (%V) of the year 2009 (%G); week 01 of ISO 8601
year 2010 starts on Monday, 4 January 2010. Similarly, the first
two days of January 2011 are considered to be part of week 52 of
the year 2010.
C#의 System.DayOfWeek는 일요일이 0이다.4
public enum DayOfWeek {
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
}
Java의 java.time.DayOfWeek는 ISO-8601을 따른다. getValue()를 호출하면 월요일이 1, 일요일이 7이다.5
/**
* Gets the day-of-week {@code int} value.
* <p>
* The values are numbered following the ISO-8601 standard, from 1 (Monday) to 7 (Sunday).
* See {@link java.time.temporal.WeekFields#dayOfWeek()} for localized week-numbering.
*
* @return the day-of-week, from 1 (Monday) to 7 (Sunday)
*/
public int getValue() {
return ordinal() + 1;
}
그래서 다음과 같이 동작한다.
DayOfWeek.MONDAY.getValue(); // 1
DayOfWeek.SUNDAY.getValue(); // 7
재미있는 것은 Java enum 자체의 순서다.
public enum DayOfWeek implements TemporalAccessor, TemporalAdjuster {
/**
* The singleton instance for the day-of-week of Monday.
* This has the numeric value of {@code 1}.
*/
MONDAY,
/**
* The singleton instance for the day-of-week of Tuesday.
* This has the numeric value of {@code 2}.
*/
TUESDAY,
/**
* The singleton instance for the day-of-week of Wednesday.
* This has the numeric value of {@code 3}.
*/
WEDNESDAY,
/**
* The singleton instance for the day-of-week of Thursday.
* This has the numeric value of {@code 4}.
*/
THURSDAY,
/**
* The singleton instance for the day-of-week of Friday.
* This has the numeric value of {@code 5}.
*/
FRIDAY,
/**
* The singleton instance for the day-of-week of Saturday.
* This has the numeric value of {@code 6}.
*/
SATURDAY,
/**
* The singleton instance for the day-of-week of Sunday.
* This has the numeric value of {@code 7}.
*/
SUNDAY;
}
Java enum의 ordinal()은 0부터 시작한다. 그러면 MONDAY.ordinal()은 0이고 SUNDAY.ordinal()은 6이다. 하지만 getValue()는 ISO-8601에 맞춰 ordinal() + 1을 반환한다.
그래서 Java 문서에는 굳이 ordinal()을 쓰지 말라고 적혀 있다.6
/**
* A day-of-week, such as 'Tuesday'.
* <p>
* {@code DayOfWeek} is an enum representing the 7 days of the week -
* Monday, Tuesday, Wednesday, Thursday, Friday, Saturday and Sunday.
* <p>
* In addition to the textual enum name, each day-of-week has an {@code int} value.
* The {@code int} value follows the ISO-8601 standard, from 1 (Monday) to 7 (Sunday).
* It is recommended that applications use the enum rather than the {@code int} value
* to ensure code clarity.
* <p>
* <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code DayOfWeek}.
* Use {@code getValue()} instead.</b>
*/
문서에 이렇게 적혀 있지만, 아마 누군가는 분명 ordinal()을 쓸거다. 그리고 다른 사람이 문서를 읽고 쓴 getValue()와 충돌할 것이다.
지금까지는 그래도 1부터 6까지는 같았다. 그런데, 리눅스의 Weekday(3) enum은 일요일을 1로 둔다.7
enum Weekday {
Sunday = 1,
Monday = 2,
Tuesday = 3,
Wednesday = 4,
Thursday = 5,
Friday = 6,
Saturday = 7,
Sun = 1,
Mon = 2,
Tue = 3,
Wed = 4,
Thu = 5,
Fri = 6,
Sat = 7
}
ISO-8601에서는 월요일이 1이고 일요일이 7인데, 여기서는 일요일이 1이고 토요일이 7이다. 둘 다 1부터 7까지를 쓰지만 의미는 완전히 다르다.
- ISO-8601, calendar week.^
- GNU coreutils
date(1)의%u,%w포맷.^ - Linux man-pages
strftime(3)의 ISO 8601 week dates 설명.^ - Microsoft Learn,
System.DayOfWeek. 소스는dayofweek.cs.^ - OpenJDK,
java.time.DayOfWeek. 구 문서는 ThreeTenDayOfWeek.^ - OpenJDK,
java.time.DayOfWeek. 구 문서는 ThreeTenDayOfWeek.^ - Linux
Weekday(3)문서의 enum 예시.^