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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23069370/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 20:25:57  来源:igfitidea点击:

Format a date using the new date time API

javadatetimejava-8java-time

提问by user2336315

I was playing with the new date time API but when running this:

我正在使用新的日期时间 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);
    }
}

It throws:

它抛出:

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)

When looking at the source code of the LocalDate class, I see:

查看 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);
    }

As it described in the doc:

正如它在文档中描述的那样:

This method will create a formatter based on a simple pattern of letters and symbols as described in the class documentation.

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

And all these letters are defined.

所有这些字母都已定义

So why DateTimeFormatter.ofPatterndoesn't allow us to use some pattern letters?

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

采纳答案by James_D

LocalDaterepresents just a date, not a DateTime. So "HH:mm:ss" make no sense when formatting a LocalDate. Use a LocalDateTimeinstead, assuming you want to represent both a date and time.

LocalDate仅代表一个日期,而不是 DateTime。所以“HH:mm:ss”在格式化LocalDate. 使用 aLocalDateTime代替,假设您要同时表示日期和时间。

回答by Meno Hochschild

I would like to add following details to the correct answer of @James_D:

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

Background:Most date-and-time-libraries (java.util.Calendarin Java, see also .Net-DateTime or Datein JavaScript or DateTimein Perl) are based on the concept of a universal all-purpose unique temporal type (in German there is the poetic expression "eierlegende Wollmilchsau"). In this design there cannot be an unsupported field. But the price is high: Many time problems cannot be adequately handled with such an unflexible approach because it is hard to impossible to find a common denominator for all kinds of temporal objects.

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

JSR-310 has choosen another way, namely to allow different temporal types which consist of type-specific sets of supported built-in fields. The natural consequence is that not every possible field is supported by every type (and users can even define their own specialized fields). It is also possible to programmatically askevery object of type TemporalAccessorfor its specific set of supported fields. For LocalDatewe find:

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 

There is no HOUR_OF_DAY-field which explains the problem of UnsupportedTemporalTypeException. And if we look at the JSR-310-mapping of pattern symbols to fieldswe see that the symbol H is mapped to unsupported HOUR_OF_DAY:

没有 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);    
}

This field mapping does not mean that the field is supported by the concrete type. Parsing happens in several steps. The field mapping is only the first step. The second step is then parsing to a raw object of type TemporalAccessor. And finally parsing delegates to the target type (here: LocalDate) and let it decide if it accepts all the field values in parsed intermediate object.

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

回答by isapir

The right class for me was ZonedDateTimewhich includes both Time and Time Zone.

对我来说正确的课程ZonedDateTime包括时间和时区。

LocalDatedoesn't have the Time information so you get a UnsupportedTemporalTypeException: Unsupported field: HourOfDay.

LocalDate没有时间信息,所以你得到一个UnsupportedTemporalTypeException: Unsupported field: HourOfDay.

You can use LocalDateTimebut then you don't have the Time Zone information so if you try to access that (even by using one of the predefined formatters) you will get a UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds.

您可以使用LocalDateTime但随后您没有时区信息,因此如果您尝试访问该信息(即使使用预定义的格式化程序之一),您将获得UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds.