Skip to content

Commit fb2361b

Browse files
authored
Change year max digits for strict_date_optional_time and date_optional_time backports(#73034) (#73750)
We changed the default joda behaviour in strict_date_optional_time to max 4 digits in a year. Java.time implementation should behave the same way. At the same time date_optional_time should have 9digits for year part. closes #52396 closes #72191 backports #73034
1 parent a27fe61 commit fb2361b

File tree

16 files changed

+144
-69
lines changed

16 files changed

+144
-69
lines changed

client/rest-high-level/src/test/java/org/elasticsearch/client/ml/calendars/ScheduledEventTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package org.elasticsearch.client.ml.calendars;
1010

1111
import org.elasticsearch.common.Nullable;
12+
import org.elasticsearch.common.time.DateUtils;
1213
import org.elasticsearch.common.xcontent.XContentParser;
1314
import org.elasticsearch.test.AbstractXContentTestCase;
1415

@@ -17,8 +18,9 @@
1718
public class ScheduledEventTests extends AbstractXContentTestCase<ScheduledEvent> {
1819

1920
public static ScheduledEvent testInstance(String calendarId, @Nullable String eventId) {
20-
Date start = new Date(randomNonNegativeLong());
21-
Date end = new Date(start.getTime() + randomIntBetween(1, 10000) * 1000);
21+
int duration = randomIntBetween(1, 10000) * 1000;
22+
Date start = new Date(randomLongBetween(1, DateUtils.MAX_MILLIS_BEFORE_9999) - duration );
23+
Date end = new Date(start.getTime() + duration);
2224

2325
return new ScheduledEvent(randomAlphaOfLength(10), start, end, calendarId, eventId);
2426
}

server/src/main/java/org/elasticsearch/common/time/DateFormatters.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,21 @@ public class DateFormatters {
5959
.toFormatter(Locale.ROOT)
6060
.withResolverStyle(ResolverStyle.STRICT);
6161

62+
private static final DateTimeFormatter STRICT_YEAR_MONTH_DAY_PRINTER = new DateTimeFormatterBuilder()
63+
.appendValue(ChronoField.YEAR, 4, 9, SignStyle.EXCEEDS_PAD)
64+
.optionalStart()
65+
.appendLiteral("-")
66+
.appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE)
67+
.optionalStart()
68+
.appendLiteral('-')
69+
.appendValue(DAY_OF_MONTH, 2, 2, SignStyle.NOT_NEGATIVE)
70+
.optionalEnd()
71+
.optionalEnd()
72+
.toFormatter(Locale.ROOT)
73+
.withResolverStyle(ResolverStyle.STRICT);
74+
6275
private static final DateTimeFormatter STRICT_YEAR_MONTH_DAY_FORMATTER = new DateTimeFormatterBuilder()
63-
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
76+
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.EXCEEDS_PAD)
6477
.optionalStart()
6578
.appendLiteral("-")
6679
.appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE)
@@ -83,7 +96,7 @@ public class DateFormatters {
8396
.withResolverStyle(ResolverStyle.STRICT);
8497

8598
private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_PRINTER = new DateTimeFormatterBuilder()
86-
.append(STRICT_YEAR_MONTH_DAY_FORMATTER)
99+
.append(STRICT_YEAR_MONTH_DAY_PRINTER)
87100
.appendLiteral('T')
88101
.optionalStart()
89102
.appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE)
@@ -166,7 +179,7 @@ public class DateFormatters {
166179
.withResolverStyle(ResolverStyle.STRICT);
167180

168181
private static final DateTimeFormatter STRICT_DATE_OPTIONAL_TIME_PRINTER_NANOS = new DateTimeFormatterBuilder()
169-
.append(STRICT_YEAR_MONTH_DAY_FORMATTER)
182+
.append(STRICT_YEAR_MONTH_DAY_PRINTER)
170183
.appendLiteral('T')
171184
.optionalStart()
172185
.appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE)
@@ -578,7 +591,7 @@ public class DateFormatters {
578591
*/
579592
private static final DateFormatter STRICT_YEAR_MONTH = new JavaDateFormatter("strict_year_month",
580593
new DateTimeFormatterBuilder()
581-
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
594+
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.EXCEEDS_PAD)
582595
.appendLiteral("-")
583596
.appendValue(MONTH_OF_YEAR, 2, 2, SignStyle.NOT_NEGATIVE)
584597
.toFormatter(Locale.ROOT)
@@ -588,7 +601,7 @@ public class DateFormatters {
588601
* A strict formatter that formats or parses a year, such as '2011'.
589602
*/
590603
private static final DateFormatter STRICT_YEAR = new JavaDateFormatter("strict_year", new DateTimeFormatterBuilder()
591-
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
604+
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.EXCEEDS_PAD)
592605
.toFormatter(Locale.ROOT)
593606
.withResolverStyle(ResolverStyle.STRICT));
594607

@@ -630,7 +643,7 @@ public class DateFormatters {
630643
);
631644

632645
private static final DateTimeFormatter STRICT_ORDINAL_DATE_TIME_NO_MILLIS_BASE = new DateTimeFormatterBuilder()
633-
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
646+
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.EXCEEDS_PAD)
634647
.appendLiteral('-')
635648
.appendValue(DAY_OF_YEAR, 3, 3, SignStyle.NOT_NEGATIVE)
636649
.appendLiteral('T')
@@ -761,7 +774,7 @@ public class DateFormatters {
761774
new JavaDateFormatter("strict_hour_minute", DateTimeFormatter.ofPattern("HH:mm", Locale.ROOT));
762775

763776
private static final DateTimeFormatter STRICT_ORDINAL_DATE_TIME_PRINTER = new DateTimeFormatterBuilder()
764-
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
777+
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.EXCEEDS_PAD)
765778
.appendLiteral('-')
766779
.appendValue(DAY_OF_YEAR, 3, 3, SignStyle.NOT_NEGATIVE)
767780
.appendLiteral('T')
@@ -775,7 +788,7 @@ public class DateFormatters {
775788
.withResolverStyle(ResolverStyle.STRICT);
776789

777790
private static final DateTimeFormatter STRICT_ORDINAL_DATE_TIME_FORMATTER_BASE = new DateTimeFormatterBuilder()
778-
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
791+
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.EXCEEDS_PAD)
779792
.appendLiteral('-')
780793
.appendValue(DAY_OF_YEAR, 3, 3, SignStyle.NOT_NEGATIVE)
781794
.appendLiteral('T')
@@ -1016,7 +1029,7 @@ public class DateFormatters {
10161029

10171030
private static final DateTimeFormatter STRICT_ORDINAL_DATE_FORMATTER = new DateTimeFormatterBuilder()
10181031
.parseCaseInsensitive()
1019-
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
1032+
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.EXCEEDS_PAD)
10201033
.appendLiteral('-')
10211034
.appendValue(DAY_OF_YEAR, 3)
10221035
.optionalStart()
@@ -1042,7 +1055,7 @@ public class DateFormatters {
10421055
/////////////////////////////////////////
10431056

10441057
private static final DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder()
1045-
.appendValue(ChronoField.YEAR, 1, 5, SignStyle.NORMAL)
1058+
.appendValue(ChronoField.YEAR, 1, 9, SignStyle.NORMAL)
10461059
.optionalStart()
10471060
.appendLiteral('-')
10481061
.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NOT_NEGATIVE)

server/src/main/java/org/elasticsearch/common/time/DateUtils.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
import static org.elasticsearch.common.time.DateUtilsRounding.utcMillisAtStartOfYear;
3030

3131
public class DateUtils {
32+
public static final long MAX_MILLIS_BEFORE_9999 = 253402300799999L; // end of year 9999
33+
public static final long MAX_MILLIS_BEFORE_MINUS_9999 = -377705116800000L; // beginning of year -9999
34+
3235
public static DateTimeZone zoneIdToDateTimeZone(ZoneId zoneId) {
3336
if (zoneId == null) {
3437
return null;

server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.joda.time.DateTimeZone;
2020
import org.joda.time.format.DateTimeFormat;
2121
import org.joda.time.format.ISODateTimeFormat;
22+
import org.junit.Assert;
2223
import org.junit.BeforeClass;
2324

2425
import java.time.ZoneId;
@@ -54,6 +55,18 @@ public void testIncorrectFormat() {
5455
assertParseException("2021-01-01T23-35-00Z", "strict_date_optional_time");
5556
}
5657

58+
public void testMinMillis() {
59+
String jodaFormatted = Joda.forPattern("strict_date_optional_time").formatMillis(Long.MIN_VALUE);
60+
String javaFormatted = DateFormatter.forPattern("strict_date_optional_time").formatMillis(Long.MIN_VALUE);
61+
Assert.assertEquals(jodaFormatted, javaFormatted);
62+
}
63+
public void testYearParsing() {
64+
//this one is considered a year
65+
assertSameDate("1234", "strict_date_optional_time||epoch_millis");
66+
//this one is considered a 12345milliseconds since epoch
67+
assertSameDate("12345", "strict_date_optional_time||epoch_millis");
68+
}
69+
5770
public void testTimezoneParsing() {
5871
/** this testcase won't work in joda. See comment in {@link #testPartialTimeParsing()}
5972
* assertSameDateAs("2016-11-30T+01", "strict_date_optional_time", "strict_date_optional_time");

server/src/test/java/org/elasticsearch/common/time/DateFormattersTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ public void testEpochMillisParser() {
9090
public void testPrintersLongMinMaxValue() {
9191
for (FormatNames format : FormatNames.values()) {
9292
DateFormatter formatter = DateFormatters.forPattern(format.getSnakeCaseName());
93-
formatter.format(DateFieldMapper.Resolution.MILLISECONDS.toInstant(Long.MIN_VALUE));
94-
formatter.format(DateFieldMapper.Resolution.MILLISECONDS.toInstant(Long.MAX_VALUE));
93+
formatter.format(DateFieldMapper.Resolution.MILLISECONDS.toInstant(DateUtils.MAX_MILLIS_BEFORE_MINUS_9999));
94+
formatter.format(DateFieldMapper.Resolution.MILLISECONDS.toInstant(DateUtils.MAX_MILLIS_BEFORE_9999));
9595
}
9696
assertWarnings("Format name \"week_year\" is deprecated and will be removed in a future version. Use \"weekyear\" format instead");
9797
}

server/src/test/java/org/elasticsearch/common/time/JavaDateMathParserTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,10 +311,10 @@ public void testTimestamps() {
311311
long datetime = parser.parse("1418248078", () -> 0).toEpochMilli();
312312
assertDateEquals(datetime, "1418248078", "2014-12-10T21:47:58.000");
313313

314-
// a timestamp before 100000 is a year
314+
// for date_optional_time a timestamp with more than 9digits is epoch
315315
assertDateMathEquals("9999", "9999-01-01T00:00:00.000");
316316
assertDateMathEquals("10000", "10000-01-01T00:00:00.000");
317-
assertDateMathEquals("100000", "1970-01-01T00:01:40.000");
317+
assertDateMathEquals("1000000000", "1970-01-12T13:46:40.000Z");
318318

319319
// but 10000 with T is still a date format
320320
assertDateMathEquals("10000-01-01T", "10000-01-01T00:00:00.000");

server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,17 @@ public void testStore() throws Exception {
139139

140140
public void testIgnoreMalformed() throws IOException {
141141
testIgnoreMalformedForValue("2016-03-99",
142-
"failed to parse date field [2016-03-99] with format [strict_date_optional_time||epoch_millis]");
142+
"failed to parse date field [2016-03-99] with format [strict_date_optional_time||epoch_millis]",
143+
"strict_date_optional_time||epoch_millis");
143144
testIgnoreMalformedForValue("-2147483648",
144-
"Invalid value for Year (valid values -999999999 - 999999999): -2147483648");
145-
testIgnoreMalformedForValue("-522000000", "long overflow");
145+
"failed to parse date field [-2147483648] with format [strict_date_optional_time||epoch_millis]",
146+
"strict_date_optional_time||epoch_millis");
147+
testIgnoreMalformedForValue("-522000000", "long overflow", "date_optional_time");
146148
}
147149

148-
private void testIgnoreMalformedForValue(String value, String expectedCause) throws IOException {
150+
private void testIgnoreMalformedForValue(String value, String expectedCause, String dateFormat) throws IOException {
149151

150-
DocumentMapper mapper = createDocumentMapper(fieldMapping(this::minimalMapping));
152+
DocumentMapper mapper = createDocumentMapper(fieldMapping((builder)-> dateFieldMapping(builder, dateFormat)));
151153

152154
MapperParsingException e = expectThrows(MapperParsingException.class,
153155
() -> mapper.parse(source(b -> b.field("field", value))));
@@ -157,6 +159,7 @@ private void testIgnoreMalformedForValue(String value, String expectedCause) thr
157159

158160
DocumentMapper mapper2 = createDocumentMapper(fieldMapping(b -> b
159161
.field("type", "date")
162+
.field("format", dateFormat)
160163
.field("ignore_malformed", true)));
161164

162165
ParsedDocument doc = mapper2.parse(source(b -> b.field("field", value)));
@@ -166,6 +169,11 @@ private void testIgnoreMalformedForValue(String value, String expectedCause) thr
166169
assertArrayEquals(new String[] { "field" }, TermVectorsService.getValues(doc.rootDoc().getFields("_ignored")));
167170
}
168171

172+
private void dateFieldMapping(XContentBuilder builder, String dateFormat) throws IOException {
173+
builder.field("type", "date");
174+
builder.field("format", dateFormat);
175+
176+
}
169177
public void testChangeFormat() throws IOException {
170178

171179
DocumentMapper mapper = createDocumentMapper(fieldMapping(b -> b

server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/LongBoundsTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.elasticsearch.common.io.stream.BytesStreamOutput;
1313
import org.elasticsearch.common.io.stream.StreamInput;
1414
import org.elasticsearch.common.time.DateFormatter;
15+
import org.elasticsearch.common.time.DateUtils;
1516
import org.elasticsearch.common.xcontent.ToXContent;
1617
import org.elasticsearch.common.xcontent.XContentBuilder;
1718
import org.elasticsearch.common.xcontent.XContentParser;
@@ -44,8 +45,8 @@ public static LongBounds randomExtendedBounds() {
4445
* Construct a random {@link LongBounds} in pre-parsed form.
4546
*/
4647
public static LongBounds randomParsedExtendedBounds() {
47-
long maxDateValue = 253402300799999L; // end of year 9999
48-
long minDateValue = -377705116800000L; // beginning of year -9999
48+
long maxDateValue = DateUtils.MAX_MILLIS_BEFORE_9999;
49+
long minDateValue = DateUtils.MAX_MILLIS_BEFORE_MINUS_9999; // beginning of year -9999
4950
if (randomBoolean()) {
5051
// Construct with one missing bound
5152
if (randomBoolean()) {

test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,13 @@ public static DateTimeZone randomDateTimeZone() {
915915
return DateTimeZone.forID(randomFrom(JODA_TIMEZONE_IDS));
916916
}
917917

918+
/**
919+
* generate a random epoch millis in a range 1 to 9999-12-31T23:59:59.999
920+
*/
921+
public long randomMillisUpToYear9999() {
922+
return randomLongBetween(1, DateUtils.MAX_MILLIS_BEFORE_9999);
923+
}
924+
918925
/**
919926
* generate a random TimeZone from the ones available in java.util
920927
*/

0 commit comments

Comments
 (0)