使用新的日期时间 API 格式化日期

新手上路,请多包涵

我正在玩新的日期时间 API,但在运行时:

 public class Test {
    public static void main(String[] args){
        String dateFormatted = LocalDate.now()
                                        .format(DateTimeFormatter
                                              .ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println(dateFormatted);
    }
}

它抛出:

 Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay
    at java.time.LocalDate.get0(LocalDate.java:680)
    at java.time.LocalDate.getLong(LocalDate.java:659)
    at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
    at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2543)
    at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2182)
    at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1745)
    at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1719)
    at java.time.LocalDate.format(LocalDate.java:1685)
    at Test.main(Test.java:23)

在查看 LocalDate 类的源代码时,我看到:

   private int get0(TemporalField field) {
        switch ((ChronoField) field) {
            case DAY_OF_WEEK: return getDayOfWeek().getValue();
            case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((day - 1) % 7) + 1;
            case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1;
            case DAY_OF_MONTH: return day;
            case DAY_OF_YEAR: return getDayOfYear();
            case EPOCH_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'EpochDay' for get() method, use getLong() instead");
            case ALIGNED_WEEK_OF_MONTH: return ((day - 1) / 7) + 1;
            case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1;
            case MONTH_OF_YEAR: return month;
            case PROLEPTIC_MONTH: throw new UnsupportedTemporalTypeException("Invalid field 'ProlepticMonth' for get() method, use getLong() instead");
            case YEAR_OF_ERA: return (year >= 1 ? year : 1 - year);
            case YEAR: return year;
            case ERA: return (year >= 1 ? 1 : 0);
        }
        throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
    }

正如文档中所述:

如类文档中所述,此方法将基于字母和符号的简单模式创建格式化程序。

所有这些字母都已 定义

那么为什么 DateTimeFormatter.ofPattern 不允许我们使用一些模式字母?

原文由 user2336315 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 770
2 个回答

LocalDate 只代表一个日期,而不是日期时间。因此,“HH:mm:ss”在格式化 LocalDate 时毫无意义。使用 LocalDateTime 代替,假设您想要表示日期和时间。

原文由 James_D 发布,翻译遵循 CC BY-SA 3.0 许可协议

我想在@James_D 的正确答案中添加以下详细信息:

背景: 大多数日期和时间库( java.util.Calendar 在 Java 中,另请参见 .Net-DateTime 或 Date 在 JavaScript 中或 DateTime )基于 Per关于通用通用唯一时间类型的概念(在德语中有诗意的表达“eierlegende Wollmilchsau”)。在此设计中,不能有不受支持的字段。但代价很高:许多时间问题无法用这种不灵活的方法得到充分处理,因为很难甚至不可能为所有类型的时间对象找到一个共同点。

JSR-310 选择了另一种方式,即允许不同的时间类型,这些类型由特定于类型的受支持内置字段集组成。自然的结果是并非每个类型都支持每个可能的字段(用户甚至可以定义自己的专门字段)。也可以通过 编程方式询问 类型为 TemporalAccessor 的每个对象的特定支持字段集。对于 LocalDate 我们发现:

 •DAY_OF_WEEK
•ALIGNED_DAY_OF_WEEK_IN_MONTH
•ALIGNED_DAY_OF_WEEK_IN_YEAR
•DAY_OF_MONTH
•DAY_OF_YEAR
•EPOCH_DAY
•ALIGNED_WEEK_OF_MONTH
•ALIGNED_WEEK_OF_YEAR
•MONTH_OF_YEAR
•PROLEPTIC_MONTH
•YEAR_OF_ERA
•YEAR
•ERA

没有 HOUR_OF_DAY 字段可以解释 UnsupportedTemporalTypeException 的问题。如果我们查看 JSR-310- 模式符号到字段的映射, 我们会看到符号 H 映射到不受支持的 HOUR_OF_DAY:

 /** Map of letters to fields. */
private static final Map<Character, TemporalField> FIELD_MAP = new HashMap<>();
static {
  FIELD_MAP.put('G', ChronoField.ERA);
  FIELD_MAP.put('y', ChronoField.YEAR_OF_ERA);
  FIELD_MAP.put('u', ChronoField.YEAR);
  FIELD_MAP.put('Q', IsoFields.QUARTER_OF_YEAR);
  FIELD_MAP.put('q', IsoFields.QUARTER_OF_YEAR);
  FIELD_MAP.put('M', ChronoField.MONTH_OF_YEAR);
  FIELD_MAP.put('L', ChronoField.MONTH_OF_YEAR);
  FIELD_MAP.put('D', ChronoField.DAY_OF_YEAR);
  FIELD_MAP.put('d', ChronoField.DAY_OF_MONTH);
  FIELD_MAP.put('F', ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH);
  FIELD_MAP.put('E', ChronoField.DAY_OF_WEEK);
  FIELD_MAP.put('c', ChronoField.DAY_OF_WEEK);
  FIELD_MAP.put('e', ChronoField.DAY_OF_WEEK);
  FIELD_MAP.put('a', ChronoField.AMPM_OF_DAY);
  FIELD_MAP.put('H', ChronoField.HOUR_OF_DAY);
  FIELD_MAP.put('k', ChronoField.CLOCK_HOUR_OF_DAY);
  FIELD_MAP.put('K', ChronoField.HOUR_OF_AMPM);
  FIELD_MAP.put('h', ChronoField.CLOCK_HOUR_OF_AMPM);
  FIELD_MAP.put('m', ChronoField.MINUTE_OF_HOUR);
  FIELD_MAP.put('s', ChronoField.SECOND_OF_MINUTE);
  FIELD_MAP.put('S', ChronoField.NANO_OF_SECOND);
  FIELD_MAP.put('A', ChronoField.MILLI_OF_DAY);
  FIELD_MAP.put('n', ChronoField.NANO_OF_SECOND);
  FIELD_MAP.put('N', ChronoField.NANO_OF_DAY);
}

此字段映射并不意味着该字段受具体类型支持。解析分几个步骤进行。字段映射只是第一步。然后,第二步是解析为 TemporalAccessor 类型的原始对象。最后将委托解析为目标类型(此处: LocalDate )并让它决定是否接受解析的中间对象中的所有字段值。

原文由 Meno Hochschild 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题