【问题标题】:Calendar get year returns wrong result日历获取年份返回错误结果
【发布时间】:2016-04-11 16:54:33
【问题描述】:

我想创建一个日历对象并将其设置为某一年和该年的某一周。

Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.WEEK_OF_YEAR, weekOfYear);  // 1
calendar.set(Calendar.YEAR, year); // 2016
setWeekChecked(calendar);

这是我将日历对象传递给 setWeekChecked 方法时的 toString:

java.util.GregorianCalendar[time=?,areFieldsSet=false,lenient=true,zone=America/New_York,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=0,WEEK_OF_YEAR=1 ,WEEK_OF_MONTH=2,DAY_OF_MONTH=7,DAY_OF_YEAR=7,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=5,HOUR_OF_DAY=5,MINUTE=25,SECOND=43,MILLISECOND=219,ZONE_OFFSET=-18000000, DST_OFFSET=0]

在 setWeekChecked 方法中:

public void setWeekChecked(final Calendar cal) {
    final int targetWeek = cal.get(Calendar.WEEK_OF_YEAR); // Returns 1
    final int targetYear = cal.get(Calendar.YEAR); // Returns 2015??
}

这是现在日历对象的 toString:

java.util.GregorianCalendar[time=1451557543219,areFieldsSet=true,lenient=true,zone=America/New_York,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=11,WEEK_OF_YEAR=1 ,WEEK_OF_MONTH=5,DAY_OF_MONTH=31,DAY_OF_YEAR=365,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=5,HOUR_OF_DAY=5,MINUTE=25,SECOND=43,MILLISECOND=219,ZONE_OFFSET=-18000000, DST_OFFSET=0]

我做错了什么?

【问题讨论】:

  • 我不知道这个问题,但通常在日历教程中我看到 +1 带有月份和年份字段
  • @VivekMishra:没有年份...
  • 是的,月份从索引 0 开始
  • 所以年份也可能是这种情况
  • @VivekMishra:但事实并非如此。恐怕在这一点上猜测没有帮助。

标签: java android calendar


【解决方案1】:

tl;博士

LocalDate.of( 2016 , Month.JULY , 1 ) 
         .with( IsoFields.WEEK_OF_WEEK_BASED_YEAR , 1 ) 

详情

Answer by Jon Skeet 是正确的。更新:我们有更好的方法。

Java 8 和更高版本中内置的 java.time 类取代了与 Java 的最早版本捆绑在一起的麻烦的旧旧日期时间类。并且 java.time 正式取代了 Joda-Time,因为该项目现在处于维护模式。

正如 Skeet 所指出的,有不同的方式来定义一年中的一周。

java.time 类为week-of-year 的标准ISO 8601 定义提供支持。这个定义是第 1 周是第一周有星期四,并且该周从星期一开始。因此,一周的开始可能包括上一年的一天或多天,最后一周可能包括下一年的一天或多天。一年总是有 52 或 53 周。

有关更多详细信息,请参阅my Answer 到类似的问题。

LocalDate 类表示没有时间和时区的仅日期值。

获取一个接近所需年份年中的日期。所需的年份是基于周的年份,而不是日历年,因此必须避免日历年的开始或结束。在您的情况下,您希望 2016 年以周为单位。

LocalDate ld = LocalDate.of( 2016 , Month.JULY , 1 ) ;

接下来,我们使用 IsoFields.WEEK_OF_WEEK_BASED_YEAR 将该日期调整为所需的一周。

LocalDate dayIn2016W01 = ld.with( IsoFields.WEEK_OF_WEEK_BASED_YEAR , 1 ) ;

如果您想要该周的第一天,请使用 TemporalAdjusters 类中的另一个 TemporalAdjuster

LocalDate firstDayOf2016W01 = dayIn2016W01.with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) );

提示:当 Android 变得更强大时,请使用 ThreeTen-Extra 项目中的 YearWeek 类。


关于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】:

    我怀疑日历正在尝试使用 2016 年第一周的当前星期几(今天是星期四)。

    现在,查看您的日历设置,您有 firstDayOfWeek=1(所以星期从星期日到星期六)和 minimumDaysInFirstWeek=1(所以一年的第一周是包括 1 月 1 日的那一周)。

    这意味着您日历中 2016 年的第一周从 Decemember 2015 年 27 日到 2016 年 1 月 2 日。因此第一周的星期四是 12 月 31 日 - 这正是您向我们展示的日历所说的。

    从根本上说,“一年中的一周”的日历算术很棘手,因为:

    • 有许多不同的文化特定方式来看待它们
    • 通常需求不会指定您真正感兴趣的那些内容

    如果可能的话,我强烈建议您使用 Joda Time,以使您的日期/时间处理代码从一开始就更清晰,但您仍然需要准确地了解您的意思“将其设置为该年的某一年和某一周”。请注意,Joda Time 将“week-year”(与 week-of-week-year 和 day-of-week 一起使用)与“year”(与 month 和 day-of-month 一起使用)的概念分开,这很有帮助。您需要注意,对于给定日期,周年和年可能不同。

    【讨论】:

    • 很好的解释和正当的理由
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-17
    • 2019-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多