Skip to content

Commit

Permalink
Added New DateTime parser implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Prabhat Sharma <[email protected]>
  • Loading branch information
Prabhat Sharma committed Dec 5, 2023
1 parent f7f3500 commit 4c85946
Show file tree
Hide file tree
Showing 7 changed files with 865 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.common.time;

import java.text.Format;
import java.text.ParsePosition;
import java.time.ZoneId;
import java.time.chrono.Chronology;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
import java.util.Locale;

public class CustomDateTimeFormatter {
private final DateTimeFormatter formatter;

public CustomDateTimeFormatter(String pattern) {
this.formatter = DateTimeFormatter.ofPattern(pattern, Locale.ROOT);
}

public CustomDateTimeFormatter(String pattern, Locale locale) {
this.formatter = DateTimeFormatter.ofPattern(pattern, locale);
}

public CustomDateTimeFormatter(DateTimeFormatter formatter) {
this.formatter = formatter;
}

public static CustomDateTimeFormatter ofPattern(String pattern) {
return new CustomDateTimeFormatter(pattern);
}

public static CustomDateTimeFormatter ofPattern(String pattern, Locale locale) {
return new CustomDateTimeFormatter(pattern, locale);
}

public static final CustomDateTimeFormatter ISO_LOCAL_DATE = new CustomDateTimeFormatter(DateTimeFormatter.ISO_LOCAL_DATE);
public static final CustomDateTimeFormatter ISO_LOCAL_TIME = new CustomDateTimeFormatter(DateTimeFormatter.ISO_LOCAL_TIME);
public static final CustomDateTimeFormatter ISO_LOCAL_DATE_TIME = new CustomDateTimeFormatter(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
public static final CustomDateTimeFormatter ISO_INSTANT = new CustomDateTimeFormatter(DateTimeFormatter.ISO_INSTANT);
public static final CustomDateTimeFormatter ISO_OFFSET_DATE_TIME = new CustomDateTimeFormatter(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
public static final CustomDateTimeFormatter ISO_ZONED_DATE_TIME = new CustomDateTimeFormatter(DateTimeFormatter.ISO_ZONED_DATE_TIME);
public static final CustomDateTimeFormatter ISO_DATE = new CustomDateTimeFormatter(DateTimeFormatter.ISO_DATE);
public static final CustomDateTimeFormatter ISO_TIME = new CustomDateTimeFormatter(DateTimeFormatter.ISO_TIME);
public static final CustomDateTimeFormatter ISO_OFFSET_TIME = new CustomDateTimeFormatter(DateTimeFormatter.ISO_OFFSET_TIME);
public static final CustomDateTimeFormatter ISO_OFFSET_DATE = new CustomDateTimeFormatter(DateTimeFormatter.ISO_OFFSET_DATE);
public static final CustomDateTimeFormatter ISO_ORDINAL_DATE = new CustomDateTimeFormatter(DateTimeFormatter.ISO_ORDINAL_DATE);
public static final CustomDateTimeFormatter ISO_WEEK_DATE = new CustomDateTimeFormatter(DateTimeFormatter.ISO_WEEK_DATE);
public static final CustomDateTimeFormatter ISO_DATE_TIME = new CustomDateTimeFormatter(DateTimeFormatter.ISO_DATE_TIME);
public static final CustomDateTimeFormatter BASIC_ISO_DATE = new CustomDateTimeFormatter(DateTimeFormatter.BASIC_ISO_DATE);

public CustomDateTimeFormatter withLocale(Locale locale) {
return new CustomDateTimeFormatter(getFormatter().withLocale(locale));
}

public CustomDateTimeFormatter withZone(ZoneId zoneId) {
return new CustomDateTimeFormatter(getFormatter().withZone(zoneId));
}

public CustomDateTimeFormatter withChronology(Chronology chrono) {
return new CustomDateTimeFormatter(getFormatter().withChronology(chrono));
}

public String format(TemporalAccessor temporal) {
return this.getFormatter().format(temporal);
}

public TemporalAccessor parse(CharSequence text, ParsePosition position) {
return this.getFormatter().parse(text, position);
}

public TemporalAccessor parse(CharSequence text) {
return this.getFormatter().parse(text);
}

public <T> T parse(CharSequence text, TemporalQuery<T> query) {
return this.getFormatter().parse(text, query);
}

public ZoneId getZone() {
return this.getFormatter().getZone();
}

public Locale getLocale() {
return this.getFormatter().getLocale();
}

public TemporalAccessor parse(String input) {
return formatter.parse(input);
}

public DateTimeFormatter getFormatter() {
return formatter;
}

public Format toFormat() {
return getFormatter().toFormat();
}

public Object parseObject(String text, ParsePosition pos) {
return getFormatter().toFormat().parseObject(text, pos);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1299,6 +1299,41 @@ public class DateFormatters {
.withResolverStyle(ResolverStyle.STRICT)
);

/**
* Returns RFC 3339 a popular ISO 8601 profile compatible date time formatter and parser.
* This is not fully compatible to the existing spec, its more linient and closely follows w3c note on datetime
*/

public static final DateFormatter RFC3339_DATE_FORMATTER = new JavaDateFormatter(
"rfc3339_date_time",
new CustomDateTimeFormatter(STRICT_DATE_OPTIONAL_TIME_PRINTER),
new RFC3339Parser(
new DateTimeFormatterBuilder().append(DATE_FORMATTER)
.optionalStart()
.appendLiteral('T')
.appendValue(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE)
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 1, 2, SignStyle.NOT_NEGATIVE)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE)
.optionalStart()
.appendFraction(NANO_OF_SECOND, 1, 9, true)
.optionalEnd()
.optionalStart()
.appendLiteral(',')
.appendFraction(NANO_OF_SECOND, 1, 9, false)
.optionalEnd()
.optionalStart()
.appendOffsetId()
.optionalEnd()
.optionalEnd()
.optionalEnd()
.toFormatter(Locale.ROOT)
.withResolverStyle(ResolverStyle.STRICT)
)
);

private static final DateTimeFormatter HOUR_MINUTE_SECOND_FORMATTER = new DateTimeFormatterBuilder().append(HOUR_MINUTE_FORMATTER)
.appendLiteral(":")
.appendValue(SECOND_OF_MINUTE, 1, 2, SignStyle.NOT_NEGATIVE)
Expand Down Expand Up @@ -2152,6 +2187,8 @@ static DateFormatter forPattern(String input) {
return STRICT_YEAR_MONTH;
} else if (FormatNames.STRICT_YEAR_MONTH_DAY.matches(input)) {
return STRICT_YEAR_MONTH_DAY;
} else if (FormatNames.RFC3339_DATE_TIME.matches(input)) {
return RFC3339_DATE_FORMATTER;
} else {
try {
return new JavaDateFormatter(
Expand Down
225 changes: 225 additions & 0 deletions server/src/main/java/org/opensearch/common/time/DateTime.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.common.time;

import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.Year;
import java.time.ZoneOffset;
import java.time.temporal.ChronoField;
import java.util.Objects;
import java.util.Optional;

/**
* Container class for parsed date/date-time data.
*/
public class DateTime {
private final int year;
private final int month;
private final int day;
private final int hour;
private final int minute;
private final int second;
private final int nano;
private final ZoneOffset offset;
private final int fractionDigits;

public DateTime(
final int year,
final int month,
final int day,
final int hour,
final int minute,
final int second,
final int nano,
final ZoneOffset offset,
final int fractionDigits
) {
this.year = year;
this.month = assertSize(month, 1, 12, ChronoField.MONTH_OF_YEAR);
this.day = assertSize(day, 1, 31, ChronoField.DAY_OF_MONTH);
this.hour = assertSize(hour, 0, 23, ChronoField.HOUR_OF_DAY);
this.minute = assertSize(minute, 0, 59, ChronoField.MINUTE_OF_HOUR);
this.second = assertSize(second, 0, 60, ChronoField.SECOND_OF_MINUTE);
this.nano = assertSize(nano, 0, 999_999_999, ChronoField.NANO_OF_SECOND);
this.offset = offset;
this.fractionDigits = fractionDigits;
}

/**
* Create a new instance with second granularity from the input parameters
*/
public static DateTime of(int year, int month, int day, int hour, int minute, int second, ZoneOffset offset) {
return new DateTime(year, month, day, hour, minute, second, 0, offset, 0);
}

/**
* Create a new instance with nanosecond granularity from the input parameters
*/
public static DateTime of(
int year,
int month,
int day,
int hour,
int minute,
int second,
int nanos,
ZoneOffset offset,
final int fractionDigits
) {
return new DateTime(year, month, day, hour, minute, second, nanos, offset, fractionDigits);
}

/**
* Create a new instance with year granularity from the input parameters
*/
public static DateTime ofYear(int year) {
return new DateTime(year, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC, 0);
}

/**
* Create a new instance with year-month granularity from the input parameters
*/
public static DateTime ofYearMonth(int years, int months) {
return new DateTime(years, months, 1, 0, 0, 0, 0, ZoneOffset.UTC, 0);
}

/**
* Create a new instance with day granularity from the input parameters
*/
public static DateTime ofDate(int years, int months, int days) {
return new DateTime(years, months, days, 0, 0, 0, 0, ZoneOffset.UTC, 0);
}

/**
* Create a new instance with minute granularity from the input parameters
*/
public static DateTime of(int years, int months, int days, int hours, int minute, ZoneOffset offset) {
return new DateTime(years, months, days, hours, minute, 0, 0, offset, 0);
}

public static DateTime of(int years, int months, int days, int hours, int minute) {
return new DateTime(years, months, days, hours, minute, 0, 0, ZoneOffset.UTC, 0);
}

private int assertSize(int value, int min, int max, ChronoField field) {
if (value > max) {
throw new DateTimeException("Field " + field.name() + " out of bounds. Expected " + min + "-" + max + ", got " + value);
}
return value;
}

public int getYear() {
return year;
}

public int getMonth() {
return month;
}

public int getDayOfMonth() {
return day;
}

public int getHour() {
return hour;
}

public int getMinute() {
return minute;
}

public int getSecond() {
return second;
}

public int getNano() {
return nano;
}

/**
* Returns the time offset, if available
*
* @return the time offset, if available
*/
public Optional<ZoneOffset> getOffset() {
return Optional.ofNullable(offset);
}

/**
* Creates a {@link Year} discarding any higher granularity fields
*
* @return the {@link Year}
*/
public Year toYear() {
return Year.of(year);
}

/**
* Creates an {@link OffsetDateTime}
*
* @return the {@link OffsetDateTime}
*/
public OffsetDateTime toOffsetDatetime() {
if (offset != null) {
return OffsetDateTime.of(year, month, day, hour, minute, second, nano, offset);
}
throw new DateTimeException("No zone offset information found");
}

/**
* Creates a {@link LocalDate}, discarding any higher granularity fields
*
* @return the {@link LocalDate}
*/
public LocalDate toLocalDate() {
return LocalDate.of(year, month, day);
}

/**
* Return the number of significant fraction digits in the second.
*
* @return The number of significant fraction digits
*/
public int getFractionDigits() {
return fractionDigits;
}

/**
* * @hidden
*/
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DateTime dateTime = (DateTime) o;
return year == dateTime.year
&& month == dateTime.month
&& day == dateTime.day
&& hour == dateTime.hour
&& minute == dateTime.minute
&& second == dateTime.second
&& nano == dateTime.nano
&& fractionDigits == dateTime.fractionDigits
&& Objects.equals(offset, dateTime.offset);
}

/**
* @hidden
*/
@Override
public int hashCode() {
return Objects.hash(year, month, day, hour, minute, second, nano, offset, fractionDigits);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
*/
public enum FormatNames {
ISO8601(null, "iso8601"),
RFC3339_DATE_TIME(null, "rfc3339"),
BASIC_DATE("basicDate", "basic_date"),
BASIC_DATE_TIME("basicDateTime", "basic_date_time"),
BASIC_DATE_TIME_NO_MILLIS("basicDateTimeNoMillis", "basic_date_time_no_millis"),
Expand Down
Loading

0 comments on commit 4c85946

Please sign in to comment.