java 如何确定通过周数和天数(周)的日期

How to determine the date passing the Week Number and Day Number (Week) in java

我需要通过传递这些参数来获取日期

  1. 周数(一个月内)即 1、2、3、4、5
  2. 天数(一周)0(星期日)到 6(星期六)
  3. 月份

我在 Calendar class 中查找构造函数,但不包含这些参数。

要根据年份、周数和星期几创建日期,请使用 java.util.Calendar 实例:

Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 2017);
        cal.set(Calendar.WEEK_OF_YEAR, 26);
        cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);

Calendar转换为java.util.Date

Date date = cal.getTime();

要将 Date 转换为 java.time.LocalDateTime 使用:

LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

尽管你的标签我同意 Joe C 的评论,但你应该更喜欢现代的 Java 日期和时间 API(也称为 java.time 或 JSR-310)早已过时的 Calendar 和朋友们。现代的 类 对程序员更友好,更易于使用,它们带来的惊喜更少,代码更清晰、更自然(至少当你习惯了流畅的风格时),并且更容易调试一个错误。

我不知道你是如何计算一个月的周数的。我假设第一周从该月的第一天开始并持续 7 天(因此第二周从该月的第 8 周开始,第 5 周从该月的第 29 天开始)。如果你算的不一样,请看.

我建议这个方法:

public static LocalDate weekNoAndDayToDate(Year year, Month month, int weekOfMonth, int dayOfWeek) {
    // find day of week: convert from 0 (Sunday) to 6 (Saturday)
    // to DayOfWeek, from 1 (Monday) to 7 (Sunday)
    DayOfWeek dow;
    if (dayOfWeek == 0) { // Sunday
        dow = DayOfWeek.SUNDAY;
    } else {
        dow = DayOfWeek.of(dayOfWeek);
    }
    return year.atMonth(month)
            .atDay(1)
            .with(TemporalAdjusters.nextOrSame(dow))
            .with(ChronoField.ALIGNED_WEEK_OF_MONTH, weekOfMonth);
}

我们可以用这个来做例子

    LocalDate today = weekNoAndDayToDate(Year.of(2017), Month.JULY, 1, 1);

这会产生

2017-07-03

如果您需要 DateGregorianCalendar,例如您无法更改的旧 API,请执行以下操作之一:

    Date oldfashiondDateObject
            = Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
    GregorianCalendar oldfashionedCalendarObject
            = GregorianCalendar.from(today.atStartOfDay(ZoneId.systemDefault()));

在这两种情况下,不同时区的结果会有所不同(旧类的固有问题之一)。在我的电脑上,第一个产生 Date of

Mon Jul 03 00:00:00 CEST 2017

第二个产生 GregorianCalendar 等于同一时间点。

,您需要定义一周从哪一天开始,以及必须将第多少天视为第一周。

例如,ISO-8601 标准认为:

  • 星期一是一周的第一天。
  • 第一周最少天数:标准是第一周至少需要4天

月份分为几个时期,每个时期从定义的一周的第一天开始。同一个月中最早的时期如果少于最小天数则称为周0,如果至少有最小天数则称为周1

根据您定义它们的方式,您可能会得到不同的结果。考虑 2017 年 7 月的日历:

     July 2017
Su Mo Tu We Th Fr Sa
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

如果我们考虑 ISO 的定义,我们有:

  • 第零周 - 2017-07-01 至 2017-07-02
  • 第 1 周:从 2017-07-03 到 2017-07-09
  • 第 2 周:从 2017-07-10 到 2017-07-16
  • 第 3 周:从 2017-07-17 到 2017-07-22
  • 第 4 周:从 2017-07-23 到 2017-07-30
  • 第 5 周:2017-07-31

如果我们将一周的第一天设为星期日,将第一周的最少天数设为 1,则我们有:

  • 第 1 周:2017-07-01
  • 第 2 周:从 2017-07-02 到 2017-07-08
  • 第 3 周:从 2017-07-09 到 2017-07-15
  • 第 4 周:从 2017-07-16 到 2017-07-21
  • 第 5 周:从 2017-07-22 到 2017-07-29
  • 第 6 周:从 2017-07-30 到 2017-07-31

由于已经发布了 Calendar 的解决方案,我将使用新的 API 编写一个解决方案。如果您使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.

如果您使用 Java <= 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it ).

下面的代码适用于两者。 唯一的区别是 包名称 (在 Java 8 中是 java.time 而在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但是 classes 和方法 names 是相同的。

我正在使用 DateTimeFormatter,因为它需要所有字段(月、年、星期几和星期几)并相应地解析日期,创建一个 LocalDate(其中有day/month/year 字段)。我也在使用 WeekFields class,它可以配置为使用不同的周定义(第一天和第一周的最少天数)

还有一点调整将星期日视为零:

public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
    // you can customize your week definition (first day of week and mininum days in first week)
    WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);
    DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        // week of month, using week definition from WeekFields
        .appendValue(wf.weekOfMonth()).appendPattern(" ")
        // day of week
        .appendValue(ChronoField.DAY_OF_WEEK)
        // month and year
        .appendPattern(" M/yyyy")
        // create formatter
        .toFormatter();

    return LocalDate.parse(weekOfMonth + " " +
           // Sunday is 0, adjusting value
           (dayOfWeek == 0 ? 7 : dayOfWeek) + " " + month + "/" + year, fmt);
}

使用此代码(一周从星期日开始,并且需要 2 天才能被视为第一周 - 否则周将为零,如上面的第一个示例):

LocalDate d = getDate(1, 6, 7, 2017);

d 将是 2017-07-08(2017 年 7 月第 1 周的星期六)。

如果要使用 ISO 8601 定义,请使用常量 WeekFields.ISO 而不是使用 WeekFields.of() 方法。


一样,也可以不创建格式化程序来完成:获取月份的第一个dayOfWeek并将其调整为所需的weekOfMonth:

public LocalDate getDate(int weekOfMonth, int dayOfWeek, int month, int year) {
    // you can customize your week definition (first day of week and mininum days in first week)
    WeekFields wf = WeekFields.of(DayOfWeek.SUNDAY, 2);

    // Sunday is 0, adjusting value
    DayOfWeek dow = DayOfWeek.of(dayOfWeek == 0 ? 7 : dayOfWeek);

    // get the first weekday of the month
    LocalDate first = LocalDate.of(year, month, 1).with(TemporalAdjusters.nextOrSame(dow));

    // check in which week this date is
    int week = first.get(wf.weekOfMonth());

    // adjust to weekOfMonth
    return first.plusWeeks(weekOfMonth - week);
}

它的工作方式完全相同,但不需要格式化程序 - 我测试了从 1600 年到 2100 年的日期,它得到了相同的结果。


PS: Calendar 也通过方法 setFirstDayOfWeek()setMinimalDaysInFirstWeek() 进行了类似的配置。您可以检查调用相应 get 方法的默认配置。