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
How does Java "week year" really work?
提问by Dave Mulligan
This started as a simple error: I had YYYY
instead of yyyy
in my format string for a SimpleDateFormat
object. 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 dd
in 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 SimpleDateFormat
outputs 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, SimpleDateFormat
expects 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 Locale
if 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:
- 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 WeekFields
class.
该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.
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 YearWeek
class to be helpful. This class offers several handy methods such as generating a LocalDate
for 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 年。
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 类?
- Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6and Java SE 7
- Most of the java.timefunctionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
- Later versions of Android bundle implementations of the java.timeclasses.
- For earlier Android (<26), the ThreeTenABPproject adapts ThreeTen-Backport(mentioned above). See How to use ThreeTenABP….
- Java SE 8、Java SE 9、Java SE 10、Java SE 11及更高版本 - 标准 Java API 的一部分,具有捆绑实现。
- Java 9 添加了一些小功能和修复。
- Java SE 6和Java SE 7
- 大多数java.time功能在ThreeTen-Backport 中被反向移植到 Java 6 & 7 。
- 安卓
- 更高版本的 Android 捆绑实现java.time类。
- 对于早期的 Android(<26),ThreeTenABP项目采用了ThreeTen-Backport(上面提到过)。请参阅如何使用ThreeTenABP ...。