【问题标题】:Need to query Calendar instance for Calendar setting to work需要查询日历实例才能使日历设置生效
【发布时间】:2019-07-18 02:01:45
【问题描述】:

我有一个日历实例,它从 12 月的第一天开始。我正在尝试将该日历实例设置为一年中的最后一个星期二。在设置一年中的最后一天和星期几之间的某个地方,Calendar 实例恢复到原始时间:

Calendar cal = ....;
Log.d(TAG, String.format("Starting here %d-%d-%d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)));

cal.set(Calendar.DAY_OF_YEAR, cal.getActualMaximum(Calendar.DAY_OF_YEAR));
cal.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);

Log.d(TAG, String.format("Ending here %d-%d-%d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)));
/* Other stuff */

给我 2017 年和 2018 年的输出:

2017:

Starting here 2017-12-1 
Ending here 2017-11-28 

2018 年:

Starting here 2018-12-1 
Ending here 2018-11-27 

一个不满意的修复

但是,如果我从 Calendar 实例中检索数据,它可以正常工作:

Calendar cal = ....;
Log.d(TAG, String.format("Starting here %d-%d-%d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)));

cal.set(Calendar.DAY_OF_YEAR, cal.getActualMaximum(Calendar.DAY_OF_YEAR));

// Ask the Calendar instance for information
long dummyValue = cal.getTimeInMillis();

cal.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);

Log.d(TAG, String.format("Ending here %d-%d-%d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)));
/* Other stuff */

给我 2017 年和 2018 年的输出:

2017:

Starting here 2017-12-1 
Ending here 2018-1-2 

2018 年:

Starting here 2018-12-1
Ending here 2019-1-1 

是的,我知道第二组值的日期不是上周二。这就是/* Other stuff */ 的用途。

【问题讨论】:

  • 考虑丢弃长期过时且设计不佳的Calendar,并将ThreeTenABP 添加到您的Android 项目中,以便使用java.time,现代Java 日期和时间API。与它一起工作要好得多。我相信观察到的行为已记录在案。 Calendar 只是令人困惑,有时非常令人困惑。

标签: android calendar java.util.calendar


【解决方案1】:

java.time 和 ThreeTenABP

    LocalDate myLocalDate = LocalDate.of(2017, Month.DECEMBER, 1);

    System.out.format("Starting here: %s%n", myLocalDate);
    LocalDate lastTuesdayOfYear = myLocalDate
            .with(TemporalAdjusters.lastDayOfYear())
            .with(TemporalAdjusters.previousOrSame(DayOfWeek.TUESDAY));
    System.out.format("Ending here:   %s%n", lastTuesdayOfYear);

2017 年的产出:

Starting here: 2017-12-01
Ending here:   2017-12-26

2018 年:

Starting here: 2018-12-01
Ending here:   2018-12-25

这个给你一年中的最后一个星期二。不需要再做任何事情了。

问题:我可以在 Android 上使用 java.time 吗?

是的,java.time 在较旧和较新的 Android 设备上运行良好。它只需要至少 Java 6

  • 在 Java 8 及更高版本以及更新的 Android 设备(从 API 级别 26 起)中,现代 API 是内置的。
  • 在 Java 6 和 7 中获得 ThreeTen Backport,这是现代类的后向端口(JSR 310 的 ThreeTen;请参阅底部的链接)。
  • 在(较旧的)Android 上使用 ThreeTen Backport 的 Android 版本。它被称为 ThreeTenABP。并确保从 org.threeten.bp 导入日期和时间类以及子包。

你的代码出了什么问题?

Calendar 类只是令人困惑。在您的情况下,它的行为符合文档。请允许我引用:

日历字段分辨率

从日历字段计算日期和时间时,……可能存在不一致的信息 (如 1996 年 7 月 15 日星期二(公历)——1996 年 7 月 15 日是 实际上是星期一)。 Calendar 将日历字段值解析为 按以下方式确定日期和时间。

如果日历字段值有任何冲突,Calendar 给出 最近设置的日历字段的优先级。这 以下是日历字段的默认组合。这 最近的组合,由最近设置的单曲确定 字段,将被使用。

对于日期字段:

 YEAR + MONTH + DAY_OF_MONTH
 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
 YEAR + DAY_OF_YEAR
 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR

在您的情况下,DAY_OF_WEEK 是最近设置的。 DAY_OF_WEEK 是上述五种组合中的三种。不知道选哪个。它们都不包括DAY_OF_YEAR,您设置的另一个字段,所以DAY_OF_YEAR 没有被使用。这解释了你看到的行为。如果您坚持使用Calendar,您应该可以使用cal.set(Calendar.DAY_OF_WEEK_IN_MONTH, -1);,因为负值从月底开始倒数。

但是,Calendar 不仅设计不佳且令人困惑,而且早已过时。所以考虑改用 java.time。

链接

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-28
    • 1970-01-01
    • 1970-01-01
    • 2012-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多