【问题标题】:Calendar hour set to 0, yet displays 1, why?日历小时设置为 0,但显示为 1,为什么?
【发布时间】:2012-09-03 07:04:30
【问题描述】:

我在使用 Java 日历时遇到了一个奇怪的问题。

以下代码从午夜开始的日期连续添加 3 小时。

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

    }

}

然而,显示的结果是:

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

为什么第一个小时是 1 而不是 0?我位于 GMT+1,这可能是相关的吗?

【问题讨论】:

  • 你考虑过使用jodatime吗?
  • @BigMike 我在日历中找不到任何与日光相关的方法...

标签: java datetime calendar timezone


【解决方案1】:

java.time

可怕的 Calendar 类在几年前被 JSR 310 中定义的现代 java.time 类所取代。具体由 ZonedDateTime 取代。投入时间和精力去理解 CalendarSunOracleJCP 社区都放弃了这个有缺陷的课程,我们也应该放弃。

您的目的使用 UTC 而不是特定时区。所以OffsetDateTime是合适的。

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

每次增加 3 小时,共 25 次。使用Duration 表示以小时-分钟-秒为单位的时间跨度。将Duration 对象作为TemporalAmount 传递给OffsetDatetime::plus

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

看到这个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


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 & JPA 2.2 支持 java.time

从哪里获取 java.time 类?

【讨论】:

    【解决方案2】:

    我想你应该使用 now.set(日历.HOUR, 0);而是 now.set(Calendar.HOUR_OF_DAY, 0);

    【讨论】:

    • 错了。 HOUR 表示上午或下午之间的小时(除了一般建议,在现代 Java 日期和时间 API java.time 出现后不再使用Calendar)。
    【解决方案3】:

    现在应该可以正常工作了

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

    【讨论】:

      【解决方案4】:

      使用now.getTime() 得到没有时区的Date

      尝试设置时区

      sdf.setTimeZone(now.getTimeZone());
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-03-17
        • 2017-04-18
        • 2014-06-05
        • 1970-01-01
        • 2011-07-19
        • 2021-04-17
        • 1970-01-01
        相关资源
        最近更新 更多