Mike6679
Mike6679

Reputation: 6117

Java Calendar issues setting 12pm

I'm creating a Calendar instance (current time) then setting hour minute and am/pm

Calendar now = Calendar.getInstance();

now.set(Calendar.HOUR,12);
now.set(Calendar.MINUTE,0);
now.set(Calendar.AM_PM,1);

Then if I try to grab the am/pm from the now Calendar instance it incorrectly always set to am and 1 day ahead of now. This only seems to happen with hour 12 and no other hour. What is the issue here? Does the order I set them matter or in the case of hour 12, should I use the 24 hour format to set the 'now' instance? Yes I should have mentioned, this is on Android.

Upvotes: 6

Views: 9775

Answers (3)

Basil Bourque
Basil Bourque

Reputation: 339053

tl;dr

For 12 AM.

LocalTime.MIN.with ( ChronoField.CLOCK_HOUR_OF_AMPM , 12 )
         .with ( ChronoField.AMPM_OF_DAY , 0 );

For 12 PM.

LocalTime.MIN.with ( ChronoField.CLOCK_HOUR_OF_AMPM , 12 )
             .with ( ChronoField.AMPM_OF_DAY , 1 );

Try it live.

Avoid legacy date-time classes

You are using troublesome old date-time classes that are now legacy, supplanted by java.time in Java 8 and later, and in Android supplanted by the adaptation of the java.time back-port.

LocalTime

Whenever possible, use 24-hour time as you mention in your Question. The LocalTime class in java.time does this, using hours 0-23 for hour-of-day.

If forced to use 12-hour clock, one approach is to construct strings and then let LocalTime parse them.

String input = hour + ":" + minute + " " + ( isAM ? "AM" : "PM" ) ;  // Ex: 7:53 AM

DateTimeFormatter

Define a formatting pattern to match with DateTimeFormatter class.

DateTimeFormatter f = DateTimeFormatter.ofPattern( "h:m a" );
LocalTime lt = LocalTime.parse( input , f );

If all you need is time-of-day, you are done.

ZonedDateTime

If you need to associate this time with a date, create a LocalDate object, specify the context of your intended time zone with a ZoneId, and shake to mix into a ZonedDateTime. If your time-of-day is invalid for that date, java.time makes an adjustment as needed. Be sure to study the JavaDoc to understand the behavior of such an adjustment to see if you agree.

LocalDate ld = LocalDate.of( 2017 , Month.MARCH , 4 );
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ); 

TemporalField

The TemporalField interface provides for classes that can alter date-time values. Because java.time uses immutable objects, we instantiate an fresh object based on another object but with a particular field altered. The ChronoField enum provides some handy implementations of this interface.

The HOUR_OF_AMPM object lets us specify the hour-of-day counting 1-12 rather than 0-23. Combine this with AMPM_OF_DAY to specify if that 1-12 hour was meant for the morning (AM = 0) or the evening (PM = 1). You can chain these calls for convenience.

Let's try both AM and PM, passing either a zero or a one in that last argument. First AM, passing a 0.

LocalTime fiveAm = 
    LocalTime.MIN.with ( ChronoField.HOUR_OF_AMPM , 5 )
                 .withMinute ( 23 )
                 .with ( ChronoField.AMPM_OF_DAY , 0 );  // 05:23

And PM, passing a 1.

LocalTime fivePm = 
    LocalTime.MIN.with ( ChronoField.HOUR_OF_AMPM , 5 )
                 .withMinute ( 23 )
                 .with ( ChronoField.AMPM_OF_DAY , 1 );  // 17:23

Dump to console.

System.out.println ( "fiveAm.toString(): " + fiveAm );
System.out.println ( "fivePm.toString(): " + fivePm );

fiveAm.toString(): 05:23

fivePm.toString(): 17:23

That code above uses HOUR_OF_AMPM which counts hours 0-11. Let's try a zero to see the effect.

LocalTime zeroAm = 
    LocalTime.MIN.with ( ChronoField.HOUR_OF_AMPM , 0 )
                 .withMinute ( 23 )
                 .with ( ChronoField.AMPM_OF_DAY , 0 );

LocalTime zeroPm = 
    LocalTime.MIN.with ( ChronoField.HOUR_OF_AMPM , 0 )
                 .withMinute ( 23 )
                 .with ( ChronoField.AMPM_OF_DAY , 1 );

Dump to console.

System.out.println ( "zeroAm.toString(): " + zeroAm );
System.out.println ( "zeroPm.toString(): " + zeroPm );

zeroAm.toString(): 00:23

zeroPm.toString(): 12:23

But the Question wants to use 12, meaning we count hours of the 12-hour clock as 1-12 rather than 0-11. The java.time classes accommodate that as well. Just switch from using HOUR_OF_AMPM to CLOCK_HOUR_OF_AMPM.

LocalTime twelveAm = 
    LocalTime.MIN.with ( ChronoField.CLOCK_HOUR_OF_AMPM , 12 )
                 .withMinute ( 23 )
                 .with ( ChronoField.AMPM_OF_DAY , 0 );

LocalTime twelvePm = 
    LocalTime.MIN.with ( ChronoField.CLOCK_HOUR_OF_AMPM , 12 )
                 .withMinute ( 23 )
                 .with ( ChronoField.AMPM_OF_DAY , 1 );

Dump to console.

System.out.println ( "twelveAm.toString(): " + twelveAm );
System.out.println ( "twelvePm.toString(): " + twelvePm );

twelveAm.toString(): 00:23

twelvePm.toString(): 12:23

Again, I advise you to use 24-hour clock whenever possible. Makes life much easier, works by default with LocalTime class, and avoids the mistakes and confusion that comes from the ambiguity of 12-hour clock.

Live code

You can see all that example code running live at IdeOne.com.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Upvotes: 0

user2938472
user2938472

Reputation: 11

Much better answer for me was by AndroidDev

How to set time to 24 hour format in Calendar S/he says use Calendar.HOUR_OF_DAY so it should be now.set(Calendar.HOUR_OF_DAY, 0);

Upvotes: 1

mprivat
mprivat

Reputation: 21902

It's because each half of the day goes from hour zero to the end of hour 11. Mathematically speaking, there is no 12th hour (even though we use it all the time in real life). So what you mean to set is hour zero in the PM period, so:

    now.set(Calendar.HOUR, 0);
    now.set(Calendar.MINUTE,0);
    now.set(Calendar.AM_PM, Calendar.PM);

By setting the hour to 12, you tell the calendar (right or wrong, you'll have to ask the Oracle engineers) to set it to the 12th hour of the PM period, which winds up being the zero hour of the AM period of the next day.

There are only 12 hours in a half-day period and that count is zero based, so mathematically speaking, there is no 12th hour. It's zero. It is a little counter intuitive because we are used to saying it's 12:30, not 0:30.

Upvotes: 19

Related Questions