Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
557 views
in Technique[技术] by (71.8m points)

java - How to set correct time with AM PM with Calendar object?

Simple question why the result is like the following for this code:

Calendar cal2 = Calendar.getInstance();
cal2.set(Calendar.HOUR, 12);
cal2.set(Calendar.AM_PM, Calendar.PM);
System.out.println(cal2.getTime().toString()); // Wed Jan 13 00:11:08 EET 2021
cal2.set(Calendar.AM_PM, Calendar.PM);
System.out.println(cal2.getTime().toString()); // Wed Jan 13 12:11:08 EET 2021
        
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR, 12);
cal.set(Calendar.AM_PM, Calendar.AM);
System.out.println(cal.getTime().toString()); // Tue Jan 12 12:11:08 EET 2021
cal.set(Calendar.AM_PM, Calendar.AM);
System.out.println(cal.getTime().toString()); // Tue Jan 12 00:11:08 EET 2021

The first looks like it is 12 at midnight not afternoon. The third one looks like it is 12 afternoon, not midnight

Why setting calendar AM or PM multiple times change the result? How to set the time correctly?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Calendar is very confusing

You are so far from the first being confused about how the Calendar class works. Fortunately the class is also long outdated. You should not use it.

Why setting calendar AM or PM multiple times change the result? …

To answer your question as asked, Andi80 is correct in the other answer and the comments to it: HOUR goes from 0 through 11. The documentation says about HOUR:

Field number for get and set indicating the hour of the morning or afternoon. HOUR is used for the 12-hour clock (0 - 11). Noon and midnight are represented by 0, not by 12. E.g., at 10:04:15.250 PM the HOUR is 10.

When you first set hour to 12 and AM/PM to PM, one should have expected an exception because the hour value is out of range. But no, a Calendar object with default settings doesn’t give you that. Instead it sets the time to 0 AM the following day; Jan 13 when you ran the code on Jan 12. By Calendar logic hour 12 is the hour that comes after hour 11.

When you set PM again, Calendar takes off from the time you had already got, which is in AM, and changes it into PM, so you get 12:11:08, still on Jan 13, the following day.

Why does it calculate the time twice? Not once and not three times when you do three calls to set()? It’s another confusing trait of Calendar. It calculates the time when you call getTime() (and some designated other methods). At that point it picks up all the changes from the calls to set() up to that point and combines them to the best of its abilities, discarding some if there are conflicts, using rules that no person in their right mind will want to understand.

The case for AM is similar, so I leave the details to the reader.

java.time

… How to set the time correctly?

I recommend that you use java.time, the modern Java date and time API, for your time work. If you just want 12 noon or 12 midnight, they are built in as constants:

    LocalTime t12Noon = LocalTime.NOON;
    System.out.println(t12Noon);
    
    LocalTime t12Midnight = LocalTime.MIDNIGHT;
    System.out.println(t12Midnight);

Output is:

12:00
00:00

A LocalTime is a time of day without a date.

If you have already got a time and only want to adjust the hour and AM/PM, use with():

    LocalTime t12Noon = LocalTime.now(ZoneId.systemDefault())
            .with(ChronoField.CLOCK_HOUR_OF_AMPM, 12)
            .with(ChronoField.AMPM_OF_DAY, 1); // 1 = PM
    System.out.println(t12Noon);
    
    LocalTime t12Midnight = LocalTime.now(ZoneId.systemDefault())
            .with(ChronoField.CLOCK_HOUR_OF_AMPM, 12)
            .with(ChronoField.AMPM_OF_DAY, 0); // 0 = AM
    System.out.println(t12Midnight);
12:47:00.665155
00:47:00.669248

If you need the date too, use ZonedDateTime or another appropriate class. All of the date-time classes of java.time that include time of day have the same with method, so the code will be the same.

If you indispensably need a Calendar object for a legacy API that you cannot afford to upgrade to java.time just now, use a ZonedDateTIme from java.time for your time math. Then use GregorianCalendar.from(ZoendDateTIme) for the conversion to a Calendar object.

Links


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...