Setting values of Java Calendar does not give expected date-time

Deadly 提交于 2019-11-30 17:50:33

You need to set the time zone first. See the definition of GregorianCalendar.setTimeZone below:

public void setTimeZone(TimeZone value)
{
    zone = value;
    sharedZone = false;
    /* Recompute the fields from the time using the new zone.  This also
     * works if isTimeSet is false (after a call to set()).  In that case
     * the time will be computed from the fields using the new zone, then
     * the fields will get recomputed from that.  Consider the sequence of
     * calls: cal.setTimeZone(EST); cal.set(HOUR, 1); cal.setTimeZone(PST).
     * Is cal set to 1 o'clock EST or 1 o'clock PST?  Answer: PST.  More
     * generally, a call to setTimeZone() affects calls to set() BEFORE AND
     * AFTER it up to the next call to complete().
     */
    areAllFieldsSet = areFieldsSet = false;
}

As mentioned by gtgaxiola: From the Calendar Documentation

Under the Field Manipulation section:

set(f, value) changes calendar field f to value. In addition, it sets an internal member variable to indicate that calendar field f has been changed. Although calendar field f is changed immediately, the calendar's time value in milliseconds is not recomputed until the next call to get(), getTime(), getTimeInMillis(), add(), or roll() is made.

The problem is that your getTime() call recomputes the date but setTimeZone(..) doesn't set the internal member variable isTimeSet to false. So the last line in your first output is wrong for you because you expect the timezone to be considered which is not.

From the Calendar Documentation

Under the Field Manipulation section:

set(f, value) changes calendar field f to value. In addition, it sets an internal member variable to indicate that calendar field f has been changed. Although calendar field f is changed immediately, the calendar's time value in milliseconds is not recomputed until the next call to get(), getTime(), getTimeInMillis(), add(), or roll() is made.

Thus, multiple calls to set() do not trigger multiple, unnecessary computations. As a result of changing a calendar field using set(), other calendar fields may also change, depending on the calendar field, the calendar field value, and the calendar system. In addition, get(f) will not necessarily return value set by the call to the set method after the calendar fields have been recomputed. The specifics are determined by the concrete calendar class.

I would simply set the time zone first:

 Calendar cal = GregorianCalendar.getInstance();

    cal.clear();
    cal.setTimeZone(TimeZone.getTimeZone("EDT"));
    cal.setTime(today);
    cal.set(Calendar.HOUR_OF_DAY,hour);
    cal.set(Calendar.MINUTE,min);
    cal.set(Calendar.SECOND,sec);
    cal.set(Calendar.MILLISECOND,ms);

However it was doing what it should, as said in the comments 4am is 11pm in EST.

And even better solution would be not to use Calendar at all but joda-time for instance.

EDIT: This produces the right time for me.

    Date today = new Date();
    int hour = 4, min  = 0, sec  = 0, ms   = 64;
    boolean print = false;

    Calendar cal = GregorianCalendar.getInstance();
    if(print)
        System.out.println("After initializing, time is: "+cal.getTime());
    cal.clear();
    if(print)
        System.out.println("After clearing, time is: "+cal.getTime());
    cal.setTimeZone(TimeZone.getTimeZone("EDT"));
    if(print)
        System.out.println("After setting time zone, time is: "+cal.getTime());
    cal.setTime(today);
    if(print)
        System.out.println("After setting date, time is: "+cal.getTime());
    cal.set(Calendar.HOUR_OF_DAY,hour);
    if(print)
        System.out.println("After setting hour, time is: "+cal.getTime());
    cal.set(Calendar.MINUTE,min);
    if(print)
        System.out.println("After setting minute, time is: "+cal.getTime());
    cal.set(Calendar.SECOND,sec);
    if(print)
        System.out.println("After setting second, time is: "+cal.getTime());
    cal.set(Calendar.MILLISECOND,ms);
    if(print)
        System.out.println("After setting milliseconds, time is: "+cal.getTime());

    System.out.println("TIME: "+cal.getTime());

You create an instance of the current date in GMT:

Calendar cal = GregorianCalendar.getInstance();
cal.setTime(today);

Then you change the time to 4 AM:

cal.set(Calendar.HOUR_OF_DAY,hour);
cal.set(Calendar.MINUTE,min);
cal.set(Calendar.SECOND,sec);

Afterwards, you convert the date from GMT to EST which is 23 00:

cal.setTimeZone(TimeZone.getTimeZone("EDT"));

A debugger will help you see these changes each step of the way :)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!