将周跨度作为 Java 中从零开始的日期(0-364 或 0-365)数字对

Get week span as pair of zero-based day-of-year (0-364 or 0-365) numbers in Java

我需要获取周跨度:周开始(月)| 周末(星期日)一年中的天数。

例如5.9.2019,应该是241-247(从0开始的天数)。

然而,我的方法产生了 250 | 249

public static Pair<Integer, Integer> getWeekSpan(Timestamp date) {
        Pair<Integer, Integer> result = new Pair<Integer, Integer>();
        Calendar cal = timestampToCalendar(date);

        // start of week
        cal.set(Calendar.DAY_OF_WEEK, cal.getActualMinimum(Calendar.DAY_OF_WEEK));
        result.setKey(cal.get(Calendar.DAY_OF_YEAR) - 1);

        // end of week
        cal.set(Calendar.DAY_OF_WEEK, cal.getActualMaximum(Calendar.DAY_OF_WEEK));
        result.setValue(cal.get(Calendar.DAY_OF_YEAR) - 1);

        return result;
    }
    public static Calendar timestampToCalendar(Timestamp timestamp) {
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(timestamp.getTime());
        return cal;
    }

直接设置周一和周日解决了这个问题。

public static Pair<Integer, Integer> getWeekSpan(Timestamp date) {
        Pair<Integer, Integer> result = new Pair<Integer, Integer>();
        Calendar cal = timestampToCalendar(date);

        // start of week
        cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        result.setKey(cal.get(Calendar.DAY_OF_YEAR) - 1);

        // end of week
        cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        result.setValue(cal.get(Calendar.DAY_OF_YEAR) - 1);

        return result;
    }

编辑:但是这段代码确实解决了最初的问题,它没有解决时区问题并且使用了弃用的类。有关详细信息,请参阅其他答案。

java.time

public static Pair<Integer, Integer> getWeekSpan(LocalDate date) {
    return new Pair<>(getZeroBasedDayOfYear(date.with(DayOfWeek.MONDAY)),
            getZeroBasedDayOfYear(date.with(DayOfWeek.SUNDAY)));
}

private static int getZeroBasedDayOfYear(LocalDate date) {
    return date.getDayOfYear() - 1;
}

输出:

[244, 250]

它与你所说的预期不完全一致,但它确实与我从 运行 你自己的答案中的代码中得到的一致,所以我相信它是正确的。这些数字对应于 2019 年 9 月 2 日星期一和 9 月 8 日星期日。

不知道你的Pairclass有没有二参构造函数。如果没有,我相信您会按照您在问题中所做的相同方式填写这两个整数。

注意一周可能会跨过新年。例如,如果我指定 2019-12-31,我会得到:

[363, 4]

4表示次年1月5日

我的代码依赖于您需要 ISO 周这一事实,其中星期一是一周的第一天。如果需要,我们可以通过 WeekFields 对象引入更多的灵活性:

public static Pair<Integer, Integer> getWeekSpan(LocalDate date) {
    WeekFields wf = WeekFields.ISO;
    return new Pair<>(getZeroBasedDayOfYear(date.with(wf.dayOfWeek(), 1)),
            getZeroBasedDayOfYear(date.with(wf.dayOfWeek(), 7)));
}

只要我用WeekFields.ISO,结果还是一样的,不过这里是你可以插的地方,比如WeekFields.of(Locale.getDefault())如果你想使用默认的周定义语言环境。

您使用的 classes,TimestampCalendar,设计不佳且早已过时。我建议你不要使用它们。相反,我使用 java.time 中的 LocalDate,现代 Java 日期和时间 API。

如果您从遗留的 API 中获得了一个您现在无法更改或不想升级的 Timestamp 对象,转换为 LocalDate 的方法是:

    LocalDate convertedLocalDate = yourTimestamp.toInstant()
            .atZone(ZoneId.systemDefault())
            .toLocalDate();

你的代码出了什么问题?

这是 Calendar class 变得令人困惑的地方之一。它从星期日的 1 到星期六的 7 对一周中的几天进行编号。因此,当您询问实际的最小值和最大值时,星期日得到 1,星期六得到 7 ,即使这不是一周的第一天和最后一天 。所以发生的事情是您首先将星期几设置为星期日,从而产生 9 月 8 日,因为您的一周从星期一开始。顺便说一句,Calendar.getInstance() 从您的默认语言环境中获取周定义,这无法从代码中读取并且可能会使许多人感到困惑。如果有一天您的代码在具有不同默认语言环境的 JVM 上运行并且突然表现不同,则更是如此。接下来,当将星期几设置为最大值 7 时,您得到了 9 月 7 日星期六。日期被正确转换为 250 和 249。

Link

Oracle tutorial: Date Time 解释如何使用 java.time.

在传统日期时间和现代日期时间之间转换 classes

如果因为与尚未更新到 java.time classes 的旧代码互操作而必须从 java.sql.Timestamp 开始,您可以来回转换。

Instant

您会发现新的 to…/from… 转换方法已添加到旧的 class 中。 java.sql.Timestamp class offers the toInstant method to render a java.time.Instant object. Both the old and new classes represent a moment in UTC with a resolution of nanoseconds

Instant instant = myJavaUtilTimestamp.toInstant() ;

下一个问题是 UTC, a point on the timeline. But your business problem requires a date on the calendar. The trick is that for any given moment the date varies around the globe by time zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec

ZonedDateTime

因此您必须指定要确定日期的时区。应用 ZoneId to get a ZonedDateTime 对象。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

LocalDate

zdt 代表与 instant 相同的时刻。所不同的是,zdt 是通过该时区地区的人们使用的偏移量来查看的。因此,我们可以从那个 ZonedDateTime 中提取日期(年-月-日),正如魁北克人当时看到的那样,给出这个例子。仅日期值由 LocalDate class 表示,没有时间和时区。

LocalDate localDate = zdt.toLocalDate() ;

现在,有了这个 localDate 对象,您可以按照 Ole V.V 的 中的说明进行操作 V.V。