将周跨度作为 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 日星期日。
不知道你的Pair
class有没有二参构造函数。如果没有,我相信您会按照您在问题中所做的相同方式填写这两个整数。
注意一周可能会跨过新年。例如,如果我指定 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,Timestamp
和 Calendar
,设计不佳且早已过时。我建议你不要使用它们。相反,我使用 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。
我需要获取周跨度:周开始(月)| 周末(星期日)一年中的天数。
例如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 日星期日。
不知道你的Pair
class有没有二参构造函数。如果没有,我相信您会按照您在问题中所做的相同方式填写这两个整数。
注意一周可能会跨过新年。例如,如果我指定 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,Timestamp
和 Calendar
,设计不佳且早已过时。我建议你不要使用它们。相反,我使用 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 的