Java Calendar에서 1 월 달이 0 인 이유는 무엇입니까?


300

에서 java.util.Calendar, 1 월은 0으로 정의하지 달 1. 것과 특정의 이유가 있나요?

많은 사람들이 그것에 대해 혼란스러워하는 것을 보았습니다 ...


4
상수 JANUARY, FEBRUARY 등이 존재하기 때문에 그러한 종류의 구현 세부 사항이 아닌가? 날짜 클래스는 올바른 Java 열거 지원보다 우선합니다.
gnud

6
더 성가신 이유-왜 12 월이 있습니까?
matt b

40
@gnud : 아니요, 구현 세부 정보가 아닙니다. "자연적인"기본 (예 : Jan = 1)의 정수를 받았을 때 달력 API와 함께 사용해야 할 때 고통 스럽습니다.
Jon Skeet 2012

1
@matt b : 13 개월이있는 그레고리력이 아닌 달력 (달 달력 등)을위한 것입니다. 그렇기 때문에 숫자로 생각하지 말고 달력으로 현지화하는 것이 가장 좋습니다.
erickson

7
13 개월의 논쟁은 의미가 없습니다. 그렇다면 추가 월이 0 또는 13이 아닌 이유는 무엇입니까?
Quinn Taylor

답변:


323

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 개월의 달력을 얻 자마자 실패하지만 적어도 지정된 크기는 예상 개월 수입니다.

이것은 아니다 좋은 이유,하지만 그건 이유 ...

편집 : 주석 종류의 날짜 / 캘린더에서 내가 잘못 생각하는 것에 대한 몇 가지 아이디어를 요청합니다.

  • 놀라운 기준 (날짜의 연도 기준은 1900, 더 이상 사용되지 않는 생성자에 대해서는 인정, 둘 다의 월 기준은 0)
  • 변경 가능-불변 유형을 사용하면 실제로 효과적인 으로 작업 하는 것이 훨씬 간단 해집니다.
  • 불충분 한 유형의 집합 : 가지고 Date있고 좋은Calendar 여러 가지 있지만 값이없는 "존"대 "로컬"의 분리로, 같은 시간 대 날짜 대 날짜 / 시간
  • 명확하게 명명 된 메소드 대신 매직 상수를 사용하여 못생긴 코드를 생성하는 API
  • 추론하기 매우 어려운 API-일이 재 계산되는시기에 관한 모든 사업 등
  • 매개 변수가없는 생성자를 기본적으로 "지금"으로 설정하면 테스트하기 어려운 코드가 생성됨
  • Date.toString()항상 (의 지금 전에 많은 스택 오버플로 사용자를 혼동 것을) 시스템 로컬 시간대를 사용하여 구현

14
... 유용한 간단한 Date 메소드를 모두 사용하지 않는 것은 무엇입니까? 이제는 끔찍한 Calendar 객체를 복잡한 방식으로 사용하여 단순했던 작업을 수행해야합니다.
Brian Knoblauch

3
@ 브라이언 : 당신의 고통을 느낍니다. 다시 말하지만, Joda 시간 간단이 : (불변성 요소도 함께 작업에 일이 훨씬 더 유쾌한 있습니다.)입니다
존 소총

8
질문에 대답하지 않았습니다.
Zeemee February

2
@ user443854 : 편집에 몇 가지 사항을 나열했습니다. 도움이되는지 확인하십시오.
Jon Skeet

2
Java 8을 사용하는 경우 Calendar 클래스를 버리고 새롭고 매끄러운 DateTime API로 전환 할 수 있습니다 . 새로운 API에는 불변 / 스레드 세이프 DateTimeFormatter 가 포함되어있어 문제가 많고 값 비싼 SimpleDateFormat보다 크게 개선되었습니다.
ccpizza

43

몇 달 동안 수학하는 것이 훨씬 쉬우니까요.

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

모든 달은 동일하게 작동하며 해결 방법이 필요하지 않습니다.


5
이것은 만족 스럽습니다. 최소한이 광기에 가치가 있습니다!
moljac024

"많은 마법의 숫자"-아, 그것은 두 번 나타나는 하나입니다.
user123444555621

C가 불행히도 "모듈러스"대신 "나머지"연산자를 사용했기 때문에 한 달 뒤로 돌아가는 것은 여전히 ​​어색합니다. 또한 연도를 조정하지 않고 한 달을 얼마나 자주 참 아야하는지 잘 모르겠으며, 1-12 개월이 지나도`while (month> 12) {month- = 12; year ++;}
supercat

2
DateTime.AddMonths와 같은 제정 한 함수는 lib에서 제대로 구현하기가 너무 어렵 기 때문에, 우리가 설명한 수학을 수행해야합니다 ... Mmmmmkay
nsimeonov

8
나는이 upvotes을 이해하지 않습니다 - ((11 - 1 + 1) % 12) + 1 = 12그냥 (11 % 12) + 1그냥 1을 추가해야 개월 1..12 위해, 즉 모듈로 일입니다. 마법이 필요하지 않습니다.
mfitzp 2016 년

35

C 기반 언어는 C를 어느 정도 복사합니다. 에 tm정의 된 구조 는 (주석) 범위가 0-11 time.h인 정수 필드 tm_mon를 갖습니다 .

C 기반 언어는 인덱스 0에서 배열을 시작합니다. 따라서 인덱스로 월 이름 배열로 문자열을 출력하는 데 편리했습니다 tm_mon.


22

이에 대한 답변이 많이 있었지만 어쨌든 주제에 대한 견해를 밝힐 것입니다. 앞에서 언급 한 것처럼이 이상한 동작의 원인은 POSIX C time.h에서 나온 것입니다. POSIX C 는 0-11 범위의 정수로 저장됩니다. 이유를 설명하려면 다음과 같이보십시오. 연도 및 일수는 구어 숫자로 간주되지만 월은 고유 한 이름을 갖습니다. 따라서 1 월은 첫 달이므로 첫 번째 배열 요소 인 오프셋 0으로 저장됩니다. monthname[JANUARY]될 것 "January"입니다. 연도의 첫 달은 첫 달 배열 요소입니다.

반면 요일 숫자는 이름이 없으므로 0-30으로 int에 저장하면 혼란 스러울 수 있으며 day+1출력에 대한 많은 지시 사항을 추가하고 물론 많은 버그가 발생하기 쉽습니다.

그러나 불일치는 혼란 스럽습니다. 특히 자바 스크립트 (이 기능을 상속받은 자바 스크립트)에서 언어와는 거리가 먼 곳에 추상화되어야하는 스크립팅 언어입니다.

TL; DR : 월은 이름이 있고 월은 없습니다.


1
"달에는 이름이 있고 요일은 없습니다." '금요일'에 대해 들어 본 적이 있습니까? ;) OK 나는 당신이 '.. 달의 일은하지 않는다'를 의미한다고 추측합니다. 아마도 (그렇지 않으면 좋은) 답변을 편집하는 것이 비용이들 것입니다. :-)
Andrew Thompson

0/0/0000이 "00-Jan-0000"또는 "00-XXX-0000"으로 더 잘 렌더링됩니까? IMHO, 13 개의 "개월"이 있었지만 0 개월에 더미 이름이 주어지면 많은 코드가 더 깨끗했을 것입니다.
supercat

1
흥미로운 점이지만 0/0/0000은 유효한 날짜가 아닙니다. 40/40/0000을 어떻게 렌더링 하시겠습니까?
piksel bitworks

12

Java 8에는 새로운 Date / Time API JSR 310 이 있습니다. 사양 리드는 JodaTime의 기본 작성자와 동일하며 많은 유사한 개념과 패턴을 공유합니다.


2
새로운 날짜 시간 API는 이제 자바의 일부 8
mschenk74

9

게으름을 말하고 싶습니다. 배열은 0에서 시작합니다 (모두 알고 있음). 일년 중 몇 달은 배열이므로 Sun의 일부 엔지니어는이 작은 코드를 Java 코드에 넣는 것을 귀찮게하지 않았다고 생각합니다.


9
아뇨. 프로그래머보다 고객의 효율성을 최적화하는 것이 더 중요합니다. 이 고객은 여기서 묻는 시간을 보내고 있기 때문에 실패했습니다.
TheSmurf

2
효율성과는 전혀 관련이 없습니다. 몇 개월이 배열에 저장되어있는 것과 같지 않으며 12 개월을 나타내는 데 13이 필요합니다. 처음부터 API를 사용자 친화적으로 만들지 않는 것이 중요합니다. Josh Bloch가 "Effective Java"의 날짜 및 달력에서 으르렁 거립니다. 완벽한 API는 거의 없으며 Java의 날짜 / 시간 API는 불완전한 역할을하는 불행한 역할을합니다. 그것은 인생이지만 효율성과 관련이 있다고 가정하지 마십시오.
Quinn Taylor

1
그렇다면 0에서 30까지의 일을 계산하지 않는 이유는 무엇입니까? 일관성이없고 조잡합니다.
Juangui Jordán

9

아마도 C의 "struct tm"도 마찬가지이기 때문입니다.


8

프로그래머는 0 기반 인덱스에 집착하기 때문입니다. 좋습니다. 그보다 조금 더 복잡합니다. 하위 수준의 논리를 사용하여 0 기반 인덱싱을 사용하는 경우 더 의미가 있습니다. 그러나 전반적으로 나는 여전히 첫 문장을 고수 할 것입니다.


1
이것은 그 숙어 / 이동 습관의 또 다른입니다 방법의 모든 것을 상쇄하지 인덱스의 측면에서 이루어집니다 어셈블러 또는 기계 언어로 다시. 배열 표기법은 오프셋 0에서 시작하여 연속 블록에 액세스하는 지름길이되었습니다.
Ken Gentle

4

개인적으로, 나는 Java Calendar API의 이상 함을 그레고리력 중심의 사고 방식과 이혼하고 그 점에서 더 불가지론 적으로 프로그래밍해야한다는 표시로 가져 왔습니다. 특히, 몇 달 동안 하드 코딩 된 상수를 피하기 위해 다시 한 번 배웠습니다.

다음 중 어느 것이 더 정확할까요?

if (date.getMonth() == 3) out.print("March");

if (date.getMonth() == Calendar.MARCH) out.print("March");

이것은 Joda Time에 대해 나에게 약간의 자극을주는 한 가지 사실을 보여줍니다-프로그래머가 하드 코딩 된 상수로 생각하도록 장려 할 수 있습니다. (조금만. Joda가 프로그래머가 잘못 프로그래밍 하도록 강요하는 것은 아닙니다 .)


1
그러나 코드에 상수가 없을 때 어떤 스키마가 두통을 유발할 가능성이 높습니까? 웹 서비스 호출의 결과 또는 그 밖의 가치가 있습니다.
Jon Skeet 2012

물론 해당 웹 서비스 호출에서도 해당 상수를 사용해야합니다. :-) 외부 발신자도 마찬가지입니다. 여러 표준이 존재한다는 사실을 확인한 후에는 표준을 시행 할 필요성이 분명해집니다. (나는 당신의 의견을 이해하기를 바랍니다 ...)
Paul Brinkley

3
그렇습니다. 우리는 1 기반 표준 인 달을 표현할 때 세계의 거의 모든 것이 사용하는 표준을 시행해야합니다.
Jon Skeet

여기서 핵심 단어는 "거의"입니다. 분명히 Jan = 등은 매우 광범위하게 사용되는 날짜 시스템에서 자연 스럽지만 왜이 경우에도 하드 코딩 된 상수를 피하는 데 예외를 두어야합니까?
Paul Brinkley

3
인생이 더 쉬워지기 때문입니다. 그냥 그렇습니다. 나는 한 결코 1 기반의 달 시스템과 오프별로 하나의 문제가 발생하지 않습니다. Java API에서 이러한 버그를 많이 보았습니다 . 세상의 다른 사람들이하는 일을 무시하는 것은 말이되지 않습니다.
Jon Skeet

4

나를 위해 아무도 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으로 설정하지 않습니다.

DateFormatGregorianCalendar 제대로 메쉬하지 않습니다. 간접적으로 날짜로 한 번 달력을 두 번 지정해야합니다.

사용자가 시간대를 올바르게 구성하지 않은 경우 기본적으로 PST 또는 GMT로 자동 설정됩니다.

GregorianCalendar에서, 달은 지구상의 다른 모든 사람들처럼 1이 아니라 1 월 0 일부터 시작하여 번호가 매겨집니다. 그러나 요일은 일요일 = 1, 월요일 = 2, 토요일 = 7과 같이 1부터 시작합니다. 아직 DateFormat. 구문 분석은 1 월 1 일부터 전통적인 방식으로 작동합니다.


4

java.util.Month

Java는 몇 달 동안 1 기반 인덱스를 사용하는 또 다른 방법을 제공합니다. java.time.Month열거 형을 사용하십시오 . 12 개월마다 하나의 객체가 미리 정의되어 있습니다. 1 월 -12 월에 각각 1-12 개의 숫자가 할당되어 있습니다. 번호 getValue를 문의하십시오.

의 만들기 사용 Month.JULY(당신에게 7을 제공합니다) 대신에이 Calendar.JULY(당신에게 6을 제공합니다).

(import java.time.*;)

3

tl; dr

Month.FEBRUARY.getValue()  // February → 2.

2

세부

Jon Skeet답변 이 맞습니다.

이제 우리는 번거로운 구식 날짜-시간 클래스 인 java.time 클래스를 현대적으로 대체했습니다 .

java.time.Month

이러한 클래스 중에는 열거 형이 있습니다. 열거 형에는 클래스가로드 될 때 자동으로 인스턴스화되는 하나 이상의 미리 정의 된 객체가 있습니다. 에 : 우리가 다스 같은 개체가 각각 이름을 부여 , , , 등을. 각각은 클래스 상수입니다. 코드의 어느 곳에서나 이러한 객체를 사용하고 전달할 수 있습니다. 예:Month MonthJANUARYFEBRUARYMARCHstatic final publicsomeMethod( 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을 참조하십시오 .

또한 유용 찾을 수 있습니다 YearYearMonth클래스.


java.time에 대하여

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

정확히 0 자체로 정의되지 않고 Calendar.January로 정의됩니다. 열거 형 대신 상수로 정수를 사용하는 것이 문제입니다. Calendar.January == 0.


1
값은 하나이며 동일합니다. API는 0을 반환 할 수도 있으며 상수와 동일합니다. Calendar.JANUARY는 1로 정의 할 수있었습니다. 이것이 요점입니다. 열거 형은 훌륭한 솔루션이지만 Java 5까지는 언어에 진정한 열거 형이 추가되지 않았으며 Date는 처음부터 사용되었습니다. 불행한 일이지만 타사 코드에서 사용하면 이러한 기본 API를 "수정"할 수는 없습니다. 수행 할 수있는 최선의 방법은 새로운 API를 제공하고 기존 API를 폐기하여 사람들이 계속 참여하도록하는 것입니다. Java 7 감사합니다 ...
Quinn Taylor

0

언어 쓰기는보기보다 어렵고 특히 시간을 처리하는 것은 대부분의 사람들이 생각하는 것보다 훨씬 어렵습니다. 문제의 일부 (실제로는 Java가 아님)에 대해서는 https://www.youtube.com/watch?v=-5wpm-gesOY 의 YouTube 비디오 "시간 및 시간대 문제-Computerphile"를 참조하십시오 . 머리가 혼란스러워 웃으면 서 놀라지 마십시오.


-1

DannySmurf의 게으름에 대한 답변 외에도 다음과 같은 상수를 사용하도록 권장하는 것 Calendar.JANUARY입니다.


5
특정 달에 대한 코드를 명시 적으로 작성할 때는이 모든 것이 매우 좋지만, 다른 소스에서 "정상적인"형태로 달을 얻는 것은 고통스러운 일입니다.
Jon Skeet 2012

1
특정 방식으로 해당 월 값을 인쇄하려고 할 때도 고통입니다. 항상 1을 추가하는 것입니다.
Brian Warshaw

-2

모든 것이 0으로 시작하기 때문입니다. 이것은 Java 프로그래밍의 기본 사실입니다. 한 가지가 그것에서 벗어나면 혼란의 전체적인 슬픔으로 이어질 것입니다. 그것들의 형성을 주장하고 그들과 코드를 작성하지 마십시오.


2
아니요, 실제 세계의 대부분은 1로 시작합니다. 오프셋 은 0으로 시작하고 한 달은 오프셋이 아니며, 12 월 중 하나는 31 일 또는 30 일 또는 29 일 중 하나입니다. 28. 월을 오프셋으로 취급하는 것은 특히 변덕 스럽습니다. 특히 동시에 같은 날에 월을 취급하지 않는 경우에는 더욱 그렇습니다. 이 차이에 대한 이유는 무엇입니까?
SantiBailors

실제 세계에서 1로 시작합니다. Java 세계에서 0으로 시작합니다. 그러나 ... 나는 다음과 같은 이유로 생각합니다. 그것은 ...-또한 필요하다면 (혼동하거나 2 월을 확인할 필요없이) 해당 월의 완전한 요일을 보여줍니다. 또한 한 해의 월 수가 규칙적이고 한 달의 날이 아니므로 배열을 선언하고 배열을 더 잘 맞추기 위해 오프셋을 사용해야하는 경우 의미가 없습니다.
Syrrus
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.