Calendar hour set to 0, yet displays 1, why?

I am running into a strange issue with Java Calendar.

The following code adds 3 hours consecutively from a date starting at midnight.

public static void main(String[] args) {

    Calendar now = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));

    // Setting to January 29th, 1920 at 00:00:00
    // now.setTimeZone(TimeZone.getTimeZone("GMT+0"));
    now.set(Calendar.YEAR, 1920);
    now.set(Calendar.MONTH, 0);
    now.set(Calendar.DAY_OF_MONTH, 29);
    now.set(Calendar.HOUR_OF_DAY, 0);
    now.set(Calendar.MINUTE, 0);
    now.set(Calendar.SECOND, 0);
    now.set(Calendar.MILLISECOND, 0);       

    now.setLenient(false);

    int threeHours = 1000 * 60 * 60 * 3;

    SimpleDateFormat sdf
        = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");

    for (int i=0;i<25;i++) {

        System.out.println(sdf.format(now.getTime()));
        now.add(Calendar.MILLISECOND, threeHours);

    }

}

Yet, the displayed result is:

1920-01-29 01:00:00 000
1920-01-29 04:00:00 000
1920-01-29 07:00:00 000
1920-01-29 10:00:00 000
1920-01-29 13:00:00 000
1920-01-29 16:00:00 000
1920-01-29 19:00:00 000
1920-01-29 22:00:00 000
1920-01-30 01:00:00 000
1920-01-30 04:00:00 000
1920-01-30 07:00:00 000
1920-01-30 10:00:00 000
1920-01-30 13:00:00 000
1920-01-30 16:00:00 000
1920-01-30 19:00:00 000
1920-01-30 22:00:00 000
1920-01-31 01:00:00 000
1920-01-31 04:00:00 000
1920-01-31 07:00:00 000
1920-01-31 10:00:00 000
1920-01-31 13:00:00 000
1920-01-31 16:00:00 000
1920-01-31 19:00:00 000
1920-01-31 22:00:00 000
1920-02-01 01:00:00 000

Why is the first hour 1 and not 0? I am located at GMT+1, could this be related?

Upvotes: 5

Views: 8742

Answers (4)

Basil Bourque
Basil Bourque

Reputation: 338276

java.time

The terrible Calendar class was supplanted years ago by the modern java.time classes defined in JSR 310. Specifically replaced by ZonedDateTime. There is no point to investing time and effort in understanding Calendar — Sun, Oracle, and the JCP community all gave up on that flawed class, and so should we.

Your purpose uses UTC rather than a particular time zone. So OffsetDateTime is appropriate.

// Setting to January 29th, 1920 at 00:00:00 in UTC.
LocalDate ld = LocalDate.of( 1920 , Month.JANUARY , 29 ) ;
LocalTime lt = LocalTime.MIN ;
ZoneOffset offset = ZoneOffset.UTC ;
OffsetDateTime odt = OffsetDateTime.of( ld , lt , offset ) ;

Add 3 hours at a time, 25 times. Use Duration to represent a span-of-time on the scale of hours-minutes-seconds. Pass the Duration object to OffsetDatetime::plus, as a TemporalAmount.

Duration d = Duration.ofHours( 3 ) ;
for( int i = 0 ; i < 25 ; i ++ )
{
    System.out.println( odt ) ; 
    // Set up the next loop.
    odt = odt.plus( d ) ;
}

See this code run live at IdeOne.com.

odt.toString(): 1920-01-29T00:00Z
1920-01-29T00:00Z
1920-01-29T03:00Z
1920-01-29T06:00Z
1920-01-29T09:00Z
1920-01-29T12:00Z
1920-01-29T15:00Z
1920-01-29T18:00Z
1920-01-29T21:00Z
1920-01-30T00:00Z
1920-01-30T03:00Z
1920-01-30T06:00Z
1920-01-30T09:00Z
1920-01-30T12:00Z
1920-01-30T15:00Z
1920-01-30T18:00Z
1920-01-30T21:00Z
1920-01-31T00:00Z
1920-01-31T03:00Z
1920-01-31T06:00Z
1920-01-31T09:00Z
1920-01-31T12:00Z
1920-01-31T15:00Z
1920-01-31T18:00Z
1920-01-31T21:00Z
1920-02-01T00:00Z

Table of all date-time types in Java, both modern and legacy


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.

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

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

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.

Where to obtain the java.time classes?

Upvotes: 1

akjsdhfkds
akjsdhfkds

Reputation: 21

i guess you should use now.set(Calendar.HOUR, 0); instead now.set(Calendar.HOUR_OF_DAY, 0);

Upvotes: 2

MaVRoSCy
MaVRoSCy

Reputation: 17839

now it should work fine

        Calendar now = Calendar.getInstance();
        TimeZone timezone = TimeZone.getTimeZone("GMT+0");
        now.set(Calendar.YEAR, 1920);
        now.set(Calendar.MONTH, 0);
        now.set(Calendar.DAY_OF_MONTH, 29);
        now.set(Calendar.HOUR_OF_DAY, 0);
        now.set(Calendar.MINUTE, 0);
        now.set(Calendar.SECOND, 0);
        now.set(Calendar.MILLISECOND, 0);       
        now.setLenient(false);
        int threeHours = 1000 * 60 * 60 * 3;
        SimpleDateFormat sdf
            = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        sdf.setTimeZone(timezone);
        for (int i=0;i<25;i++) {
            System.out.println(sdf.format(now.getTime()));
            now.add(Calendar.MILLISECOND, threeHours);
        }

Upvotes: 1

Peter Lawrey
Peter Lawrey

Reputation: 533472

Using now.getTime() gets the Date which doesn't have a time zone.

Try setting the timezone with

sdf.setTimeZone(now.getTimeZone());

Upvotes: 4

Related Questions