에서 java.util.Calendar
, 1 월은 0으로 정의하지 달 1. 것과 특정의 이유가 있나요?
많은 사람들이 그것에 대해 혼란스러워하는 것을 보았습니다 ...
에서 java.util.Calendar
, 1 월은 0으로 정의하지 달 1. 것과 특정의 이유가 있나요?
많은 사람들이 그것에 대해 혼란스러워하는 것을 보았습니다 ...
답변:
Java 날짜 / 시간 API 인 끔찍한 혼란의 일부일뿐입니다. 무엇이 잘못되었는지 나열하는 데는 시간이 오래 걸릴 것입니다 (그리고 문제의 절반을 모른다고 확신합니다). 날짜와 시간으로 작업하는 것은 까다로울 수 있지만 어쨌든 매우 큽니다.
호의를 가지고 대신 Joda Time 또는 JSR-310을 사용하십시오 .
편집 : 이유에 관해서는-다른 답변에서 언급했듯이 오래된 C API 또는 0부터 시작하는 일반적인 느낌으로 인해 발생할 수 있습니다. 당일은 물론 1로 시작합니다. 원래 구현 팀 외부의 누군가가 실제로 이유를 밝힐 수 있는지 의심 스럽지만 독자 는 잘못된 결정의 전체 영역을 살펴보기 위해 잘못된 결정을 내린 이유 에 대해 너무 걱정하지 말 것을 촉구합니다 .java.util.Calendar
더 나은 것을 찾기 위해 .
0 기반 인덱스를 사용하는 것을 선호 하는 한 가지 점 은 "이름 배열"과 같은 것을 쉽게 만든다는 것입니다.
// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];
물론, 이것은 13 개월의 달력을 얻 자마자 실패하지만 적어도 지정된 크기는 예상 개월 수입니다.
이것은 아니다 좋은 이유,하지만 그건 이유 ...
편집 : 주석 종류의 날짜 / 캘린더에서 내가 잘못 생각하는 것에 대한 몇 가지 아이디어를 요청합니다.
Date
있고 좋은Calendar
여러 가지 있지만 값이없는 "존"대 "로컬"의 분리로, 같은 시간 대 날짜 대 날짜 / 시간Date.toString()
항상 (의 지금 전에 많은 스택 오버플로 사용자를 혼동 것을) 시스템 로컬 시간대를 사용하여 구현몇 달 동안 수학하는 것이 훨씬 쉬우니까요.
12 월 이후 1 개월은 1 월이지만 일반적으로이를 파악하려면 월 번호를 가져 와서 수학을 수행해야합니다.
12 + 1 = 13 // What month is 13?
알아! 모듈러스 12를 사용하여이 문제를 빠르게 해결할 수 있습니다.
(12 + 1) % 12 = 1
이것은 11 월까지 11 개월 동안 잘 작동합니다 ...
(11 + 1) % 12 = 0 // What month is 0?
월을 추가하기 전에 1을 빼고 다시 모듈로 계산 한 다음 다시 1을 다시 추가하면이 문제를 모두 해결할 수 있습니다.
((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!
이제 0-11 개월의 문제에 대해 생각해 봅시다.
(0 + 1) % 12 = 1 // February
(1 + 1) % 12 = 2 // March
(2 + 1) % 12 = 3 // April
(3 + 1) % 12 = 4 // May
(4 + 1) % 12 = 5 // June
(5 + 1) % 12 = 6 // July
(6 + 1) % 12 = 7 // August
(7 + 1) % 12 = 8 // September
(8 + 1) % 12 = 9 // October
(9 + 1) % 12 = 10 // November
(10 + 1) % 12 = 11 // December
(11 + 1) % 12 = 0 // January
모든 달은 동일하게 작동하며 해결 방법이 필요하지 않습니다.
((11 - 1 + 1) % 12) + 1 = 12
그냥 (11 % 12) + 1
그냥 1을 추가해야 개월 1..12 위해, 즉 후 모듈로 일입니다. 마법이 필요하지 않습니다.
이에 대한 답변이 많이 있었지만 어쨌든 주제에 대한 견해를 밝힐 것입니다. 앞에서 언급 한 것처럼이 이상한 동작의 원인은 POSIX C time.h
에서 나온 것입니다. POSIX C 는 0-11 범위의 정수로 저장됩니다. 이유를 설명하려면 다음과 같이보십시오. 연도 및 일수는 구어 숫자로 간주되지만 월은 고유 한 이름을 갖습니다. 따라서 1 월은 첫 달이므로 첫 번째 배열 요소 인 오프셋 0으로 저장됩니다. monthname[JANUARY]
될 것 "January"
입니다. 연도의 첫 달은 첫 달 배열 요소입니다.
반면 요일 숫자는 이름이 없으므로 0-30으로 int에 저장하면 혼란 스러울 수 있으며 day+1
출력에 대한 많은 지시 사항을 추가하고 물론 많은 버그가 발생하기 쉽습니다.
그러나 불일치는 혼란 스럽습니다. 특히 자바 스크립트 (이 기능을 상속받은 자바 스크립트)에서 언어와는 거리가 먼 곳에 추상화되어야하는 스크립팅 언어입니다.
TL; DR : 월은 이름이 있고 월은 없습니다.
게으름을 말하고 싶습니다. 배열은 0에서 시작합니다 (모두 알고 있음). 일년 중 몇 달은 배열이므로 Sun의 일부 엔지니어는이 작은 코드를 Java 코드에 넣는 것을 귀찮게하지 않았다고 생각합니다.
프로그래머는 0 기반 인덱스에 집착하기 때문입니다. 좋습니다. 그보다 조금 더 복잡합니다. 하위 수준의 논리를 사용하여 0 기반 인덱싱을 사용하는 경우 더 의미가 있습니다. 그러나 전반적으로 나는 여전히 첫 문장을 고수 할 것입니다.
개인적으로, 나는 Java Calendar API의 이상 함을 그레고리력 중심의 사고 방식과 이혼하고 그 점에서 더 불가지론 적으로 프로그래밍해야한다는 표시로 가져 왔습니다. 특히, 몇 달 동안 하드 코딩 된 상수를 피하기 위해 다시 한 번 배웠습니다.
다음 중 어느 것이 더 정확할까요?
if (date.getMonth() == 3) out.print("March");
if (date.getMonth() == Calendar.MARCH) out.print("March");
이것은 Joda Time에 대해 나에게 약간의 자극을주는 한 가지 사실을 보여줍니다-프로그래머가 하드 코딩 된 상수로 생각하도록 장려 할 수 있습니다. (조금만. Joda가 프로그래머가 잘못 프로그래밍 하도록 강요하는 것은 아닙니다 .)
나를 위해 아무도 mindpro.com 보다 더 잘 설명하지 않습니다 .
잡았다
java.util.GregorianCalendar
보다 훨씬 적은 버그와 버그가 있습니다old java.util.Date
클래스 만 여전히 소풍이 아닙니다.일광 절약 시간제를 처음 제안했을 때 프로그래머가 있었다면 그들은 미쳤고 다루기 힘든 것으로 거부했을 것입니다. 일광 절약 시간제를 사용하면 근본적인 모호성이 있습니다. 가을에 오전 2시에 시계를 한 시간 뒤로 설정하면 현지 시간으로 오전 1시 30 분이라는 두 가지 다른 순간이 있습니다. 일광 절약 시간을 계획했는지 또는 표준 시간으로 읽을 것인지 기록한 경우에만 구분할 수 있습니다.
불행히도 말할 방법이 없습니다
GregorianCalendar
당신이 의도 한 . 모호성을 피하기 위해 더미 UTC TimeZone으로 현지 시간을 알려 주어야합니다. 프로그래머는 일반적으로이 문제에 눈을 감고이 시간 동안 아무도 아무것도하지 않기를 바랍니다.밀레니엄 버그. 버그는 여전히 Calendar 클래스에 속하지 않습니다. JDK (Java Development Kit) 1.3에서도 2001 버그가 있습니다. 다음 코드를 고려하십시오.
GregorianCalendar gc = new GregorianCalendar(); gc.setLenient( false ); /* Bug only manifests if lenient set false */ gc.set( 2001, 1, 1, 1, 0, 0 ); int year = gc.get ( Calendar.YEAR ); /* throws exception */
이 버그는 2001/01/01 MST의 오전 7시에 사라집니다.
GregorianCalendar
형식화되지 않은 int 마법 상수의 거대한 더미에 의해 제어됩니다. 이 기술은 컴파일 타임 오류 검사에 대한 희망을 완전히 없애줍니다. 예를 들어 사용하는 월을 얻으려면GregorianCalendar. get(Calendar.MONTH));
GregorianCalendar
날것GregorianCalendar.get(Calendar.ZONE_OFFSET)
과 일광 절약이 있습니다GregorianCalendar. get( Calendar. DST_OFFSET)
를 사용하지만 실제 시간대 오프셋을 사용하는 방법은 없습니다. 이 두 가지를 별도로 가져와 함께 추가해야합니다.
GregorianCalendar.set( year, month, day, hour, minute)
초를 0으로 설정하지 않습니다.
DateFormat
과GregorianCalendar
제대로 메쉬하지 않습니다. 간접적으로 날짜로 한 번 달력을 두 번 지정해야합니다.사용자가 시간대를 올바르게 구성하지 않은 경우 기본적으로 PST 또는 GMT로 자동 설정됩니다.
GregorianCalendar에서, 달은 지구상의 다른 모든 사람들처럼 1이 아니라 1 월 0 일부터 시작하여 번호가 매겨집니다. 그러나 요일은 일요일 = 1, 월요일 = 2, 토요일 = 7과 같이 1부터 시작합니다. 아직 DateFormat. 구문 분석은 1 월 1 일부터 전통적인 방식으로 작동합니다.
java.util.Month
Java는 몇 달 동안 1 기반 인덱스를 사용하는 또 다른 방법을 제공합니다. java.time.Month
열거 형을 사용하십시오 . 12 개월마다 하나의 객체가 미리 정의되어 있습니다. 1 월 -12 월에 각각 1-12 개의 숫자가 할당되어 있습니다. 번호 getValue
를 문의하십시오.
의 만들기 사용 Month.JULY
(당신에게 7을 제공합니다) 대신에이 Calendar.JULY
(당신에게 6을 제공합니다).
(import java.time.*;)
Month.FEBRUARY.getValue() // February → 2.
2
이제 우리는 번거로운 구식 날짜-시간 클래스 인 java.time 클래스를 현대적으로 대체했습니다 .
java.time.Month
이러한 클래스 중에는 열거 형이 있습니다. 열거 형에는 클래스가로드 될 때 자동으로 인스턴스화되는 하나 이상의 미리 정의 된 객체가 있습니다. 에 : 우리가 다스 같은 개체가 각각 이름을 부여 , , , 등을. 각각은 클래스 상수입니다. 코드의 어느 곳에서나 이러한 객체를 사용하고 전달할 수 있습니다. 예:Month
Month
JANUARY
FEBRUARY
MARCH
static final public
someMethod( Month.AUGUST )
다행스럽게도, 1은 1 월이고 12는 12 월입니다.
Month
특정 월 번호 (1-12) 의 객체를 가져옵니다.
Month month = Month.of( 2 ); // 2 → February.
다른 방향으로 가면서 Month
월 번호를 물체에 요청하십시오 .
int monthNumber = Month.FEBRUARY.getValue(); // February → 2.
이 수업 에서 매월 일 수를 아는 것과 같은 다른 많은 편리한 방법들이 있습니다. 클래스는 현지화 된 달 이름 을 생성 할 수도 있습니다 .
현지화 된 달 이름을 다양한 길이 또는 약어로 얻을 수 있습니다.
String output =
Month.FEBRUARY.getDisplayName(
TextStyle.FULL ,
Locale.CANADA_FRENCH
);
페비에
또한 정수보다 정수가 아닌이 열거 형의 객체를 코드베이스에 전달 해야 합니다 . 그렇게하면 형식 안전성이 제공되고 유효한 값 범위가 보장되며 코드 자체 문서화가 더욱 강화됩니다. Java의 놀랍도록 강력한 열거 형 기능에 익숙하지 않은 경우 Oracle Tutorial을 참조하십시오 .
또한 유용 찾을 수 있습니다 Year
및 YearMonth
클래스.
java.time의 프레임 워크는 나중에 자바 8에 내장되어 있습니다. 이 클래스는 까다로운 기존에 대신 기존 과 같은 날짜 - 시간의 수업을 java.util.Date
, .Calendar
, java.text.SimpleDateFormat
.
Joda 타임 프로젝트는 현재의 유지 관리 모드 , java.time로 마이그레이션을 조언한다.
자세한 내용은 Oracle Tutorial을 참조하십시오 . 많은 예제와 설명을 보려면 스택 오버플로를 검색하십시오. 사양은 JSR 310 입니다.
java.time 클래스는 어디서 구할 수 있습니까?
ThreeTen - 추가 프로젝트 추가 클래스와 java.time를 확장합니다. 이 프로젝트는 향후 java.time에 추가 될 수있는 입증 된 근거입니다. 당신은 여기에 몇 가지 유용한 클래스와 같은 찾을 수 있습니다 Interval
, YearWeek
, YearQuarter
, 그리고 더 .
정확히 0 자체로 정의되지 않고 Calendar.January로 정의됩니다. 열거 형 대신 상수로 정수를 사용하는 것이 문제입니다. Calendar.January == 0.
언어 쓰기는보기보다 어렵고 특히 시간을 처리하는 것은 대부분의 사람들이 생각하는 것보다 훨씬 어렵습니다. 문제의 일부 (실제로는 Java가 아님)에 대해서는 https://www.youtube.com/watch?v=-5wpm-gesOY 의 YouTube 비디오 "시간 및 시간대 문제-Computerphile"를 참조하십시오 . 머리가 혼란스러워 웃으면 서 놀라지 마십시오.
DannySmurf의 게으름에 대한 답변 외에도 다음과 같은 상수를 사용하도록 권장하는 것 Calendar.JANUARY
입니다.
모든 것이 0으로 시작하기 때문입니다. 이것은 Java 프로그래밍의 기본 사실입니다. 한 가지가 그것에서 벗어나면 혼란의 전체적인 슬픔으로 이어질 것입니다. 그것들의 형성을 주장하고 그들과 코드를 작성하지 마십시오.