>

GregorianCalendar 클래스에서 이상한 동작을 겪었고 실제로 내가 나쁜 일을하고 있는지 궁금했습니다.

초기화 날짜의 월에 달력을 설정하려는 달보다 실제 최대 값이 더 큰 경우에만 추가됩니다.

예제 코드는 다음과 같습니다.

   // today is 2010/05/31  
    GregorianCalendar cal = new GregorianCalendar();
    cal.set(Calendar.YEAR, 2010);
    cal.set(Calendar.MONTH, 1); // FEBRUARY
    cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
    cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY));
    cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE));
    cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND));
    cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND));
    return cal.getTime(); // => 2010/03/03, wtf

캘린더 초기화 날짜가 31 일 (월)이고 2 월 (28 일)로 설정된 월이 엉망이어서 문제가 발생한다는 것을 알고 있습니다. 수정은 쉽지만 (년과 월을 설정하기 전에 day_of_month를 1로 설정), 이것이 실제로 원하는 행동인지 궁금합니다. 어떤 생각?

  • 답변 # 1

    현재 날짜/시간의 실제 최대 값을 가져옵니다. 5 월 31 일은 2 월 28 일보다 3 일이 더 많으므로 3 월 3 일로 이동합니다.

    Calendar#clear() 에 전화해야합니다  그것을 얻거나 만든 후에 :

    GregorianCalendar cal = new GregorianCalendar();
    cal.clear();
    // ...
    
    

    결과 :

    Sun Feb 28 23:59:59 GMT-04:00 2010
    
    

    (내 시간대에 따라 맞음)

    답 중 하나에서 말했듯이, java.util.Calendar  그리고 Date  서사시 실패입니다. 집중적 인 날짜/시간 작업을 수행 할 때는 JodaTime을 고려하십시오.

  • 답변 # 2

    예, 이것이 작동하는 방식입니다. 당신이 GregorianCalendar 에서 시작하는 경우  정확한 날짜를 가지고 있고 일관성이 없어서 수정하면 얻은 결과를 신뢰할 수 없습니다.

    getActualMaximum(..) 에 관한 문서에 따르면  상태는 다음과 같습니다.

    와이즈 비즈

    그래서 작동해야하지만 일관된 값으로 공급해야합니다.2010 년 2 월 31 일이 정확하지 않으며 날짜 값에 의존하는 항목 (예 :

    For example, if the date of this instance is February 1, 2004, the actual maximum value of the DAY_OF_MONTH field is 29 because 2004 is a leap year, and if the date of this instance is February 1, 2005, it's 28.

    )을 적용 함 )가 작동하지 않습니다. 자체적으로 어떻게 수정해야합니까? 그 달을 결정함으로써 잘못입니까? 아니면 그 날이 잘못되었다고?

    그러므로 모든 사람이 항상 JodaTime을 사용한다고 말하면 .. :)

  • 답변 # 3

    원하는 행동이 아니라고 확신합니다. 나는 그들이 수업을 할 때 그 유스 케이스를 실제로 생각한 사람이 아무도 없다고 확신합니다. 중요한 것은 캘린더는 내부 상태에 매우 큰 문제가 있으며 모든 설정된 방법에서 모든 잠재적 전환을 관리하는 방법입니다.

    프로젝트에서 JodaTime 또는 JSR-310을 사용할 수없는 경우 Calendar 클래스를 사용할 때 단위 테스트를 많이 수행하십시오. 이 경우 캘린더 코드는 코드를 실행하는 달의 요일 또는 시간에 따라 다르게 작동합니다.

  • 답변 # 4

    아마도 getActualMaximum  당신을 위해 그것을 정렬합니다. 아래 코드를 실행할 때 예외가 발생합니다.

    그렇지 않으면 Joda가 더 나은 답변입니다.

    setLenient(boolean lenient)
    
    

  • 답변 # 5

    현대적인 답변에 기여하고 싶습니다.

    import java.util.Calendar;
    public class CalTest
    {
        public static void main(String[] args)
        {
            // today is 2010/05/31
            Calendar cal = Calendar.getInstance();
            cal.setLenient(false);
            cal.set(Calendar.YEAR, 2010);
            cal.set(Calendar.MONTH, 1); // FEBRUARY
            cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
            cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY));
            cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE));
            cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND));
            cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND));
            System.out.println(cal.getTime());
        }
    }
    
    

    내 시간대로 실행하면 다음과 같이 인쇄됩니다.

    와이즈 비즈

    실행 한 연도 및 월에 상관없이 인쇄물이 동일합니다. 시간대에 대한 종속성은 유감 스럽지만 ZonedDateTime endOfFebruary2010 = LocalDate.of(2010, Month.MARCH, 1) .atStartOfDay(ZoneId.systemDefault()) .minusNanos(1); System.out.println(endOfFebruary2010); 와 같이 원하는 시간대를 지정하여 언급 할 수 있습니다. .

    2010-02-28T23:59:59.999999999+01:00[Europe/Copenhagen]

    를 사용하고 추천합니다. , 최신 Java 날짜 및 시간 API.

    필수적으로 구식 ZoneId.of("Asia/Oral") 가 필요한 경우  객체 (이 경우에만)를 변환하십시오 :

    java.time
    
    

    와이즈 비즈

    몇 달 동안 며칠 만 필요한 경우 (이는 중복 질문으로 요청 됨) :

    java.util.Date
    
    

    와이즈 비즈

    알다시피, 당신의 진짜 질문은 :

    와이즈 비즈

    아무것도없는 Date oldFashionedDate = Date.from(endOfFebruary2010.toInstant()); System.out.println(oldFashionedDate); 는 그것이 원하는 행동이라고 강력하게 믿는다  생성자는 현재 날짜와 현재 시간을 반환합니다. 그리고 그

    Sun Feb 28 23:59:59 CET 2010

     명시 적으로 설정 한 필드 만 설정하고 다른 필드는 변경하지 마십시오. 그리고 2010 년 2 월 31 일에는 해당 월에 28 일밖에 없었기 때문에 오류의 징후없이 3 월로 오버플로했습니다. 이러한 디자인 결정의 조합으로 피할 수없는 결론이 나옵니다. 관찰 한 동작은 의도적 인 것입니다.

    이것이 열악한 디자인이라고 생각한다면, 우리는 많은 사람들과 동의합니다. 이것이 또한 YearMonth ym = YearMonth.of(2011, Month.FEBRUARY); int numDays = ym.lengthOfMonth(); System.out.println(numDays); 를 대체하는 이유였습니다.  그리고

    28

     

    …I was wondering is this really was the wanted behaviour. Any thoughts ?

    와 함께 나왔다  4 년 전 당신은 GregorianCalendar 를 사용할 필요가 없습니다  다시.

    예약 : 우리 도구는 여전히 Java 1.7에 의존합니다

    Calender.set()  Java 7에서 잘 작동합니다. 적어도6만 있으면됩니다.

    Java 8 이상 및 최신 Android 기기 (API 레벨 26부터)에 최신 API가 내장되어 있습니다.

    Java 6 및 7에서는 새 클래스의 백 포트 인 ThreeTen Backport를 가져옵니다 (ThreeTen for JSR 310;하단의 링크 참조).

    On (이전) Android는 Android 버전의 ThreeTen Backport를 사용합니다. 이름이 ThreeTenABP입니다. Calendar 에서 날짜 및 시간 클래스를 가져와야합니다.  하위 패키지 포함

    링크

    Oracle tutorial : GregorianCalendar 사용법을 설명하는 Date Time .

    JSR (Java Specification Request) 310, 여기서 java.time  처음 설명되었습니다.

    Wyzwyz의 백 포트 인 ThreeTen Backport 프로젝트 Java 6 및 7로 (JSR-310 용 ThreeTen)

    ThreeTenABP, ThreeTen Backport의 Android 버전

    질문 : Android 프로젝트에서 ThreeTenABP를 사용하는 방법에 대한 자세한 설명과 함께

    Calendar

  • 이전 java - KG, Litre, Metre, KM 등과 같은 다양한 항목 단위를 나타내는 API가 있습니까?
  • 다음 javascript - 배열 객체에 내 키가 있는지 확인하는 방법