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

时间:2022-05-05 17:01:55

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.ofPattern doesn't allow us to use some pattern letters?

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

3 个解决方案

#1


127  

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

LocalDate仅代表日期,而不是DateTime。因此,格式化LocalDate时“HH:mm:ss”没有任何意义。假设您要表示日期和时间,请使用LocalDateTime。

#2


26  

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

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

Background: Most date-and-time-libraries (java.util.Calendar in Java, see also .Net-DateTime or Date in JavaScript or DateTime in 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中的java.util.Calendar,参见.Net-DateTime或JavaScript中的日期或Perl中的DateTime)基于通用通用唯一时间类型的概念(in德语有诗意的表达“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 ask every object of type TemporalAccessor for its specific set of supported fields. For LocalDate we 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 fields we 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),并让它决定是否接受已解析的中间对象中的所有字段值。

#3


2  

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

LocalDate没有时间信息,因此您获得UnsupportedTemporalTypeException:Unsupported字段:HourOfDay。

You can use LocalDateTime but 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字段:OffsetSeconds。

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

对我来说合适的课程是ZonedDateTime,其中包括时区和时区。

#1


127  

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

LocalDate仅代表日期,而不是DateTime。因此,格式化LocalDate时“HH:mm:ss”没有任何意义。假设您要表示日期和时间,请使用LocalDateTime。

#2


26  

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

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

Background: Most date-and-time-libraries (java.util.Calendar in Java, see also .Net-DateTime or Date in JavaScript or DateTime in 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中的java.util.Calendar,参见.Net-DateTime或JavaScript中的日期或Perl中的DateTime)基于通用通用唯一时间类型的概念(in德语有诗意的表达“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 ask every object of type TemporalAccessor for its specific set of supported fields. For LocalDate we 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 fields we 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),并让它决定是否接受已解析的中间对象中的所有字段值。

#3


2  

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

LocalDate没有时间信息,因此您获得UnsupportedTemporalTypeException:Unsupported字段:HourOfDay。

You can use LocalDateTime but 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字段:OffsetSeconds。

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

对我来说合适的课程是ZonedDateTime,其中包括时区和时区。