Skip to content

Commit cfc0a47

Browse files
authored
Core: Deprecate negative epoch timestamps (#36793)
Negative timestamps are currently supported in joda time. These are dates before epoch. However, it doesn't really make sense to have a negative timestamp, since this is a modern format. Any dates before epoch can be represented with normal date formats, like ISO8601. Additionally, implementing negative epoch timestamp parsing in java time has an edge case which would more than double the code required. This commit deprecates use of negative epoch timestamps.
1 parent 6221d6b commit cfc0a47

File tree

6 files changed

+45
-34
lines changed

6 files changed

+45
-34
lines changed

rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/40_range.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ setup:
304304
index: test
305305
type: test
306306
id: 4
307-
body: { "date" : "-2524492800000" }
307+
body: { "date" : "10000" }
308308

309309
- do:
310310
index:
@@ -324,16 +324,16 @@ setup:
324324
age_groups:
325325
date_range:
326326
field: date
327-
missing: "-2240496000000"
327+
missing: "0"
328328
ranges:
329329
- key: Generation Y
330330
from: '315561600000'
331331
to: '946713600000'
332332
- key: Generation X
333-
from: "-157737600000"
333+
from: "200000"
334334
to: '315561600000'
335335
- key: Other
336-
to: "-2208960000000"
336+
to: "200000"
337337

338338
- match: { hits.total: 5 }
339339

server/src/main/java/org/elasticsearch/common/joda/Joda.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,10 +372,14 @@ public int parseInto(DateTimeParserBucket bucket, String text, int position) {
372372
int factor = hasMilliSecondPrecision ? 1 : 1000;
373373
try {
374374
long millis = new BigDecimal(text).longValue() * factor;
375-
// check for deprecation, but after it has parsed correctly so the "e" isn't from something else
375+
// check for deprecations, but after it has parsed correctly so invalid values aren't counted as deprecated
376+
if (millis < 0) {
377+
deprecationLogger.deprecatedAndMaybeLog("epoch-negative", "Use of negative values" +
378+
" in epoch time formats is deprecated and will not be supported in the next major version of Elasticsearch.");
379+
}
376380
if (scientificNotation.matcher(text).find()) {
377381
deprecationLogger.deprecatedAndMaybeLog("epoch-scientific-notation", "Use of scientific notation" +
378-
"in epoch time formats is deprecated and will not be supported in the next major version of Elasticsearch.");
382+
" in epoch time formats is deprecated and will not be supported in the next major version of Elasticsearch.");
379383
}
380384
DateTime dt = new DateTime(millis, DateTimeZone.UTC);
381385
bucket.saveField(DateTimeFieldType.year(), dt.getYear());

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,9 @@ public void testDuellingFormatsValidParsing() {
7575
assertSameDate("1522332219.0", "epoch_second");
7676
assertSameDate("0", "epoch_second");
7777
assertSameDate("1", "epoch_second");
78-
assertSameDate("-1", "epoch_second");
79-
assertSameDate("-1522332219", "epoch_second");
8078
assertSameDate("1522332219321", "epoch_millis");
8179
assertSameDate("0", "epoch_millis");
8280
assertSameDate("1", "epoch_millis");
83-
assertSameDate("-1", "epoch_millis");
84-
assertSameDate("-1522332219321", "epoch_millis");
8581

8682
assertSameDate("20181126", "basic_date");
8783
assertSameDate("20181126T121212.123Z", "basic_date_time");

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ public void testThatNegativeEpochsCanBeParsed() {
303303
formatter.parseJoda("-1234567890.9999");
304304
formatter.parseJoda("-1234567890123456.9999");
305305
}
306+
307+
assertWarnings("Use of negative values" +
308+
" in epoch time formats is deprecated and will not be supported in the next major version of Elasticsearch.");
306309
}
307310

308311
public void testForInvalidDatesInEpochSecond() {
@@ -753,13 +756,22 @@ public void testDeprecatedFormatSpecifiers() {
753756
" next major version of Elasticsearch. Prefix your date format with '8' to use the new specifier.");
754757
}
755758

756-
public void testDeprecatedScientificNotation() {
759+
public void testDeprecatedEpochScientificNotation() {
757760
assertValidDateFormatParsing("epoch_second", "1.234e5", "123400");
758761
assertWarnings("Use of scientific notation" +
759-
"in epoch time formats is deprecated and will not be supported in the next major version of Elasticsearch.");
762+
" in epoch time formats is deprecated and will not be supported in the next major version of Elasticsearch.");
760763
assertValidDateFormatParsing("epoch_millis", "1.234e5", "123400");
761764
assertWarnings("Use of scientific notation" +
762-
"in epoch time formats is deprecated and will not be supported in the next major version of Elasticsearch.");
765+
" in epoch time formats is deprecated and will not be supported in the next major version of Elasticsearch.");
766+
}
767+
768+
public void testDeprecatedEpochNegative() {
769+
assertValidDateFormatParsing("epoch_second", "-12345", "-12345");
770+
assertWarnings("Use of negative values" +
771+
" in epoch time formats is deprecated and will not be supported in the next major version of Elasticsearch.");
772+
assertValidDateFormatParsing("epoch_millis", "-12345", "-12345");
773+
assertWarnings("Use of negative values" +
774+
" in epoch time formats is deprecated and will not be supported in the next major version of Elasticsearch.");
763775
}
764776

765777
private void assertValidDateFormatParsing(String pattern, String dateToParse) {

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737

3838
import java.io.IOException;
3939
import java.util.Collection;
40-
import java.util.Locale;
4140

4241
import static org.hamcrest.Matchers.containsString;
4342
import static org.hamcrest.Matchers.notNullValue;
@@ -228,8 +227,8 @@ public void testFloatEpochFormat() throws IOException {
228227

229228
assertEquals(mapping, mapper.mappingSource().toString());
230229

231-
double epochFloatMillisFromEpoch = (randomDouble() * 2 - 1) * 1000000;
232-
String epochFloatValue = String.format(Locale.US, "%f", epochFloatMillisFromEpoch);
230+
long epochMillis = randomNonNegativeLong();
231+
String epochFloatValue = epochMillis + "." + randomIntBetween(0, 999);
233232

234233
ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", BytesReference
235234
.bytes(XContentFactory.jsonBuilder()
@@ -241,7 +240,7 @@ public void testFloatEpochFormat() throws IOException {
241240
IndexableField[] fields = doc.rootDoc().getFields("field");
242241
assertEquals(2, fields.length);
243242
IndexableField pointField = fields[0];
244-
assertEquals((long)epochFloatMillisFromEpoch, pointField.numericValue().longValue());
243+
assertEquals(epochMillis, pointField.numericValue().longValue());
245244
}
246245

247246
public void testChangeLocale() throws IOException {

x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public void testGettingInvalidByte() throws Exception {
150150
double doubleNotByte = randomDoubleBetween(Byte.MAX_VALUE + 1, Double.MAX_VALUE, true);
151151
float floatNotByte = randomFloatBetween(Byte.MAX_VALUE + 1, Float.MAX_VALUE);
152152
String randomString = randomUnicodeOfCodepointLengthBetween(128, 256);
153-
long randomDate = randomLong();
153+
long randomDate = randomNonNegativeLong();
154154

155155
String doubleErrorMessage = (doubleNotByte > Long.MAX_VALUE || doubleNotByte < Long.MIN_VALUE) ?
156156
Double.toString(doubleNotByte) : Long.toString(Math.round(doubleNotByte));
@@ -279,7 +279,7 @@ public void testGettingInvalidShort() throws Exception {
279279
double doubleNotShort = randomDoubleBetween(Short.MAX_VALUE + 1, Double.MAX_VALUE, true);
280280
float floatNotShort = randomFloatBetween(Short.MAX_VALUE + 1, Float.MAX_VALUE);
281281
String randomString = randomUnicodeOfCodepointLengthBetween(128, 256);
282-
long randomDate = randomLong();
282+
long randomDate = randomNonNegativeLong();
283283

284284
String doubleErrorMessage = (doubleNotShort > Long.MAX_VALUE || doubleNotShort < Long.MIN_VALUE) ?
285285
Double.toString(doubleNotShort) : Long.toString(Math.round(doubleNotShort));
@@ -400,7 +400,7 @@ public void testGettingInvalidInteger() throws Exception {
400400
double doubleNotInt = randomDoubleBetween(getMaxIntPlusOne().doubleValue(), Double.MAX_VALUE, true);
401401
float floatNotInt = randomFloatBetween(getMaxIntPlusOne().floatValue(), Float.MAX_VALUE);
402402
String randomString = randomUnicodeOfCodepointLengthBetween(128, 256);
403-
long randomDate = randomLong();
403+
long randomDate = randomNonNegativeLong();
404404

405405
String doubleErrorMessage = (doubleNotInt > Long.MAX_VALUE || doubleNotInt < Long.MIN_VALUE) ?
406406
Double.toString(doubleNotInt) : Long.toString(Math.round(doubleNotInt));
@@ -511,7 +511,7 @@ public void testGettingInvalidLong() throws Exception {
511511
double doubleNotLong = randomDoubleBetween(getMaxLongPlusOne().doubleValue(), Double.MAX_VALUE, true);
512512
float floatNotLong = randomFloatBetween(getMaxLongPlusOne().floatValue(), Float.MAX_VALUE);
513513
String randomString = randomUnicodeOfCodepointLengthBetween(128, 256);
514-
long randomDate = randomLong();
514+
long randomDate = randomNonNegativeLong();
515515

516516
index("test", "1", builder -> {
517517
builder.field("test_double", doubleNotLong);
@@ -606,7 +606,7 @@ public void testGettingInvalidDouble() throws Exception {
606606
});
607607

608608
String randomString = randomUnicodeOfCodepointLengthBetween(128, 256);
609-
long randomDate = randomLong();
609+
long randomDate = randomNonNegativeLong();
610610

611611
index("test", "1", builder -> {
612612
builder.field("test_keyword", randomString);
@@ -689,7 +689,7 @@ public void testGettingInvalidFloat() throws Exception {
689689
});
690690

691691
String randomString = randomUnicodeOfCodepointLengthBetween(128, 256);
692-
long randomDate = randomLong();
692+
long randomDate = randomNonNegativeLong();
693693

694694
index("test", "1", builder -> {
695695
builder.field("test_keyword", randomString);
@@ -722,8 +722,8 @@ public void testGettingBooleanValues() throws Exception {
722722
builder.startObject("test_boolean").field("type", "boolean").endObject();
723723
builder.startObject("test_date").field("type", "date").endObject();
724724
});
725-
long randomDate1 = randomLong();
726-
long randomDate2 = randomLong();
725+
long randomDate1 = randomNonNegativeLong();
726+
long randomDate2 = randomNonNegativeLong();
727727

728728
// true values
729729
indexSimpleDocumentWithTrueValues(randomDate1);
@@ -805,7 +805,7 @@ public void testGettingDateWithoutCalendar() throws Exception {
805805
builder.startObject("test_boolean").field("type", "boolean").endObject();
806806
builder.startObject("test_date").field("type", "date").endObject();
807807
});
808-
Long randomLongDate = randomLong();
808+
Long randomLongDate = randomNonNegativeLong();
809809
indexSimpleDocumentWithTrueValues(randomLongDate);
810810

811811
String timeZoneId = randomKnownTimeZone();
@@ -838,7 +838,7 @@ public void testGettingDateWithCalendar() throws Exception {
838838
builder.startObject("test_boolean").field("type", "boolean").endObject();
839839
builder.startObject("test_date").field("type", "date").endObject();
840840
});
841-
Long randomLongDate = randomLong();
841+
Long randomLongDate = randomNonNegativeLong();
842842
indexSimpleDocumentWithTrueValues(randomLongDate);
843843
index("test", "2", builder -> {
844844
builder.timeField("test_date", null);
@@ -874,7 +874,7 @@ public void testGettingTimeWithoutCalendar() throws Exception {
874874
builder.startObject("test_boolean").field("type", "boolean").endObject();
875875
builder.startObject("test_date").field("type", "date").endObject();
876876
});
877-
Long randomLongDate = randomLong();
877+
Long randomLongDate = randomNonNegativeLong();
878878
indexSimpleDocumentWithTrueValues(randomLongDate);
879879

880880
String timeZoneId = randomKnownTimeZone();
@@ -906,7 +906,7 @@ public void testGettingTimeWithCalendar() throws Exception {
906906
builder.startObject("test_boolean").field("type", "boolean").endObject();
907907
builder.startObject("test_date").field("type", "date").endObject();
908908
});
909-
Long randomLongDate = randomLong();
909+
Long randomLongDate = randomNonNegativeLong();
910910
indexSimpleDocumentWithTrueValues(randomLongDate);
911911
index("test", "2", builder -> {
912912
builder.timeField("test_date", null);
@@ -940,7 +940,7 @@ public void testGettingTimestampWithoutCalendar() throws Exception {
940940
builder.startObject("release_date").field("type", "date").endObject();
941941
builder.startObject("republish_date").field("type", "date").endObject();
942942
});
943-
long randomMillis = randomLong();
943+
long randomMillis = randomNonNegativeLong();
944944

945945
index("library", "1", builder -> {
946946
builder.field("name", "Don Quixote");
@@ -951,7 +951,7 @@ public void testGettingTimestampWithoutCalendar() throws Exception {
951951
index("library", "2", builder -> {
952952
builder.field("name", "1984");
953953
builder.field("page_count", 328);
954-
builder.field("release_date", -649036800000L);
954+
builder.field("release_date", 649036800000L);
955955
builder.field("republish_date", 599616000000L);
956956
});
957957

@@ -970,7 +970,7 @@ public void testGettingTimestampWithoutCalendar() throws Exception {
970970

971971
assertTrue(results.next());
972972
assertEquals(599616000000L, results.getTimestamp("republish_date").getTime());
973-
assertEquals(-649036800000L, ((Timestamp) results.getObject(2)).getTime());
973+
assertEquals(649036800000L, ((Timestamp) results.getObject(2)).getTime());
974974

975975
assertFalse(results.next());
976976
});
@@ -983,7 +983,7 @@ public void testGettingTimestampWithCalendar() throws Exception {
983983
builder.startObject("test_boolean").field("type", "boolean").endObject();
984984
builder.startObject("test_date").field("type", "date").endObject();
985985
});
986-
Long randomLongDate = randomLong();
986+
Long randomLongDate = randomNonNegativeLong();
987987
indexSimpleDocumentWithTrueValues(randomLongDate);
988988
index("test", "2", builder -> {
989989
builder.timeField("test_date", null);
@@ -1022,7 +1022,7 @@ public void testValidGetObjectCalls() throws Exception {
10221022
double d = randomDouble();
10231023
float f = randomFloat();
10241024
boolean randomBool = randomBoolean();
1025-
Long randomLongDate = randomLong();
1025+
Long randomLongDate = randomNonNegativeLong();
10261026
String randomString = randomUnicodeOfCodepointLengthBetween(128, 256);
10271027

10281028
index("test", "1", builder -> {

0 commit comments

Comments
 (0)