java 什么是计算 ISO 8601 周数的简单好方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/147178/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 11:12:06  来源:igfitidea点击:

What is a good, simple way to compute ISO 8601 week number?

javadate

提问by user9511

Suppose I have a date, i.e. year, month and day, as integers. What's a good (correct), concise and fairly readable algorithm for computing the ISO 8601week numberof the week the given date falls into? I have come across some truly horrendous code that makes me think surely there must be a better way.

假设我有一个日期,即年、月和日,作为整数。什么是用于计算给定日期所属的一周的ISO 8601周数的好的(正确的)、简洁且可读的算法?我遇到了一些真正可怕的代码,这让我觉得肯定有更好的方法。

I'm looking to do this in Java but psuedocode for any kind of object-oriented language is fine.

我希望在 Java 中做到这一点,但任何类型的面向对象语言的伪代码都很好。

回答by Basil Bourque

tl;dr

tl;博士

LocalDate.of( 2015 , 12 , 30 )
         .get ( 
             IsoFields.WEEK_OF_WEEK_BASED_YEAR 
         )

53

53

…or…

…或者…

org.threeten.extra.YearWeek.from (
    LocalDate.of( 2015 , 12 , 30 )
)

2015-W53

2015-W53

java.time

时间

Support for the ISO 8601 weekis now built into Java 8 and later, in the java.timeframework. Avoid the old and notoriously troublesome java.util.Date/.Calendar classes as they have been supplanted by java.time.

ISO 8601 周的支持现已内置到 Java 8 及更高版本的java.time框架中。避免使用旧的、臭名昭著的麻烦 java.util.Date/.Calendar 类,因为它们已被 java.time 取代。

These new java.time classes include LocalDatefor date-only value without time-of-day or time zone. Note that you must specify a time zone to determine ‘today' as the date is not simultaneously the same around the world.

这些新的 java.time 类包括LocalDate用于没有时间或时区的仅日期值。请注意,您必须指定一个时区来确定“今天”,因为世界各地的日期不同。

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );

Or specify the year, month, and day-of-month as suggested in the Question.

或者按照问题中的建议指定年、月和月日。

LocalDate localDate = LocalDate.of( year , month , dayOfMonth );

The IsoFieldsclass provides info according to the ISO 8601 standard including the week-of-year for a week-based year.

IsoFields等级符合ISO 8601标准包括基于星期年周的一年中提供的信息。

int calendarYear = now.getYear();
int weekNumber = now.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
int weekYear = now.get ( IsoFields.WEEK_BASED_YEAR ); 

Near the beginning/ending of a year, the week-based-year may be ±1 different than the calendar-year. For example, notice the difference between the Gregorian and ISO 8601 calendars for the end of 2015: Weeks 52 & 1 become 52 & 53.

在接近一年的开始/结束时,基于周的年可能与日历年相差 ±1。例如,请注意 2015 年底公历和 ISO 8601 日历之间的差异:第 52 周和第 1 周变为第 52 周和第 53 周。

enter image description here

在此处输入图片说明

enter image description here

在此处输入图片说明

ThreeTen-Extra — YearWeek

三十额外 — YearWeek

The YearWeekclass represents both the ISO 8601 week-based year number andthe week number together as a single object. This class is found in the ThreeTen-Extraproject. The project adds functionality to the java.time classes built into Java.

YearWeek类代表ISO 8601基于本周年数都周数作为一个单一的对象。此类可在ThreeTen-Extra项目中找到。该项目为 Java 内置的 java.time 类添加了功能。

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
YearWeek yw = YearWeek.now( zoneId ) ;

Generate a YearWeekfrom a date.

YearWeek从日期生成一个。

YearWeek yw = YearWeek.from (
    LocalDate.of( 2015 , 12 , 30 )
)

This class can generate and parse strings in standard ISO 8601 format.

此类可以生成和解析标准 ISO 8601 格式的字符串。

String output = yw.toString() ;

2015-W53

2015-W53

YearWeek yw = YearWeek.parse( "2015-W53" ) ;

You can extract the week number or the week-based-year number.

您可以提取周数或基于周的年份数。

int weekNumber = yw.getWeek() ;
int weekBasedYearNumber = yw.getYear() ;

You can generate a particular date (LocalDate) by specifying a desired day-of-week to be found within that week. To specify the day-of-week, use the DayOfWeekenum built into Java 8 and later.

您可以LocalDate通过指定要在该周内找到的所需星期几来生成特定日期 ( )。要指定星期几,请使用DayOfWeekJava 8 及更高版本中内置的枚举。

LocalDate ld = yw.atDay( DayOfWeek.WEDNESDAY ) ;


About java.time

关于java.time

The java.timeframework is built into Java 8 and later. These classes supplant the troublesome old legacydate-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是JSR 310

The Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses.

现在处于维护模式Joda-Time项目建议迁移到java.time类。

You may exchange java.timeobjects directly with your database. Use a JDBC drivercompliant with JDBC 4.2or later. No need for strings, no need for java.sql.*classes.

您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC 驱动程序。不需要字符串,不需要类。java.sql.*

Where to obtain the java.time classes?

从哪里获得 java.time 类?

The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多

回答by technophile

I believe you can use the Calendar object (just set FirstDayOfWeek to Monday and MinimalDaysInFirstWeek to 4 to get it to comply with ISO 8601) and call get(Calendar.WEEK_OF_YEAR).

我相信您可以使用 Calendar 对象(只需将 FirstDayOfWeek 设置为 Monday 并将 MinimalDaysInFirstWeek 设置为 4 以使其符合 ISO 8601)并调用 get(Calendar.WEEK_OF_YEAR)。

回答by DoctorBug

/* Build a calendar suitable to extract ISO8601 week numbers
 * (see http://en.wikipedia.org/wiki/ISO_8601_week_number) */
Calendar calendar = Calendar.getInstance();
calendar.setMinimalDaysInFirstWeek(4);
calendar.setFirstDayOfWeek(Calendar.MONDAY);

/* Set date */
calendar.setTime(date);

/* Get ISO8601 week number */
calendar.get(Calendar.WEEK_OF_YEAR);

回答by skaffman

The joda-time library has an ISO8601 calendar, and provides this functionality:

joda-time 库有一个 ISO8601 日历,并提供以下功能:

http://joda-time.sourceforge.net/cal_iso.html

http://joda-time.sourceforge.net/cal_iso.html

yyyy-Www-dTHH:MM:SS.SSS This format of ISO8601 has the following fields:

* four digit weekyear, see rules below
* two digit week of year, from 01 to 53
* one digit day of week, from 1 to 7 where 1 is Monday and 7 is Sunday
* two digit hour, from 00 to 23
* two digit minute, from 00 to 59
* two digit second, from 00 to 59
* three decimal places for milliseconds if required

Weeks are always complete, and the first week of a year is the one that includes the first Thursday of the year. This definition can mean that the first week of a year starts in the previous year, and the last week finishes in the next year. The weekyear field is defined to refer to the year that owns the week, which may differ from the actual year.

yyyy-Www-dTHH:MM:SS.SSS 这种格式的 ISO8601 有以下字段:

* four digit weekyear, see rules below
* two digit week of year, from 01 to 53
* one digit day of week, from 1 to 7 where 1 is Monday and 7 is Sunday
* two digit hour, from 00 to 23
* two digit minute, from 00 to 59
* two digit second, from 00 to 59
* three decimal places for milliseconds if required

周总是完整的,一年的第一周是包括一年的第一个星期四的那一周。这个定义可能意味着一年的第一周从前一年开始,最后一周在下一年结束。weekyear 字段被定义为指拥有周的年份,这可能与实际年份不同。

The upshot of all that is, that you create a DateTime object, and call the rather confusingly (but logically) named getWeekOfWeekyear(), where a weekyear is the particular week-based definition of a year used by ISO8601.

所有这一切的结果是,您创建一个 DateTime 对象,并调用相当混乱(但在逻辑上)命名的 getWeekOfWeekyear(),其中 weekyear 是 ISO8601 使用的特定基于周的年份定义。

In general, joda-time is a fantastically useful API, I've stopped using java.util.Calendar and java.util.Date entirely, except for when I need to interface with an API that uses them.

一般来说,joda-time 是一个非常有用的 API,我已经完全停止使用 java.util.Calendar 和 java.util.Date,除非我需要与使用它们的 API 交互。

回答by istovatis

Just the Java.util.Calendar can do the trick: You can create a Calendar instance and set the First Day Of the Weekand the Minimal Days In First Week

只有 Java.util.Calendar 可以做到这一点:您可以创建一个 Calendar 实例并设置一周第一天和第一周最少天数

Calendar calendar = Calendar.getInstance();
calendar.setMinimalDaysInFirstWeek(4);
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setTime(date);

// Now you are ready to take the week of year.
calendar.get(Calendar.WEEK_OF_YEAR);

This is provided by the javaDoc

这是由javaDoc提供的

The week determination is compatible with the ISO 8601standard when getFirstDayOfWeek() is MONDAY and getMinimalDaysInFirstWeek() is 4, which values are used in locales where the standard is preferred. These values can explicitly be set by calling setFirstDayOfWeek() and setMinimalDaysInFirstWeek().

当 getFirstDayOfWeek() 为 MONDAY 且 getMinimalDaysInFirstWeek() 为 4 时,周确定与ISO 8601标准兼容,这些值用于首选标准的区域设置。这些值可以通过调用 setFirstDayOfWeek() 和 setMinimalDaysInFirstWeek() 显式设置。

回答by Mike Gleason

The Calendar class almost works, but the ISO week-based year does not coincide with what an "Olson's Timezone package" compliant system reports. This example from a Linux box shows how a week-based year value (2009) can differ from the actual year (2010):

Calendar 类几乎可以工作,但基于 ISO 周的年份与“奥尔森时区包”兼容系统报告的内容不一致。这个来自 Linux box 的示例显示了基于周的年份值 (2009) 与实际年份 (2010) 有何不同:

$ TZ=UTC /usr/bin/date --date="2010-01-01 12:34:56" "+%a %b %d %T %Z  %%Y=%Y,%%G=%G  %%W=%W,%%V=%V  %s"
Fri Jan 01 12:34:56 UTC  %Y=2010,%G=2009  %W=00,%V=53  1262349296

But Java's Calendar class still reports 2010, although the week of the year is correct. The Joda-Time classes mentioned by skaffmando handle this correctly:

但是 Java 的 Calendar 类仍然报告 2010,尽管一年中的一周是正确的。skaffman提到的 Joda-Time 类确实正确处理了这个问题:

import java.util.Calendar;
import java.util.TimeZone;
import org.joda.time.DateTime;

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTimeInMillis(1262349296 * 1000L);
cal.setMinimalDaysInFirstWeek(4);
cal.setFirstDayOfWeek(Calendar.MONDAY);
System.out.println(cal.get(Calendar.WEEK_OF_YEAR));     // %V
System.out.println(cal.get(Calendar.YEAR));             // %G

DateTime dt = new DateTime(1262349296 * 1000L);
System.out.println(dt.getWeekOfWeekyear());             // %V
System.out.println(dt.getWeekyear());                   // %G

Running that program shows:

运行该程序显示:

53 2010 53 2009

53 2010 53 2009

So the ISO 8601 week number is correct from Calendar, but the week-based year is not.

因此,日历中的 ISO 8601 周数是正确的,但基于周的年份则不正确。

The man page for strftime(3) reports:

strftime(3) 的手册页报告:

  %G     The ISO 8601 week-based year (see NOTES) with century as  a  decimal  number.   The
         4-digit year corresponding to the ISO week number (see %V).  This has the same for‐
         mat and value as %Y, except that if the ISO week number belongs to the previous  or
         next year, that year is used instead. (TZ)
  %G     The ISO 8601 week-based year (see NOTES) with century as  a  decimal  number.   The
         4-digit year corresponding to the ISO week number (see %V).  This has the same for‐
         mat and value as %Y, except that if the ISO week number belongs to the previous  or
         next year, that year is used instead. (TZ)

回答by Andrew Harmel-Law

If you want to be on the bleeding edge, you can take the latest drop of the JSR-310 codebase(Date Time API) which is led by Stephen Colebourne (of Joda Fame). Its a fluent interface and is effectively a bottom up re-design of Joda.

如果您想处于最前沿,您可以使用由 Stephen Colebourne(Joda Fame 的)领导的 JSR-310 代码库(日期时间 API)的最新版本。它是一个流畅的界面,实际上是对 Joda 的自下而上的重新设计。

回答by Andrew Harmel-Law

this is the reverse: gives you the date of the monday of the week (in perl)

这是相反的:给你一周中星期一的日期(在 perl 中)

use POSIX qw(mktime);
use Time::localtime;

sub monday_of_week {
    my $year=shift;
    my $week=shift;
    my $p_date=shift;

    my $seconds_1_jan=mktime(0,0,0,1,0,$year-1900,0,0,0);
    my $t1=localtime($seconds_1_jan);
    my $seconds_for_week;
    if (@$t1[6] < 5) {
#first of january is a thursday (or below)
        $seconds_for_week=$seconds_1_jan+3600*24*(7*($week-1)-@$t1[6]+1);
    } else {
        $seconds_for_week=$seconds_1_jan+3600*24*(7*($week-1)-@$t1[6]+8);
    }
    my $wt=localtime($seconds_for_week);
    $$p_date=sprintf("%02d/%02d/%04d",@$wt[3],@$wt[4]+1,@$wt[5]+1900);
}