Deduplicate two similar TimeUtils classes. (#43697)

* Deduplicate org.elasticsearch.xpack.core.dataframe.utils.TimeUtils and org.elasticsearch.xpack.core.ml.utils.time.TimeUtils into a common class: org.elasticsearch.xpack.core.common.time.TimeUtils.
* Add unit tests for parseTimeField and parseTimeFieldToInstant methods
This commit is contained in:
Przemysław Witek 2019-06-27 18:51:48 +02:00 committed by GitHub
parent d46e2bb26a
commit 68dbbd8793
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 71 additions and 159 deletions

View File

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.core.dataframe.utils;
package org.elasticsearch.xpack.core.common.time;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.ParseField;
@ -26,7 +26,7 @@ public final class TimeUtils {
if (parser.currentToken() == XContentParser.Token.VALUE_NUMBER) {
return new Date(parser.longValue());
} else if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
return new Date(TimeUtils.dateStringToEpoch(parser.text()));
return new Date(dateStringToEpoch(parser.text()));
}
throw new IllegalArgumentException(
"unexpected token [" + parser.currentToken() + "] for [" + fieldName + "]");
@ -36,7 +36,7 @@ public final class TimeUtils {
if (parser.currentToken() == XContentParser.Token.VALUE_NUMBER) {
return Instant.ofEpochMilli(parser.longValue());
} else if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
return Instant.ofEpochMilli(TimeUtils.dateStringToEpoch(parser.text()));
return Instant.ofEpochMilli(dateStringToEpoch(parser.text()));
}
throw new IllegalArgumentException(
"unexpected token [" + parser.currentToken() + "] for [" + fieldName + "]");
@ -123,8 +123,6 @@ public final class TimeUtils {
}
}
/**
* Check the given {@code timeValue} is a multiple of the {@code baseUnit}
*/

View File

@ -11,8 +11,8 @@ import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.xpack.core.common.notifications.AbstractAuditMessage;
import org.elasticsearch.xpack.core.common.notifications.Level;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import org.elasticsearch.xpack.core.dataframe.DataFrameField;
import org.elasticsearch.xpack.core.dataframe.utils.TimeUtils;
import java.util.Date;

View File

@ -20,11 +20,11 @@ import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParserUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import org.elasticsearch.xpack.core.dataframe.DataFrameField;
import org.elasticsearch.xpack.core.dataframe.DataFrameMessages;
import org.elasticsearch.xpack.core.dataframe.transforms.pivot.PivotConfig;
import org.elasticsearch.xpack.core.dataframe.utils.ExceptionsHelper;
import org.elasticsearch.xpack.core.dataframe.utils.TimeUtils;
import java.io.IOException;
import java.time.Instant;

View File

@ -13,7 +13,7 @@ import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.Date;

View File

@ -23,7 +23,7 @@ import org.elasticsearch.xpack.core.ml.job.messages.Messages;
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.core.ml.utils.Intervals;
import org.elasticsearch.xpack.core.ml.utils.ToXContentParams;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.time.Instant;

View File

@ -34,7 +34,7 @@ import org.elasticsearch.xpack.core.ml.utils.MlStrings;
import org.elasticsearch.xpack.core.ml.utils.QueryProvider;
import org.elasticsearch.xpack.core.ml.utils.ToXContentParams;
import org.elasticsearch.xpack.core.ml.utils.XContentObjectTransformer;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -17,7 +17,7 @@ import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.Objects;

View File

@ -18,7 +18,7 @@ import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.job.messages.Messages;
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -26,7 +26,7 @@ import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndexFiel
import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.DataCounts;
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.core.ml.utils.MlStrings;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -13,7 +13,7 @@ import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.Date;

View File

@ -14,7 +14,7 @@ import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.Date;

View File

@ -16,7 +16,7 @@ import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.job.results.Result;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.Date;

View File

@ -22,7 +22,7 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.io.InputStream;

View File

@ -17,7 +17,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.job.config.Detector;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import org.elasticsearch.Version;
import java.io.IOException;

View File

@ -16,7 +16,7 @@ import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.ArrayList;

View File

@ -15,7 +15,7 @@ import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.Date;

View File

@ -14,7 +14,7 @@ import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.Date;

View File

@ -15,7 +15,7 @@ import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.Date;

View File

@ -15,7 +15,7 @@ import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.io.IOException;
import java.util.Date;

View File

@ -11,7 +11,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.xpack.core.common.notifications.AbstractAuditMessage;
import org.elasticsearch.xpack.core.common.notifications.Level;
import org.elasticsearch.xpack.core.ml.job.config.Job;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.util.Date;

View File

@ -1,129 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.core.ml.utils.time;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.DateFieldMapper;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public final class TimeUtils {
private TimeUtils() {
// Do nothing
}
public static Date parseTimeField(XContentParser parser, String fieldName) throws IOException {
if (parser.currentToken() == XContentParser.Token.VALUE_NUMBER) {
return new Date(parser.longValue());
} else if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
return new Date(TimeUtils.dateStringToEpoch(parser.text()));
}
throw new IllegalArgumentException(
"unexpected token [" + parser.currentToken() + "] for [" + fieldName + "]");
}
/**
* First tries to parse the date first as a Long and convert that to an
* epoch time. If the long number has more than 10 digits it is considered a
* time in milliseconds else if 10 or less digits it is in seconds. If that
* fails it tries to parse the string using
* {@link DateFieldMapper#DEFAULT_DATE_TIME_FORMATTER}
*
* If the date string cannot be parsed -1 is returned.
*
* @return The epoch time in milliseconds or -1 if the date cannot be
* parsed.
*/
public static long dateStringToEpoch(String date) {
try {
long epoch = Long.parseLong(date);
if (date.trim().length() <= 10) { // seconds
return epoch * 1000;
} else {
return epoch;
}
} catch (NumberFormatException nfe) {
// not a number
}
try {
return DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parseMillis(date);
} catch (ElasticsearchParseException | IllegalArgumentException e) {
}
// Could not do the conversion
return -1;
}
/**
* Checks that the given {@code timeValue} is a non-negative multiple value of the {@code baseUnit}.
*
* <ul>
* <li>400ms is valid for base unit of seconds</li>
* <li>450ms is invalid for base unit of seconds but valid for base unit of milliseconds</li>
* </ul>
*/
public static void checkNonNegativeMultiple(TimeValue timeValue, TimeUnit baseUnit, ParseField field) {
checkNonNegative(timeValue, field);
checkMultiple(timeValue, baseUnit, field);
}
/**
* Checks that the given {@code timeValue} is a positive multiple value of the {@code baseUnit}.
*
* <ul>
* <li>400ms is valid for base unit of seconds</li>
* <li>450ms is invalid for base unit of seconds but valid for base unit of milliseconds</li>
* </ul>
*/
public static void checkPositiveMultiple(TimeValue timeValue, TimeUnit baseUnit, ParseField field) {
checkPositive(timeValue, field);
checkMultiple(timeValue, baseUnit, field);
}
/**
* Checks that the given {@code timeValue} is positive.
*
* <ul>
* <li>1s is valid</li>
* <li>-1s is invalid</li>
* </ul>
*/
public static void checkPositive(TimeValue timeValue, ParseField field) {
long nanos = timeValue.getNanos();
if (nanos <= 0) {
throw new IllegalArgumentException(field.getPreferredName() + " cannot be less or equal than 0. Value = "
+ timeValue.toString());
}
}
private static void checkNonNegative(TimeValue timeValue, ParseField field) {
long nanos = timeValue.getNanos();
if (nanos < 0) {
throw new IllegalArgumentException(field.getPreferredName() + " cannot be less than 0. Value = " + timeValue.toString());
}
}
/**
* Check the given {@code timeValue} is a multiple of the {@code baseUnit}
*/
public static void checkMultiple(TimeValue timeValue, TimeUnit baseUnit, ParseField field) {
long nanos = timeValue.getNanos();
TimeValue base = new TimeValue(1, baseUnit);
long baseNanos = base.getNanos();
if (nanos % baseNanos != 0) {
throw new IllegalArgumentException(field.getPreferredName() + " has to be a multiple of " + base.toString() + "; actual was '"
+ timeValue.toString() + "'");
}
}
}

View File

@ -10,7 +10,7 @@ import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import org.junit.Before;
import java.util.Date;

View File

@ -3,18 +3,61 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ml.utils.time;
package org.elasticsearch.xpack.core.common.time;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import java.io.IOException;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
public class TimeUtilsTests extends ESTestCase {
public void testdateStringToEpoch() {
public void testParseTimeField() throws IOException {
try (XContentParser parser = createParser(JsonXContent.jsonXContent, "123456789")) {
parser.nextToken();
Date date = TimeUtils.parseTimeField(parser, "my_time_field");
assertThat(date.getTime(), equalTo(123456789L));
}
try (XContentParser parser = createParser(JsonXContent.jsonXContent, "\"2016-05-01T10:00:00.333-0030\"")) {
parser.nextToken();
Date date = TimeUtils.parseTimeField(parser, "my_time_field");
assertThat(date.getTime(), equalTo(1462098600333L));
}
try (XContentParser parser = createParser(JsonXContent.jsonXContent, "{}")) {
parser.nextToken();
Exception e = expectThrows(IllegalArgumentException.class, () -> TimeUtils.parseTimeField(parser, "my_time_field"));
assertThat(e.getMessage(), containsString("unexpected token [START_OBJECT] for [my_time_field]"));
}
}
public void testParseTimeFieldToInstant() throws IOException {
try (XContentParser parser = createParser(JsonXContent.jsonXContent, "123456789")) {
parser.nextToken();
Instant instant = TimeUtils.parseTimeFieldToInstant(parser, "my_time_field");
assertThat(instant.toEpochMilli(), equalTo(123456789L));
}
try (XContentParser parser = createParser(JsonXContent.jsonXContent, "\"2016-05-01T10:00:00.333-0030\"")) {
parser.nextToken();
Instant instant = TimeUtils.parseTimeFieldToInstant(parser, "my_time_field");
assertThat(instant.toEpochMilli(), equalTo(1462098600333L));
}
try (XContentParser parser = createParser(JsonXContent.jsonXContent, "{}")) {
parser.nextToken();
Exception e = expectThrows(IllegalArgumentException.class, () -> TimeUtils.parseTimeFieldToInstant(parser, "my_time_field"));
assertThat(e.getMessage(), containsString("unexpected token [START_OBJECT] for [my_time_field]"));
}
}
public void testDateStringToEpoch() {
assertEquals(1462096800000L, TimeUtils.dateStringToEpoch("2016-05-01T10:00:00Z"));
assertEquals(1462096800333L, TimeUtils.dateStringToEpoch("2016-05-01T10:00:00.333Z"));
assertEquals(1462096800334L, TimeUtils.dateStringToEpoch("2016-05-01T10:00:00.334+00"));

View File

@ -9,7 +9,7 @@ import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.xpack.core.ml.job.messages.Messages;
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.util.Objects;

View File

@ -9,7 +9,7 @@ import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.xpack.core.ml.job.messages.Messages;
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.core.ml.utils.time.TimeUtils;
import org.elasticsearch.xpack.core.common.time.TimeUtils;
import java.util.Objects;