【问题标题】:How to determine the date passing the Week Number and Day Number (Week) in java如何在java中确定通过周数和天数(周)的日期
【发布时间】:2017-07-03 20:41:09
【问题描述】:

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

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

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

【问题讨论】:

  • 没有一个月就不能得到日期。
  • 如果可以避免使用java.util.Calendar 类,我再强烈建议不要使用它。您应该查看 java.time 包并使用适合您用例的类。
  • 您每月的周数是如何计算的?新的一周是从星期日开始,还是从每月 8 日开始,还是?
  • Calendar 早已被 java.time 类所取代。避免使用那些麻烦、设计不佳和有缺陷的遗留日期时间类。

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


【解决方案1】:

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

我不知道你是如何计算每个月的周数的。我假设第一周从每月的第一天开始并持续 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

【讨论】:

    【解决方案2】:

    要根据年份、周数和星期几创建日期,请使用 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());
    

    【讨论】:

    • 我用 WEEK OF MONTH 和 MONTH 替换了 WEEK OF YEAR 并且没问题。结果相同。谢谢!
    • 如果转换成java.time,使用InstantZonedDateTime而不是LocalDateTime。故意丢弃时区/偏移信息是没有意义的。此外,您应该避免麻烦的旧日期时间类,因为添加了 java.time 以完全取代它们。在 2017 年,Ole V.V. 的其他答案或者 Hugo 应该被接受而不是这个。
    • 我遇到了和@Newb一样的问题,这个例子不能解决这个大问题!
    【解决方案3】:

    作为@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 年 7 月 10 日到 2017 年 7 月 16 日
    • 第 3 周:从 2017 年 7 月 17 日到 2017 年 7 月 22 日
    • 第 4 周:从 2017 年 7 月 23 日到 2017 年 7 月 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 年 7 月 16 日到 2017 年 7 月 21 日
    • 第 5 周:从 2017 年 7 月 22 日到 2017 年 7 月 29 日
    • 第 6 周:从 2017 年 7 月 30 日到 2017 年 7 月 31 日

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

    如果您使用的是 Java ,则可以使用 ThreeTen Backport,它是 Java 8 新日期/时间类的出色反向移植。对于Android,还有ThreeTenABP(更多关于如何使用它here)。

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

    我使用的是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 方法检查默认配置。

    【讨论】:

    • 啊,当然是WeekFields 类。谢谢。不构建字符串并解析它就不能完成吗?我想我会尝试使用WeekFields.weekOfMonth() 获取每月第一个星期四的周数(例如;需要星期几)。然后使用LocalDate.plusWeeks() 添加适当的周数。
    • @OleV.V.我还没有尝试过没有格式化程序。让我们看看它是否有效,如果我找到方法,我会更新答案。谢谢! - 我使用了格式化程序,因为它已经根据其他字段解析了日期,但让我们看看是否可以通过其他方式完成......
    • @OleV.V.完毕!确实,它比使用格式化程序要好得多,非常感谢!
    • 我非常喜欢你和我之间的合作,Hugo。两个头脑比一个头脑想得更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-04
    • 2021-08-24
    • 2022-11-22
    相关资源
    最近更新 更多