Java的“周年”到底是如何工作的?定义各不相同国际标准化组织 ISO 8601java.timeorg.threeten.extra.YearWeek关于 java.time

2022-09-01 09:05:03

这开始是一个简单的错误:我没有在我的格式字符串中为对象。但是我对格式不正确的字符串的测试结果完全感到困惑。YYYYyyyySimpleDateFormat

此代码:

@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());
        }
}

产生以下输出:

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

我已经阅读了此处有关“Y”参数的文档:https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html,但我仍然看不到此处有效的逻辑。特别是最后一个例子:我可以有点理解如何将1月(也许是2月)的日期翻译成前一年的12月,但是将11月29日的日期向后移动11个月让我感到困惑。12月27日有什么特别之处?

谁能解释一下?

更多信息

@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

答案 1

很简单: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));

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

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


实际值取决于您的区域设置:

  • 一年中的哪一周是第 1 周
  • 哪一天是一周的第一天

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

在我的系统上(使用de-ch locale,以“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

答案 2

定义各不相同

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

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

  • 第 1 周包含 1 月 1 日。
  • 第 1 周是包含特定星期几(如星期日)的第一周。
  • 第 1 周是仅包含新年日期的第一周,没有前一年的日期。
  • ...以及更多

您正在使用的旧类隐式使用 由 指定的定义。正在应用的语言环境也是隐式的,如果您没有以其他方式指定,则使用 JVM 的当前缺省值。LocaleLocale

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

国际标准化组织 ISO 8601

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

ISO 8601 对一周的定义是:

  • 周从星期一开始
  • 第 1 周有日历年的第一个星期四
  • 一年包括52或53周。
  • 一年可以有上一个日历年的一天或一天以上,也可以有下一个日历年的一天或几天。

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

java.time

java.time 类在 WeekFields 类中为基于周的一年的一周提供了一些支持。

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

请参阅此代码在 IdeOne.com 实时运行

手机:2019-01-01

周: 1

org.threeten.extra.YearWeek

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

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

手机:2019-01-01

yw.toString(): 2019-W01

startOfWeek.toString(): 2018-12-31

请注意,在基于周的 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


关于 java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧日期时间类,如java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅 Oracle 教程。搜索 Stack Overflow 以获取许多示例和解释。规格是JSR 310

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

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

从哪里获取 java.time 类?


推荐