【问题标题】:Setting values of Java Calendar does not give expected date-timeJava Calendar 的设置值没有给出预期的日期时间
【发布时间】:2014-01-25 17:53:39
【问题描述】:

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

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

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

这会产生输出:

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

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

boolean print = false;

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

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

有人知道为什么会这样吗?

【问题讨论】:

  • 我相信格林威治标准时间凌晨 4 点等于美国东部标准时间晚上 11 点。
  • 我怀疑这是由于内部字段等待重新计算,这是由打印触发的。但是,这只是 另一个 避免使用 java.util.Calendar 的原因。

标签: java java.util.calendar


【解决方案1】:

来自Calendar Documentation

字段操作部分下:

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

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

【讨论】:

    【解决方案2】:

    正如 gtgaxiola 所说:来自Calendar Documentation

    字段操作部分下:

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

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

    【讨论】:

      【解决方案3】:

      您需要先设置时区。见下面 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;
      }
      

      【讨论】:

        【解决方案4】:

        我会先设置时区:

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

        但是它正在做它应该做的事情,正如 cmets 中所说,美国东部标准时间凌晨 4 点是晚上 11 点。

        更好的解决方案是使用Calendar,例如joda-time。

        编辑:这为我创造了正确的时间。

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

        【讨论】:

        • 是的。设置时区首先停止不一致的行为。现在无论 print=true 还是 print=false,它都会产生相同的结果。但是,它仍然会产生 23:00 作为时间......(!)
        • @user3170794 所以你设置 timeZone.. 将时间设置为 4:00 并打印 23:00?
        • 时间是:Sun Jan 12 23:00:00 EST 2014
        • @xirt 我真的不知道你用了什么.. 但试试我添加的代码
        • 说真的,我只是将上面的代码包装在一个 main 方法中并得到:“TIME: Sun Jan 12 23:00:00 EST 2014”。我现在在美国东部时间。我正在使用 Java 1.7.0_03。
        【解决方案5】:

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

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

        然后你把时间改成凌晨 4 点:

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

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

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

        调试器将帮助您在每个步骤中查看这些更改 :)

        【讨论】:

          猜你喜欢
          • 2023-03-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-08-15
          • 1970-01-01
          相关资源
          最近更新 更多