Java Calendar 的设置值没有给出预期的日期时间

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20981963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 05:43:08  来源:igfitidea点击:

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

javajava.util.calendar

提问by xirt

I have an hour, minute, date and millisecond timestamp, and am trying to create a Date object representing the time. The timestamp is provided in Eastern Daylight Time.

我有一个小时、分钟、日期和毫秒时间戳,我正在尝试创建一个表示时间的 Date 对象。时间戳以东部夏令时间提供。

In dissecting the problem, I created some simple test code to see what was happening and have observed the following:

在剖析问题时,我创建了一些简单的测试代码来查看发生了什么,并观察到以下内容:

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

    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.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());
    cal.setTimeZone(TimeZone.getTimeZone("EDT"));

    System.out.println("After setting time zone, time is: "+cal.getTime());

This produces the output:

这会产生输出:

After initializing, time is: Tue Jan 07 16:01:59 EST 2014
After clearing, time is: Thu Jan 01 00:00:00 EST 1970
After setting date, time is: Tue Jan 07 16:01:59 EST 2014
After setting hour, time is: Tue Jan 07 04:01:59 EST 2014
After setting minute, time is: Tue Jan 07 04:00:59 EST 2014
After setting second, time is: Tue Jan 07 04:00:00 EST 2014
After setting milliseconds, time is: Tue Jan 07 04:00:00 EST 2014
After setting time zone, time is: Tue Jan 07 04:00:00 EST 2014

However, if I change the code slightly:

但是,如果我稍微更改代码:

boolean print = false;

I get the following (different) result (!)

我得到以下(不同的)结果(!)

After setting time zone, time is: Mon Jan 06 23:00:00 EST 2014

Does anyone know why this is happening?

有谁知道为什么会这样?

采纳答案by matt forsythe

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

您需要先设置时区。请参阅下面的 GregorianCalendar.setTimeZone 的定义:

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;
}

回答by W.K.S

You create an instance of the current date in GMT:

您在 GMT 中创建当前日期的实例:

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

Then you change the time to 4 AM:

然后将时间更改为凌晨 4 点:

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:

之后,您将日期从 GMT 转换为 EST,即 23 00:

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

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

调试器将帮助您查看每一步的这些变化:)

回答by gtgaxiola

From the Calendar Documentation

来自日历文档

Under the Field Manipulationsection:

字段操作部分下:

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.

set(f, value) 将日历字段 f 更改为 value。此外,它还设置了一个内部成员变量来指示日历字段 f 已更改。尽管日历字段 f 会立即更改,但在下一次调用 get()、getTime()、getTimeInMillis()、add() 或 roll() 之前,不会重新计算以毫秒为单位的日历时间值。

因此,多次调用 set() 不会触发多次不必要的计算。作为使用 set() 更改日历字段的结果,其他日历字段也可能会更改,具体取决于日历字段、日历字段值和日历系统。此外,在重新计算日历字段后,get(f) 不一定会返回调用 set 方法设置的值。具体由具体的日历类决定。

回答by Kuba Spatny

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.

然而,正如评论中所说,美国东部时间上午 4 点是晚上 11 点,它正在做它应该做的事情。

And even better solution would be notto use Calendarat all but joda-time for instance.

甚至更好的解决方案是根本不使用Calendar,但例如 joda-time。

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());

回答by fatih

As mentioned by gtgaxiola: From the Calendar Documentation

正如 gtgaxiola 所提到的:来自日历文档

Under the Field Manipulationsection:

字段操作部分下:

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.

set(f, value) 将日历字段 f 更改为 value。此外,它还设置了一个内部成员变量来指示日历字段 f 已更改。尽管日历字段 f 会立即更改,但在下一次调用 get()、getTime()、getTimeInMillis()、add() 或 roll() 之前,不会重新计算以毫秒为单位的日历时间值。

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

问题是您的getTime()调用会重新计算日期,但 setTimeZone(..) 并未将内部成员变量设置isTimeSet为 false。因此,您的第一个输出中的最后一行对您来说是错误的,因为您希望考虑不是时区。