Java"周年"真的有用吗?

时间:2016-01-09 09:01:36

标签: java simpledateformat

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

此代码:

@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

2 个答案:

答案 0 :(得分:7)

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

<form id="2checkout" 
      action="<%= ENV['TWOCHECKOUT_PURCHASE_URL'] %>"
      method="post">
</form>

如果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。

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

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

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

在我的系统上(使用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

答案 1 :(得分:1)

定义各不相同

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

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

  • 第1周包含1月1日。
  • 第1周是包含特定星期几(例如星期日)的第一周。
  • 第1周是仅包含新年的第一天,没有上一年的日期。
  • …还有更多

您正在使用的旧类隐式使用Locale指定的定义。如果未另行指定,则使用JVM当前的默认Locale来应用的语言环境也是隐式的。

有关定义一周的难度的更多有趣信息,请参阅此问题Different behavior of WeekFields on JVM 8 and JVM 10

ISO 8601

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

ISO 8601 definition of a week是:

  • 每周从星期一开始
  • 第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() ) ;

请参阅此code run live at IdeOne.com

  

ld.toString():2019-01-01

     

周:1

org.threeten.extra.YearWeek

但是,如果要完成很多工作,我建议将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

请注意,以周为基础的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及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

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

在哪里获取java.time类?