Java“周年”是如何工作的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34691582/
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-11-02 23:12:20  来源:igfitidea点击:

How does Java "week year" really work?

javasimpledateformat

提问by Dave Mulligan

This started as a simple error: I had YYYYinstead of yyyyin my format string for a SimpleDateFormatobject. But I'm totally baffled by the results of my tests with the incorrect format string.

这开始是一个简单的错误:我有一个对象的YYYY而不是yyyy我的格式字符串SimpleDateFormat。但是我对格式字符串不正确的测试结果感到非常困惑。

This code:

这段代码:

@Test
public void whatTheHell() {
        try {
                SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/YYYY");

                Date d1 = sdf.parse("01/07/2016");
                Date d2 = sdf.parse("02/08/2016");
                Date d3 = sdf.parse("11/29/2027");

                System.out.println(d1.toString());
                System.out.println(d2.toString());
                System.out.println(d3.toString());
        } catch (ParseException pe) {
                fail("ParseException: " + pe.getMessage());
        }
}

produces this output:

产生这个输出:

Sun Dec 27 00:00:00 PST 2015
Sun Dec 27 00:00:00 PST 2015
Sun Dec 27 00:00:00 PST 2026

I've read the documentation on the 'Y' parameter here: https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html, but I still can't see the logic that's working here. Particularly the last instance: I can kinda-sorta understand how the dates in January (& maybe February) can be translated into December of the previous year, but moving the date of November 29th backwards by 11 months baffles me. And what's so special about December 27th?

我在这里阅读了关于“Y”参数的文档:https: //docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html,但我仍然看不到逻辑在这里工作。特别是最后一个例子:我有点理解如何将一月(也许二月)的日期转换为前一年的十二月,但是将 11 月 29 日的日期向后移动 11 个月让我感到困惑。12 月 27 日有什么特别之处?

Can anyone explain?

谁能解释一下?

MORE INFORMATION

更多信息

@Jan suggested that relying on the toString() method could be a problem, so I defined a date format to print YYYY MM dd '-' yyyy MM ddin the same code as above. Here is the additional output:

@Jan 建议依赖 toString() 方法可能是一个问题,所以我定义了一个日期格式以YYYY MM dd '-' yyyy MM dd与上面相同的代码打印。这是额外的输出:

2016 12 27 - 2015 12 27
2016 12 27 - 2015 12 27
2027 12 27 - 2026 12 27

采纳答案by Thomas Kl?ger

It's simple: December 27 2015 is day 1 of week 1 of week-year 2016 (and December 27 2026 is day 1 of week 1 of week-year 2027). This can be verified by adding these lines:

很简单:2015 年 12 月 27 日是 2016 年第 1 周的第 1 天(2026 年 12 月 27 日是 2027 年第 1 周的第 1 天)。这可以通过添加这些行来验证:

SimpleDateFormat odf = new SimpleDateFormat("YYYY-ww-u");
System.out.println(odf.format(d1));
System.out.println(odf.format(d2));
System.out.println(odf.format(d3));

If a SimpleDateFormatoutputs a date it can use all fields: year, month, day, day of week, week of month, week in year, week-year etc.

如果SimpleDateFormat输出日期,则它可以使用所有字段:年、月、日、星期几、一个月中的一周、一年中的一周、一周年等。

On parsing, SimpleDateFormatexpects a matching set of values: either day, month, year orday of week, week in year, week-year. Since you supplied a week-year but did not supply day of week and week in year, those to values have been assumed as 1.

在解析时,SimpleDateFormat需要一组匹配的值:日、月、年星期几、一年中的一周、一周年。由于您提供了一周年但没有提供一周中的某天和一年中的一周,因此这些值已假定为 1。



The actual values depend on your locale:

实际值取决于您的语言环境:

  • which week of a year is week 1
  • which day is the first day of the week
  • 一年中的哪一周是第 1 周
  • 哪一天是一周的第一天

(see https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html#week_and_year)

(参见https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html#week_and_year

On my system (using de-ch locale, with "EEE MMM dd HH:mm:ss zzz yyyy - YYYY-ww-u" as format) I get

在我的系统上(使用 de-ch 语言环境,格式为“EEE MMM dd HH:mm:ss zzz yyyy - YYYY-ww-u”)我得到

Mo Jan 04 00:00:00 MEZ 2016 - 2016-01-1
Mo Jan 04 00:00:00 MEZ 2016 - 2016-01-1
Mo Jan 04 00:00:00 MEZ 2027 - 2027-01-1

回答by Basil Bourque

Definitions vary

定义不同

A week can be defined in different ways. For example, in the United States, the first day of the week is considered to be Sunday most often while in Europe and many other places the first day is Monday.

一周可以用不同的方式定义。例如,在美国,一周的第一天通常被认为是星期日,而在欧洲和许多其他地方,第一天是星期一。

Likewise, the week of a week-based year can also be defined in different ways.

同样,基于周的一年中的周也可以用不同的方式定义。

  • Week # 1 contains January 1.
  • Week # 1 is the first week to contain a particular day-of-week such as Sunday.
  • Week # 1 is the first week to contain only days of the new year, with no dates from the previous year.
  • … and more
  • 第 1 周包含 1 月 1 日。
  • 第 1 周是包含特定星期几(例如星期日)的第一周。
  • 第 1 周是第一周只包含新年的几天,没有上一年的日期。
  • … 和更多

The legacy classes you are using implicitly use the definitions specified by a Locale. The locale being applied is also implicit, using the JVM's current default Localeif you do not otherwise specify.

您正在使用的遗留类隐式使用由Locale. 所应用的语言环境也是隐式的,Locale如果您没有另外指定,则使用 JVM 的当前默认值。

For even more interesting details about the difficulty in defining a week, see this Question, Different behavior of WeekFields on JVM 8 and JVM 10

有关定义一周的难度的更多有趣详细信息,请参阅此问题,JVM 8 和 JVM 10 上 WeekFields 的不同行为

ISO 8601

ISO 8601

There is a practical international standard for date-time handling, ISO 8601.

有一个实用的日期时间处理国际标准ISO 8601

The ISO 8601 definition of a weekis:

一个星期的ISO 8601的定义是:

  • Weeks start on a Monday
  • Week # 1 has the first Thursday of the year
  • A year consists of either 52 or 53 weeks.
  • A year may have one or more days from the previous calendar year, and also from the following calendar year.
  • 每周从星期一开始
  • 第 1 周是一年中的第一个星期四
  • 一年由 52 或 53 周组成。
  • 一年可能有前一个日历年的一天或多天,也可能有下一个日历年。

I suggest using the ISO 8601 standard definition whenever possible. This standard definition is simple and logical, with increasing adoption across industries.

我建议尽可能使用 ISO 8601 标准定义。这个标准定义简单而合乎逻辑,越来越多的行业采用。

java.time

时间

The java.timeclasses offer some support for week of week-based year in the WeekFieldsclass.

java.time类提供的基于周的星期一定的支持WeekFields类。

LocalDate ld = LocalDate.of( 2019 , Month.JANUARY , 1 ) ;
long week = ld.get( WeekFields.ISO.weekOfWeekBasedYear() ) ;

See this code run live at IdeOne.com.

在 IdeOne.com 上查看此代码的实时运行情况

ld.toString(): 2019-01-01

week: 1

ld.toString(): 2019-01-01

周:1

org.threeten.extra.YearWeek

org.threeten.extra.YearWeek

But if doing much of this work, I suggest adding the ThreeTen-Extralibrary to your project. You will find the YearWeekclass to be helpful. This class offers several handy methods such as generating a LocalDatefor any day within that week.

但是,如果要完成大部分工作,我建议将ThreeTen-Extra库添加到您的项目中。你会发现这YearWeek门课很有帮助。这个类提供了几种方便的方法,例如LocalDate为该周内的任何一天生成一个。

LocalDate ld = LocalDate.of ( 2019 , Month.JANUARY , 1 );
YearWeek yw = YearWeek.from ( ld );
LocalDate startOfWeek = yw.atDay ( DayOfWeek.MONDAY );

ld.toString(): 2019-01-01

yw.toString(): 2019-W01

startOfWeek.toString(): 2018-12-31

ld.toString(): 2019-01-01

yw.toString(): 2019-W01

startOfWeek.toString(): 2018-12-31

Notice how the first day of the year in the week-based year of 2019 is a date from the previous calendar year, 2018 rather than 2019.

请注意 2019 年以周为基础的一年中的第一天如何是上一个日历年的日期,即 2018 年而不是 2019 年。

calendar for month of January 2019, with week number, showing first week starts in the previous calendar year on 2018-12-31

2019 年 1 月的日历,带有周数,显示从 2018-12-31 上一个日历年开始的第一周



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 类?