如何确定在java中通过周数和日期编号(周)的日期

时间:2017-07-03 20:41:10

标签: java date calendar gregorian-calendar date-parsing

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

  1. 周数(一个月内),即1,2,3,4,5
  2. 天数(一周)0(星期日)至6(星期六)
  3. 我在Calendar类中查找了一个构造函数,但不包含这些参数。

3 个答案:

答案 0 :(得分:2)

尽管你的标签我同意Joe C的评论,你应该更喜欢现代的Java日期和时间API(AKA称为java.time或JSR-310)而不是过时的Calendar和朋友。现代课程更友好,更易于使用,它们带来更少的惊喜,导致更清晰和更自然的代码(至少当你习惯了流畅的风格),并且如果你犯错误就更容易调试。 / p>

我不知道你如何计算这个月的周数。我假设第一周从本月的第一周开始并持续7天(所以第二周从第8周开始,第5周从第29天开始)。如果您的计算方式不同,请参阅Hugo’s answer

我建议这种方法:

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

Mon Jul 03 00:00:00 CEST 2017

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

答案 1 :(得分:1)

要使用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());

答案 2 :(得分:1)

作为@Ole V.V. explained,您需要定义一周开始的日期,以及第一周必须考虑的天数。

例如,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。它更容易,less bugged and less error-prone than the old APIs

如果您使用的是 Java< = 7 ,则可以使用ThreeTen Backport,这是Java 8新日期/时间类的绝佳后端。对于 Android ,有ThreeTenABP(更多关于如何使用它here)。

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

我正在使用DateTimeFormatter因为它占用了所有字段(月,年,星期和星期几)并相应地解析了这一天,创建了LocalDate(其中有一天) /月/年字段)。我也在使用WeekFields类,可以将其配置为使用不同的周定义(第一天的第一天和最小天数)

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

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()方法。

作为@Ole V.V. suggested in the comments,也可以在不创建格式化程序的情况下完成:获取当月的第一个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方法的默认配置。