Java 日历 - 一周的第一天 VS 实际最小值

Java Calendar - First day of week VS actual minimum

我正在测试我们的日期管理框架的一部分,它应该重置一些 Calendar fields (using actual minimum)。

然而,当changing first day of week, Calendar always return SUNDAY (1) as actual minimum for day of weeks。这是一个错误吗?

MVCE:

import java.util.*;

public class Main {
  public static void main(String[] args) {
    System.out.printf("Java specification: %s%n", System.getProperty("java.vm.specification.version"));
    System.out.printf("Java version: %s%n", System.getProperty("java.specification.version"));
    System.out.printf("VM version: %s%n", System.getProperty("java.vm.version"));

    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

    int[] days = { 1, 5, 7 };
    for (int day : days) {
      cal.setTimeInMillis(0L);
      cal.setFirstDayOfWeek(day);
      System.out.printf("expected: %d, actual: %d%n", day, cal.getActualMinimum(Calendar.DAY_OF_WEEK));
    }
  }
}

You should use cal.getFirstDayOfWeek() instand of cal.getActualMinimum(Calendar.DAY_OF_WEEK)

因为 cal.getActualMinimum(Calendar.DAY_OF_WEEK) 总是 return 星期天看看 java 源代码

中 getActualMinimum(Calendar.DAY_OF_WEEK) 的实现
  public int getActualMinimum(int field) {
        if (field == DAY_OF_MONTH) {
            GregorianCalendar gc = getNormalizedCalendar();
            int year = gc.cdate.getNormalizedYear();
            if (year == gregorianCutoverYear || year == gregorianCutoverYearJulian) {
                long month1 = getFixedDateMonth1(gc.cdate, gc.calsys.getFixedDate(gc.cdate));
                BaseCalendar.Date d = getCalendarDate(month1);
                return d.getDayOfMonth();
            }
        }
        return getMinimum(field);
    }

而 getMinimum 是:-

   public int getMinimum(int field) {
        return MIN_VALUES[field];
    }

和 MIN_VALUES 数组定义为:-

 static final int MIN_VALUES[] = {
        BCE,            // ERA
        1,              // YEAR
        JANUARY,        // MONTH
        1,              // WEEK_OF_YEAR
        0,              // WEEK_OF_MONTH
        1,              // DAY_OF_MONTH
        1,              // DAY_OF_YEAR
        SUNDAY,         // DAY_OF_WEEK
        1,              // DAY_OF_WEEK_IN_MONTH
        AM,             // AM_PM
        0,              // HOUR
        0,              // HOUR_OF_DAY
        0,              // MINUTE
        0,              // SECOND
        0,              // MILLISECOND
        -13*ONE_HOUR,   // ZONE_OFFSET (UNIX compatibility)
        0               // DST_OFFSET
    };

正确。

问题和答案都使用过时的 classes。与最早版本的 Java 捆绑在一起的旧日期时间 classes 已被 java.time classes.

取代

标准清晰度

DayOfWeek enum defines a week according to the ISO 8601 standard, with Monday being 1 and Sunday 为 7。

你可以向 DayOfWeek 对象询问这个 1-7 的数字。

int dayOfWeekNumber = DayOfWeek.WEDNESDAY.getValue();

但是不要在您的代码周围传递这个数字。而是传递 DayOfWeek 对象以享受 type-safety 的好处并保证有效值。

本地化定义

对于星期几、星期几和一年中的星期字段的本地化定义,请使用 WeekFields class.

WeekFields weekFields_US = WeekFields.of ( Locale.US );
DayOfWeek firstDayOfWeek = weekFields_US.getFirstDayOfWeek ();

转储到控制台。

System.out.println ( "weekFields_US: " + weekFields_US + " | firstDayOfWeek: " + firstDayOfWeek );

weekFields_US: WeekFields[SUNDAY,1] | firstDayOfWeek: SUNDAY

定义您自己的周变化

我建议坚持指定 Locale objects to define the week. But if you insist, you can define your own variation of the week. Call WeekFields.of 并指定 (a) 一周的第一天,以及 (b) 定义一年的第一周或一个月的第一周的最少天数。

首先,为了比较,使用 IsoFields class.

中包含的标准 ISO 8601 定义获取星期几
// Using standard IsoFields.
LocalDate localDate = LocalDate.of ( 2016 , 2 , 3 ); // Feb 3 2016 = Wednesday
DayOfWeek dow = DayOfWeek.from ( localDate );
int ordinalNumberDayOfWeek = dow.getValue ();

转储到控制台。

System.out.println ( "localDate: " + localDate + " | dow: " + dow + " | ordinalNumberDayOfWeek: " + ordinalNumberDayOfWeek );

localDate: 2016-02-03 | dow: WEDNESDAY | ordinalNumberDayOfWeek: 3

现在切换到定义我们自己的 WeekFields 对象,并再次询问星期几。上面我们看到 3 因为 2016 年 2 月 3 日是星期三,所以星期一-星期二-星期三呈现 3。下面我们看到 4 因为周日-周一-周二-周三。

// Define our own WeekFields to use in place of IsoFields.
WeekFields weekFields_SUNDAY_1 = WeekFields.of ( DayOfWeek.SUNDAY , 1 );
int ordinalNumberDayOfWeek_SundayBasedWeek = localDate.get ( weekFields_SUNDAY_1.dayOfWeek () );

转储到控制台。

System.out.println ( "ordinalNumberDayOfWeek_SundayBasedWeek: " + ordinalNumberDayOfWeek_SundayBasedWeek );

ordinalNumberDayOfWeek_SundayBasedWeek: 4

(示例基于 JodaStephen 的


查看类似问题,java get week of year for given a date


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

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

从哪里获得 java.time classes?