org.embulk.util.timestamp.JavaTimestampFormatter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of embulk-util-timestamp Show documentation
Show all versions of embulk-util-timestamp Show documentation
Timestamp parser and formatter for Embulk and Embulk plugins
The newest version!
package org.embulk.util.timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.Locale;
class JavaTimestampFormatter extends TimestampFormatter {
JavaTimestampFormatter(final String pattern, final ZoneOffset defaultZoneOffset) {
this.formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern(pattern)
.toFormatter(Locale.ENGLISH)
.withResolverStyle(ResolverStyle.STRICT);
this.pattern = pattern;
this.defaultZoneOffset = defaultZoneOffset;
}
@Override
public final String format(final Instant instant) {
if (instant == null) {
throw new NullPointerException("instant is null.");
}
return this.formatter.format(instant.atOffset(this.defaultZoneOffset));
}
@Override
public final Instant parse(final String text) {
if (text == null) {
throw new DateTimeParseException("text is null.", text, 0, new NullPointerException());
} else if (text.isEmpty()) {
throw new DateTimeParseException("text is empty.", text, 0);
}
final TemporalAccessor temporal = this.formatter.parse(text);
return this.buildOffsetDateTime(temporal, text).toInstant();
}
private OffsetDateTime buildOffsetDateTime(final TemporalAccessor given, final String text) {
// Non-offset (string-ish) time zone IDs are intentionally rejected by JavaTimestampFormatter.
// because their meanings are not stable per the tz database.
final ZoneId givenZoneId = given.query(TemporalQueries.zoneId());
if (givenZoneId != null) {
throw new DateTimeParseException("Non-offset zone IDs are unaccepted in 'java:' formats: " + this.pattern, text, 0);
}
final ZoneOffset givenZoneOffset = given.query(TemporalQueries.offset());
if (given.isSupported(ChronoField.EPOCH_DAY) && given.isSupported(ChronoField.NANO_OF_DAY)) {
// Normal fastest cases: Embulk expects the record has full date and time information in most cases.
if (givenZoneOffset != null) {
return OffsetDateTime.from(given);
} else {
return OffsetDateTime.of(LocalDateTime.from(given), this.defaultZoneOffset);
}
}
// Exceptional cases: the record does not have full date and time information.
final LocalDate dateFromDefault;
if (given.isSupported(ChronoField.EPOCH_DAY)) {
dateFromDefault = LocalDate.from(given);
} else {
final int year;
if (given.isSupported(ChronoField.YEAR)) {
year = given.get(ChronoField.YEAR);
} else if (given.isSupported(ChronoField.YEAR_OF_ERA)) {
year = given.get(ChronoField.YEAR_OF_ERA);
} else {
year = 1970;
}
if (given.isSupported(ChronoField.DAY_OF_YEAR)) {
dateFromDefault = LocalDate.ofYearDay(year, given.get(ChronoField.DAY_OF_YEAR));
} else {
final int month;
if (given.isSupported(ChronoField.MONTH_OF_YEAR)) {
month = given.get(ChronoField.MONTH_OF_YEAR);
} else {
month = 1;
}
final int dayOfMonth;
if (given.isSupported(ChronoField.DAY_OF_MONTH)) {
dayOfMonth = given.get(ChronoField.DAY_OF_MONTH);
} else {
dayOfMonth = 1;
}
dateFromDefault = LocalDate.of(year, month, dayOfMonth);
}
}
final LocalTime timeFromDefault;
if (given.isSupported(ChronoField.NANO_OF_DAY)) {
timeFromDefault = LocalTime.from(given);
} else {
if (given.isSupported(ChronoField.NANO_OF_DAY)) {
timeFromDefault = LocalTime.ofNanoOfDay(given.getLong(ChronoField.NANO_OF_DAY));
} else if (given.isSupported(ChronoField.MILLI_OF_DAY)) {
timeFromDefault = LocalTime.ofNanoOfDay(given.getLong(ChronoField.MILLI_OF_DAY) * 1000000L);
} else if (given.isSupported(ChronoField.SECOND_OF_DAY)) {
timeFromDefault = LocalTime.ofSecondOfDay(given.getLong(ChronoField.SECOND_OF_DAY));
} else if (given.isSupported(ChronoField.MINUTE_OF_DAY)) {
timeFromDefault = LocalTime.ofSecondOfDay(given.getLong(ChronoField.MINUTE_OF_DAY) * 60L);
} else {
final int hourOfDay;
if (given.isSupported(ChronoField.HOUR_OF_DAY)) {
hourOfDay = given.get(ChronoField.HOUR_OF_DAY);
} else if (given.isSupported(ChronoField.CLOCK_HOUR_OF_DAY)) {
hourOfDay = given.get(ChronoField.CLOCK_HOUR_OF_DAY) % 24;
} else if (given.isSupported(ChronoField.AMPM_OF_DAY)) {
final int ampmOfDay = given.get(ChronoField.AMPM_OF_DAY) * 12;
if (given.isSupported(ChronoField.HOUR_OF_AMPM)) {
hourOfDay = given.get(ChronoField.HOUR_OF_AMPM) + ampmOfDay;
} else if (given.isSupported(ChronoField.CLOCK_HOUR_OF_AMPM)) {
hourOfDay = given.get(ChronoField.CLOCK_HOUR_OF_AMPM) + ampmOfDay;
} else {
hourOfDay = 0;
}
} else {
hourOfDay = 0;
}
final int minuteOfHour;
if (given.isSupported(ChronoField.MINUTE_OF_HOUR)) {
minuteOfHour = given.get(ChronoField.MINUTE_OF_HOUR);
} else {
minuteOfHour = 0;
}
final int secondOfMinute;
if (given.isSupported(ChronoField.SECOND_OF_MINUTE)) {
secondOfMinute = given.get(ChronoField.SECOND_OF_MINUTE);
} else {
secondOfMinute = 0;
}
final int nanoOfSecond;
if (given.isSupported(ChronoField.NANO_OF_SECOND)) {
nanoOfSecond = given.get(ChronoField.NANO_OF_SECOND);
} else if (given.isSupported(ChronoField.MICRO_OF_SECOND)) {
nanoOfSecond = given.get(ChronoField.MICRO_OF_SECOND) * 1000;
} else if (given.isSupported(ChronoField.MILLI_OF_SECOND)) {
nanoOfSecond = given.get(ChronoField.MILLI_OF_SECOND) * 1000000;
} else {
nanoOfSecond = 0;
}
timeFromDefault = LocalTime.of(hourOfDay, minuteOfHour, secondOfMinute, nanoOfSecond);
}
}
if (givenZoneOffset != null) {
return OffsetDateTime.of(dateFromDefault, timeFromDefault, givenZoneOffset);
} else {
return OffsetDateTime.of(dateFromDefault, timeFromDefault, this.defaultZoneOffset);
}
}
private final DateTimeFormatter formatter;
private final String pattern;
private final ZoneOffset defaultZoneOffset;
}