【问题标题】:Y returns 2012 while y returns 2011 in SimpleDateFormatY 返回 2012 而 y 在 SimpleDateFormat 中返回 2011
【发布时间】:2012-01-30 23:04:56
【问题描述】:

我想知道为什么 SimpleDateFormat 中的“Y”返回 2012 而“y”返回 2011:

System.out.println(new SimpleDateFormat("Y").format(new Date())); // prints 2012
System.out.println(new SimpleDateFormat("y").format(new Date())); // prints 2011

谁能解释一下原因?

【问题讨论】:

  • 就像给未来读者的一个提示:这种行为只会在一年的最后一周或一年的第一周发生。

标签: java date simpledateformat


【解决方案1】:

week year 和年份。来自 javadoc

一周年与 WEEK_OF_YEAR 周期同步。之间的所有星期 第一周和最后一周(含)具有相同的周年值。 因此,一周年的第一天和最后一天可能有不同的 日历年值。

例如,1998 年 1 月 1 日是星期四。如果 getFirstDayOfWeek() 是 MONDAY 和 getMinimalDaysInFirstWeek() 为 4(ISO 8601 标准 兼容设置),然后 1998 年的第 1 周从 1997 年 12 月 29 日开始, 并于 1998 年 1 月 4 日结束。最后三个星期的年份是 1998 年 1997 日历年的天数。但是,如果 getFirstDayOfWeek() 是 星期日,然后 1998 年的第 1 周开始于 1998 年 1 月 4 日,结束于 1998 年 1 月 10 日; 1998 年的前三天是一周的一部分 1997 年的 53 个,他们的周年是 1997 年。

【讨论】:

  • $ date Wed Dec 30 00:42:51 UTC 2015 $ date +%G 2015 $ date +%Y 2015 某些软件很困惑:strftime 计算今天(2015 年 12 月 29 日)为第 53 周,周年为 2015。
【解决方案2】:

如果日历支持周年,则格式化 Y 以获取周年。 (getCalendar().isWeekDateSupported())

【讨论】:

    【解决方案3】:

    我学习了 JSTL 标记库 format:dateshort 的艰难方法,因为请求的格式在封面下使用 YYYY。这确实可以将打印的日期提前一年。

    【讨论】:

      【解决方案4】:

      这是包含一些代码的 Java 8 更新,因为 GregorianCalendar 可能会被弃用或从未来的 JDK 版本中删除。

      新代码在WeekFields 类中处理,特别是小写y / 大写Y 使用weekBasedYear() 字段访问器。

      基于此返回一个字段以访问基于周的年份 周字段。这代表了周开始的年份的概念 在固定的星期几,例如星期一,每周属于 正好一年。该字段通常与 dayOfWeek() 和 weekOfWeekBasedYear()。

      Week one(1) 是从 getFirstDayOfWeek() 开始的那一周,其中 一年中至少有 getMinimalDaysInFirstWeek() 天。 因此, 第一周可能在年初之前开始。如果第一周 在年初之后开始,然后之前的时期在 上一年的最后一周。

      此字段可用于任何日历系统。

      在解析的解析阶段,可以从 基于周的年、年中的周和周中的日期。

      在严格模式下,所有三个字段都根据它们的范围进行验证 有效值。一年中的星期字段经过验证,以确保 生成的基于周的年份是请求的基于周的年份。

      在智能模式下,所有三个字段都根据它们的范围进行验证 有效值。基于周数的年份字段从 1 到 53,表示生成的日期可以在以下 以周为基础的年份到指定的年份。

      在宽松模式下,根据 有效值的范围。结果日期计算为等价于 以下三个阶段的方法。首先,在第一个上创建一个日期 所请求的基于周的年份的第一周中的某天。然后取 以周为基础的年,减去一,然后以周为单位添加金额 日期。最后,调整到正确的星期几 本地化周。

      WeekFields 实例的设置取决于区域设置,并且可能有不同的设置,取决于它,美国和欧洲国家(如法国)可能有不同的一天作为一周的开始。

      例如 Java 8 的 DateFormatterBuilder,使用语言环境实例化解析器,并将该语言环境用于 Y 符号:

      public final class DateTimeFormatterBuilder {
          ...
      
          private void parsePattern(String pattern) {
              ...
                      } else if (cur == 'Y') {
                          // Fields defined by Locale
                          appendInternal(new WeekBasedFieldPrinterParser(cur, count));
                      } else {
              ...
      
      
          static final class WeekBasedFieldPrinterParser implements DateTimePrinterParser {
              ...
      
              /**
               * Gets the printerParser to use based on the field and the locale.
               *
               * @param locale  the locale to use, not null
               * @return the formatter, not null
               * @throws IllegalArgumentException if the formatter cannot be found
               */
              private DateTimePrinterParser printerParser(Locale locale) {
                  WeekFields weekDef = WeekFields.of(locale);
                  TemporalField field = null;
                  switch (chr) {
                      case 'Y':
                          field = weekDef.weekBasedYear();
                          if (count == 2) {
                              return new ReducedPrinterParser(field, 2, 2, 0, ReducedPrinterParser.BASE_DATE, 0);
                          } else {
                              return new NumberPrinterParser(field, count, 19,
                                                             (count < 4) ? SignStyle.NORMAL : SignStyle.EXCEEDS_PAD, -1);
                          }
                      case 'e':
                      case 'c':
                          field = weekDef.dayOfWeek();
                          break;
                      case 'w':
                          field = weekDef.weekOfWeekBasedYear();
                          break;
                      case 'W':
                          field = weekDef.weekOfMonth();
                          break;
                      default:
                          throw new IllegalStateException("unreachable");
                  }
                  return new NumberPrinterParser(field, (count == 2 ? 2 : 1), 2, SignStyle.NOT_NEGATIVE);
              }
      
              ...
          }
      
          ...
      }
      

      这里有一些例子

      System.out.format("Conundrum                         : %s%n",
                        ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
                                     .format(DateTimeFormatter.ofPattern("YYYYMMdd'T'HHmms'S'")));
      System.out.format("Solution                          : %s%n",
                        ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
                                     .format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmms'S'")));
      
      
      System.out.format("JVM Locale first day of week      : %s%n",
                        WeekFields.of(Locale.getDefault()).getFirstDayOfWeek());
      System.out.format("US first day of week              : %s%n",
                        WeekFields.of(Locale.US).getFirstDayOfWeek());
      System.out.format("France first day of week          : %s%n",
                        WeekFields.of(Locale.FRANCE).getFirstDayOfWeek());
      System.out.format("JVM Locale min days in 1st week   : %s%n",
                        WeekFields.of(Locale.getDefault()).getMinimalDaysInFirstWeek());
      System.out.format("US min days in 1st week           : %s%n",
                        WeekFields.of(Locale.US).getMinimalDaysInFirstWeek());
      System.out.format("JVM Locale min days in 1st week   : %s%n",
                        WeekFields.of(Locale.FRANCE).getMinimalDaysInFirstWeek());
      
      System.out.format("JVM Locale week based year (big Y): %s%n",
                        ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC")).get(WeekFields.of(Locale.FRANCE).weekBasedYear()));
      System.out.format("France week based year (big Y)    : %s%n",
                        ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC")).get(WeekFields.of(Locale.FRANCE).weekBasedYear()));
      System.out.format("US week based year (big Y)        : %s%n",
                        ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC")).get(WeekFields.of(Locale.US).weekBasedYear()));
      

      关于区域设置和大写Y,您可以使用命令行选项-Duser.language=frenes 等),或者强制使用区域设置在调用时:

      System.out.format("English localized                 : %s%n",
                        ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
                                     .format(DateTimeFormatter.ofPattern("YYYYMMdd'T'HHmms'S'", Locale.ENGLISH)));
      System.out.format("French localized                  : %s%n",
                        ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
                                     .format(DateTimeFormatter.ofPattern("YYYYMMdd'T'HHmms'S'", Locale.FRENCH)));
      

      【讨论】:

        【解决方案5】:

        我来回转换日期 - 执行此操作时您会期望同一年。

        注意它是如何前进的!

        这很糟糕:YYYY!

        你可以运行它here

        import java.util.Date;
        import java.text.SimpleDateFormat;
        import java.text.ParseException;
        import static java.lang.System.out;
        class Playground {
            public static Date convertYYYYMMDDStr(String s) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                Date result = null;
                try {
                    result = sdf.parse(s);
                } catch(ParseException e) {
                    e.printStackTrace();
                }
                return result;
            }
            public static String formatDateToStrWithSDF(Date d, SimpleDateFormat s) {
                return s.format(d);
            }
            public static void main(String[ ] args) {
                // DON'T DO. Use yyyy instead of YYYY
                SimpleDateFormat sdfdmy = new SimpleDateFormat("dd-MM-YYYY"); 
                String jan1st2020sb = "2020-01-01";
                Date jan1st2020d = convertYYYYMMDDStr(jan1st2020sb);
                String jan1st2020sa = formatDateToStrWithSDF(jan1st2020d, sdfdmy);
                out.println(jan1st2020sb);
                out.println(jan1st2020d);
                out.println(jan1st2020sa);
                String dec31st2020sb = "2020-12-31";
                Date dec31st2020d = convertYYYYMMDDStr(dec31st2020sb);
                String dec31st2020sa = formatDateToStrWithSDF(dec31st2020d, sdfdmy);
                out.println(dec31st2020sb);
                out.println(dec31st2020d);
                out.println(dec31st2020sa);
            }
        }
        

        这很好:yyyy

        【讨论】:

          猜你喜欢
          • 2019-11-11
          • 2013-04-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多