Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions docs/reference/mapping/params/format.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,13 @@ down to the nearest day.
[[custom-date-formats]]
==== Custom date formats

Completely customizable date formats are supported. The syntax for these is explained
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html[DateTimeFormatter docs].
Completely customizable date formats are supported. The syntax for these is explained in
https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/format/DateTimeFormatter.html[DateTimeFormatter docs].

Note that whilst the built-in formats for week dates use the ISO definition of weekyears,
custom formatters using the `Y`, `W`, or `w` field specifiers use the JDK locale definition
of weekyears. This can result in different values between the built-in formats and custom formats
for week dates.

[[built-in-date-formats]]
==== Built In Formats
Expand Down Expand Up @@ -256,31 +261,37 @@ The following tables lists all the defaults ISO formats supported:
`week_date` or `strict_week_date`::

A formatter for a full date as four digit weekyear, two digit week of
weekyear, and one digit day of week: `xxxx-'W'ww-e`.
weekyear, and one digit day of week: `YYYY-'W'ww-e`.
This uses the ISO week-date definition.

`week_date_time` or `strict_week_date_time`::

A formatter that combines a full weekyear date and time, separated by a
'T': `xxxx-'W'ww-e'T'HH:mm:ss.SSSZ`.
'T': `YYYY-'W'ww-e'T'HH:mm:ss.SSSZ`.
This uses the ISO week-date definition.

`week_date_time_no_millis` or `strict_week_date_time_no_millis`::

A formatter that combines a full weekyear date and time without millis,
separated by a 'T': `xxxx-'W'ww-e'T'HH:mm:ssZ`.
separated by a 'T': `YYYY-'W'ww-e'T'HH:mm:ssZ`.
This uses the ISO week-date definition.

`weekyear` or `strict_weekyear`::

A formatter for a four digit weekyear: `xxxx`.
A formatter for a four digit weekyear: `YYYY`.
This uses the ISO week-date definition.

`weekyear_week` or `strict_weekyear_week`::

A formatter for a four digit weekyear and two digit week of weekyear:
`xxxx-'W'ww`.
`YYYY-'W'ww`.
This uses the ISO week-date definition.

`weekyear_week_day` or `strict_weekyear_week_day`::

A formatter for a four digit weekyear, two digit week of weekyear, and one
digit day of week: `xxxx-'W'ww-e`.
digit day of week: `YYYY-'W'ww-e`.
This uses the ISO week-date definition.

`year` or `strict_year`::

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/mapping/types/date.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ The following parameters are accepted by `date` fields:

The locale to use when parsing dates since months do not have the same names
and/or abbreviations in all languages. The default is the
https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#ROOT[`ROOT` locale],
https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#ROOT[`ROOT` locale].

<<ignore-malformed,`ignore_malformed`>>::

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ Function<String, ZonedDateTime> getFunction(String format, ZoneId zoneId, Locale
// fill the rest of the date up with the parsed date
if (accessor.isSupported(ChronoField.YEAR) == false
&& accessor.isSupported(ChronoField.YEAR_OF_ERA) == false
&& accessor.isSupported(WeekFields.ISO.weekBasedYear()) == false
&& accessor.isSupported(WeekFields.of(locale).weekBasedYear()) == false
&& accessor.isSupported(ChronoField.INSTANT_SECONDS) == false) {
int year = LocalDate.now(ZoneOffset.UTC).getYear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private static DateFormatter newDateFormatter(String format, DateTimeFormatter p
);
}

public static final WeekFields WEEK_FIELDS_ROOT = WeekFields.of(Locale.ROOT);
public static final WeekFields WEEK_FIELDS_ROOT = WeekFields.ISO;

private static final DateTimeFormatter TIME_ZONE_FORMATTER_NO_COLON = new DateTimeFormatterBuilder().appendOffset("+HHmm", "Z")
.toFormatter(Locale.ROOT)
Expand Down Expand Up @@ -2389,6 +2389,7 @@ public static ZonedDateTime from(TemporalAccessor accessor, Locale locale, ZoneI
boolean isLocalTimeSet = localTime != null;

// the first two cases are the most common, so this allows us to exit early when parsing dates
WeekFields localeWeekFields;
if (isLocalDateSet && isLocalTimeSet) {
return of(localDate, localTime, zoneId);
} else if (accessor.isSupported(ChronoField.INSTANT_SECONDS) && accessor.isSupported(NANO_OF_SECOND)) {
Expand All @@ -2407,17 +2408,18 @@ public static ZonedDateTime from(TemporalAccessor accessor, Locale locale, ZoneI
} else if (accessor.isSupported(MONTH_OF_YEAR)) {
// missing year, falling back to the epoch and then filling
return getLocalDate(accessor, locale).atStartOfDay(zoneId);
} else if (accessor.isSupported(WeekFields.of(locale).weekBasedYear())) {
return localDateFromWeekBasedDate(accessor, locale).atStartOfDay(zoneId);
} else if (accessor.isSupported(WeekFields.ISO.weekBasedYear())) {
return localDateFromWeekBasedDate(accessor, locale, WeekFields.ISO).atStartOfDay(zoneId);
} else if (accessor.isSupported((localeWeekFields = WeekFields.of(locale)).weekBasedYear())) {
return localDateFromWeekBasedDate(accessor, locale, localeWeekFields).atStartOfDay(zoneId);
}

// we should not reach this piece of code, everything being parsed we should be able to
// convert to a zoned date time! If not, we have to extend the above methods
throw new IllegalArgumentException("temporal accessor [" + accessor + "] cannot be converted to zoned date time");
}

private static LocalDate localDateFromWeekBasedDate(TemporalAccessor accessor, Locale locale) {
WeekFields weekFields = WeekFields.of(locale);
private static LocalDate localDateFromWeekBasedDate(TemporalAccessor accessor, Locale locale, WeekFields weekFields) {
if (accessor.isSupported(weekFields.weekOfWeekBasedYear())) {
return LocalDate.ofEpochDay(0)
.with(weekFields.weekBasedYear(), accessor.get(weekFields.weekBasedYear()))
Expand Down Expand Up @@ -2459,8 +2461,11 @@ public String toString() {
};

private static LocalDate getLocalDate(TemporalAccessor accessor, Locale locale) {
if (accessor.isSupported(WeekFields.of(locale).weekBasedYear())) {
return localDateFromWeekBasedDate(accessor, locale);
WeekFields localeWeekFields;
if (accessor.isSupported(WeekFields.ISO.weekBasedYear())) {
return localDateFromWeekBasedDate(accessor, locale, WeekFields.ISO);
} else if (accessor.isSupported((localeWeekFields = WeekFields.of(locale)).weekBasedYear())) {
return localDateFromWeekBasedDate(accessor, locale, localeWeekFields);
} else if (accessor.isSupported(MONTH_OF_YEAR)) {
int year = getYear(accessor);
if (accessor.isSupported(DAY_OF_MONTH)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,24 @@ public void testWeekBasedDates() {

assertThat(DateFormatters.from(dateFormatter.parse("2016")), equalTo(ZonedDateTime.of(2015, 12, 27, 0, 0, 0, 0, ZoneOffset.UTC)));
assertThat(DateFormatters.from(dateFormatter.parse("2015")), equalTo(ZonedDateTime.of(2014, 12, 28, 0, 0, 0, 0, ZoneOffset.UTC)));

// the built-in formats use different week definitions (ISO instead of locale)
dateFormatter = DateFormatters.forPattern("weekyear_week");

assertThat(
DateFormatters.from(dateFormatter.parse("2016-W01")),
equalTo(ZonedDateTime.of(2016, 01, 04, 0, 0, 0, 0, ZoneOffset.UTC))
);

assertThat(
DateFormatters.from(dateFormatter.parse("2015-W01")),
equalTo(ZonedDateTime.of(2014, 12, 29, 0, 0, 0, 0, ZoneOffset.UTC))
);

dateFormatter = DateFormatters.forPattern("weekyear");

assertThat(DateFormatters.from(dateFormatter.parse("2016")), equalTo(ZonedDateTime.of(2016, 01, 04, 0, 0, 0, 0, ZoneOffset.UTC)));
assertThat(DateFormatters.from(dateFormatter.parse("2015")), equalTo(ZonedDateTime.of(2014, 12, 29, 0, 0, 0, 0, ZoneOffset.UTC)));
}

public void testEpochMillisParser() {
Expand Down