com.arakelian.core.utils.DateUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of more-commons Show documentation
Show all versions of more-commons Show documentation
Small utility classes useful in a variety of projects. Don't reinvent the wheel. Stay small.
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arakelian.core.utils;
import static java.time.format.DateTimeFormatter.ISO_DATE;
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE;
import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import static java.time.format.TextStyle.FULL;
import static java.time.format.TextStyle.SHORT;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.NANO_OF_SECOND;
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
import static java.time.temporal.ChronoField.YEAR;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.format.SignStyle;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalQuery;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
/**
* Generic date parsing and conversion utilities
*
* Note: Consider at some point removing dependency on joda-time and switching to Java 8 time.
*
* @see http://time4j.net/tutorial/appendix.html
**/
public class DateUtils {
public enum EpochUnits {
NANOSECONDS() {
@Override
public boolean isValid(final long epoch) {
// 10^16
// October 31, 1966 or March 3, 1973
return epoch <= -10_000_000_000_000_000L || epoch >= 10_000_000_000_000_000L;
}
@Override
public long toMillis(final long value) {
return value / 1_000_000;
}
},
MICROSECONDS() {
@Override
public boolean isValid(final long epoch) {
// 10^14
// October 31, 1966 or March 3, 1973
return epoch <= -100_000_000_000_000L || epoch >= 100_000_000_000_000L;
}
@Override
public long toMillis(final long value) {
return value / 1000;
}
},
MILLISECONDS {
@Override
public boolean isValid(final long epoch) {
// January 18, 1969 or March 3, 1973
return epoch <= -30_000_000_000L || epoch >= 100_000_000_000L;
}
@Override
public long toMillis(final long value) {
return value;
}
},
SECONDS {
@Override
public boolean isValid(final long epoch) {
// there is window of time from 1/18/1969 (-30_000_000_000L) to 3/3/1973
// (100_000_000_000L) that will be assumed to be seconds.
return true;
}
@Override
public long toMillis(final long value) {
return value * 1000;
}
};
/**
* Tries to get guess units from given value.
*
* For example, Unix timestamps are the number of SECONDS since January 1, 1970, while Java
* Dates are number of MILLISECONDS since January 1, 1970.
*
* @param epoch
* epoch value
* @return units
*/
public static EpochUnits valueOf(final long epoch) {
if (NANOSECONDS.isValid(epoch)) {
return NANOSECONDS;
} else if (MICROSECONDS.isValid(epoch)) {
return MICROSECONDS;
} else if (MILLISECONDS.isValid(epoch)) {
return MILLISECONDS;
} else {
return SECONDS;
}
}
public abstract boolean isValid(final long epoch);
public Instant toInstant(final long value) {
final long epochMillis = toMillis(value);
return Instant.ofEpochMilli(epochMillis);
}
public abstract long toMillis(long value);
}
private static final String SLASH = "/";
private static final String DASH = "-";
private static final String SPACE = " ";
private static final String COMMA = ", ";
private static final Logger LOGGER = LoggerFactory.getLogger(DateUtils.class);
/**
* Customized ISO 8601 formatter which ensures that milliseconds use the same number of digit
* positions, even if it is zero, or ends with zeros; the default Java 8 versions will remove or
* trim them in those situations
**/
private static final DateTimeFormatter ISO_8601_NANOS = new DateTimeFormatterBuilder() //
.appendValue(ChronoField.YEAR_OF_ERA, 4, 19, SignStyle.EXCEEDS_PAD) //
.appendLiteral('-') //
.appendValue(ChronoField.MONTH_OF_YEAR, 2) //
.appendLiteral('-') //
.appendValue(ChronoField.DAY_OF_MONTH, 2) //
.appendLiteral('T') //
.appendValue(ChronoField.HOUR_OF_DAY, 2) //
.appendLiteral(':') //
.appendValue(ChronoField.MINUTE_OF_HOUR, 2) //
.optionalStart() //
.appendLiteral(':') //
.appendValue(ChronoField.SECOND_OF_MINUTE, 2) //
.optionalStart() //
.appendFraction(ChronoField.NANO_OF_SECOND, 9, 9, true) //
.optionalEnd() //
.optionalEnd() //
.appendOffset("+HHmm", "Z") //
.toFormatter();
/** Number of milliseconds per day **/
private static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
/** Thread-safe time only **/
private static final DateTimeFormatter TIME = build(
builder -> builder //
.appendValue(HOUR_OF_DAY, 2) //
.appendLiteral(':') //
.appendValue(MINUTE_OF_HOUR, 2) //
.optionalStart() //
.appendLiteral(':') //
.appendValue(SECOND_OF_MINUTE, 2) //
.optionalStart() //
.appendFraction(NANO_OF_SECOND, 0, 9, true));
/** Thread-safe Date with time (without seconds) **/
public static final DateTimeFormatter MMDDYYYY_HHMM = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm a");
/** Thread-safe Date with time (including seconds) **/
public static final DateTimeFormatter MMDDYYYY_HHMMSS = DateTimeFormatter
.ofPattern("MM/dd/yyyy hh:mm:ss a");
/** Thread-safe YYYY/MM/DD formatter */
public static final DateTimeFormatter YYYY_MM = DateTimeFormatter.ofPattern("yyyy/MM");
/** Thread-safe YYYY/MM/DD formatter */
public static final DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy/MM/dd");
/** Thread-safe M/D/YY formatter */
public static final DateTimeFormatter M_D_YY = DateTimeFormatter.ofPattern("M/d/yy");
/** Thread-safe Sat M/D/YY formatter */
public static final DateTimeFormatter EEE_M_D_YY = DateTimeFormatter.ofPattern("EEE M/d/yy");
/** Thread-safe HH:MM am/pm */
public static final DateTimeFormatter H_MM_ampm = DateTimeFormatter.ofPattern("h:mma");
@SuppressWarnings("ReturnValueIgnored")
private static final DateTimeFormatter ZONED_DATE_TIME_PARSER = build( //
builder -> builder //
.appendPattern("[uuuu-MM-dd'T'HH:mm:ss.SSSZZZ]") //
// more forgiving than ISO_ZONED_DATE_TIME, so listed first
.optionalStart().append(yearMonthDay(null, DASH, DASH, 4, true)).optionalEnd() //
// all of ISO stuff uses hyphens as separators; before adding another,
// check if it is extended by a pattern listed here
.optionalStart().append(ISO_ZONED_DATE_TIME).optionalEnd() //
.optionalStart().append(ISO_LOCAL_DATE_TIME).optionalEnd() //
.optionalStart().append(ISO_DATE).optionalEnd() //
.optionalStart().append(ISO_OFFSET_DATE).optionalEnd() //
.optionalStart().append(RFC_1123_DATE_TIME).optionalEnd() //
// month number + day + 4 digit year
.optionalStart().append(monthDayYear(null, SLASH, SLASH, 4, true)).optionalEnd() //
.optionalStart().append(monthDayYear(null, SLASH, SLASH, 4, false)).optionalEnd() //
.optionalStart().append(monthDayYear(null, DASH, DASH, 4, true)).optionalEnd() //
.optionalStart().append(monthDayYear(null, DASH, DASH, 4, false)).optionalEnd() //
// 4 digit year + month + day
.optionalStart().append(yearMonthDay(null, SLASH, SLASH, 4, true)).optionalEnd() //
.optionalStart().append(yearMonthDay(null, SLASH, SLASH, 4, false)).optionalEnd() //
.optionalStart().append(yearMonthDay(FULL, "", "", 4, false)).optionalEnd() //
.optionalStart().append(yearMonthDay(SHORT, "", "", 4, false)).optionalEnd() //
// day + month name + 4 digit year
.optionalStart().append(dayMonthYear(FULL, DASH, DASH, 4)).optionalEnd() //
.optionalStart().append(dayMonthYear(FULL, SPACE, SPACE, 4)).optionalEnd() //
.optionalStart().append(dayMonthYear(FULL, SPACE, COMMA, 4)).optionalEnd() //
.optionalStart().append(dayMonthYear(SHORT, DASH, DASH, 4)).optionalEnd() //
.optionalStart().append(dayMonthYear(SHORT, SPACE, SPACE, 4)).optionalEnd() //
.optionalStart().append(dayMonthYear(SHORT, SPACE, COMMA, 4)).optionalEnd() //
// month name + day + 4 digit year
.optionalStart().append(monthDayYear(FULL, DASH, DASH, 4, false)).optionalEnd() //
.optionalStart().append(monthDayYear(FULL, SPACE, SPACE, 4, false)).optionalEnd() //
.optionalStart().append(monthDayYear(FULL, SPACE, COMMA, 4, false)).optionalEnd() //
.optionalStart().append(monthDayYear(SHORT, DASH, DASH, 4, false)).optionalEnd() //
.optionalStart().append(monthDayYear(SHORT, SPACE, SPACE, 4, false)).optionalEnd() //
.optionalStart().append(monthDayYear(SHORT, SPACE, COMMA, 4, false)).optionalEnd() //
// month number + day + 2 digit year
.optionalStart().append(monthDayYear(null, SLASH, SLASH, 2, true)).optionalEnd() //
.optionalStart().append(monthDayYear(null, SLASH, SLASH, 2, false)).optionalEnd() //
.optionalStart().append(monthDayYear(null, DASH, DASH, 2, true)).optionalEnd() //
.optionalStart().append(monthDayYear(null, DASH, DASH, 2, false)).optionalEnd() //
// day + month name + 2 digit year
.optionalStart().append(dayMonthYear(FULL, DASH, DASH, 2)).optionalEnd() //
.optionalStart().append(dayMonthYear(FULL, SPACE, SPACE, 2)).optionalEnd() //
.optionalStart().append(dayMonthYear(FULL, SPACE, COMMA, 2)).optionalEnd() //
.optionalStart().append(dayMonthYear(SHORT, DASH, DASH, 2)).optionalEnd() //
.optionalStart().append(dayMonthYear(SHORT, SPACE, SPACE, 2)).optionalEnd() //
.optionalStart().append(dayMonthYear(SHORT, SPACE, COMMA, 2)).optionalEnd() //
// month name + day + 2 digit year
.optionalStart().append(monthDayYear(null, DASH, DASH, 2, false)).optionalEnd() //
.optionalStart().append(monthDayYear(FULL, DASH, DASH, 2, false)).optionalEnd() //
.optionalStart().append(monthDayYear(FULL, SPACE, SPACE, 2, false)).optionalEnd() //
.optionalStart().append(monthDayYear(FULL, SPACE, COMMA, 2, false)).optionalEnd() //
.optionalStart().append(monthDayYear(SHORT, DASH, DASH, 2, false)).optionalEnd() //
.optionalStart().append(monthDayYear(SHORT, SPACE, SPACE, 2, false)).optionalEnd() //
.optionalStart().append(monthDayYear(SHORT, SPACE, COMMA, 2, false)).optionalEnd() //
// as a last resort, parse numbers
.appendPattern("[uuuuMMdd]") //
.optionalStart().append(DateTimeFormatter.ISO_INSTANT).optionalEnd());
@SuppressWarnings("ZoneIdOfZ")
public static final ZoneId UTC_ZONE = ZoneId.of("Z");
private static final Random JVM_RANDOM = new Random();
public static ZonedDateTime atStartOfDay(final ZonedDateTime date) {
return date != null ? date.truncatedTo(ChronoUnit.DAYS) : null;
}
private static DateTimeFormatter build(final Consumer consumer) {
final DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder() //
.parseStrict() //
.parseCaseInsensitive();
consumer.accept(builder);
return builder //
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0) //
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) //
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) //
.parseDefaulting(ChronoField.NANO_OF_SECOND, 0) //
.parseDefaulting(ChronoField.ERA, 1) //
.toFormatter() //
.withChronology(IsoChronology.INSTANCE) //
.withResolverStyle(ResolverStyle.STRICT);
}
public static int compare(final Date d1, final Date d2) {
if (d1 == null) {
if (d2 != null) {
return -1;
}
} else if (d2 == null) {
return +1;
} else {
final long t1 = d1.toInstant().toEpochMilli();
final long t2 = d2.toInstant().toEpochMilli();
if (t1 < t2) {
return -1;
} else if (t1 > t2) {
return -1;
}
}
return 0;
}
public static DateTimeFormatter dayMonthYear(
final TextStyle monthStyle,
final String daySeparator,
final String monthSeparator,
final int yearDigits) {
return build(builder -> {
builder.appendValue(DAY_OF_MONTH);
builder.appendLiteral(daySeparator);
if (monthStyle != null) {
builder.appendText(MONTH_OF_YEAR, monthStyle);
} else {
builder.appendValue(MONTH_OF_YEAR);
}
builder.appendLiteral(monthSeparator);
if (yearDigits == 2) {
builder.appendValueReduced(YEAR, 2, 2, LocalDate.now(ZoneId.systemDefault()).minusYears(80));
} else if (yearDigits == 4) {
builder.appendValue(YEAR, 4, 10, SignStyle.NEVER);
} else {
throw new IllegalStateException();
}
});
}
public static boolean hasSameDate(final ZonedDateTime lhs, final ZonedDateTime rhs) {
if (lhs == null) {
return rhs == null;
} else if (rhs == null) {
return false;
}
if ((lhs.getMonth() != rhs.getMonth()) || (lhs.getDayOfMonth() != rhs.getDayOfMonth()) || (lhs.getYear() != rhs.getYear())) {
return false;
}
return true;
}
public static boolean hasTimeComponent(final Date date) {
if (date != null) {
final long epochMillis = date.toInstant().toEpochMilli();
final long timePortion = epochMillis % MILLIS_PER_DAY;
return timePortion != 0;
}
return false;
}
public static boolean hasTimeComponent(final ZonedDateTime dateTime) {
return dateTime != null && (dateTime.getHour() != 0 //
|| dateTime.getMinute() != 0 //
|| dateTime.getSecond() != 0 //
|| dateTime.getNano() != 0);
}
public static boolean isUtc(final ZonedDateTime date) {
if (date == null) {
return false;
}
return UTC_ZONE.equals(date.getZone());
}
public static DateTimeFormatter monthDayYear(
final TextStyle monthStyle,
final String monthSeparator,
final String daySeparator,
final int yearDigits,
final boolean time) {
return build(builder -> {
if (monthStyle != null) {
builder.appendText(MONTH_OF_YEAR, monthStyle);
} else {
builder.appendValue(MONTH_OF_YEAR);
}
builder.appendLiteral(monthSeparator);
builder.appendValue(DAY_OF_MONTH);
builder.appendLiteral(daySeparator);
if (yearDigits == 2) {
// follows 80-20 rule
builder.appendValueReduced(YEAR, 2, 2, LocalDate.now(ZoneId.systemDefault()).minusYears(80));
} else if (yearDigits == 4) {
builder.appendValue(YEAR, 4, 10, SignStyle.NEVER);
} else {
throw new IllegalStateException();
}
if (time) {
builder.appendLiteral(' ').append(TIME);
}
});
}
public static ZonedDateTime nowWithZoneUtc() {
return ZonedDateTime.now(ZoneOffset.UTC);
}
public static T parse(
final String text,
final ZoneId zoneIfNotSpecified,
final TemporalQuery query) {
try {
final T date = parseChecked(text, zoneIfNotSpecified, query);
return date;
} catch (final DateTimeParseException e2) {
// invalid dates are treated as nulls
LOGGER.trace("{}", e2.getMessage());
return null;
}
}
public static T parseChecked(
final String text,
final ZoneId zoneIfNotSpecified,
final TemporalQuery query) throws DateTimeParseException {
if (StringUtils.isBlank(text)) {
return null;
}
try {
// rely upon zone offset being specified in original
final T date = ZONED_DATE_TIME_PARSER.parse(text, query);
return date;
} catch (final DateTimeParseException e1) {
// try with system timezone
final T date = ZONED_DATE_TIME_PARSER.withZone(zoneIfNotSpecified).parse(text, query);
return date;
}
}
public static ZonedDateTime randomZonedDateTimeUtc(
final Random random,
final ZonedDateTime from,
final ZonedDateTime to) {
final long begin = DateUtils.toEpochMillisUtc(from);
final long end = DateUtils.toEpochMillisUtc(to);
final long range = end - begin + 1;
final long epoch = begin + (long) (random.nextDouble() * range);
return DateUtils.toZonedDateTimeUtc(epoch, EpochUnits.MILLISECONDS);
}
public static ZonedDateTime randomZonedDateTimeUtc(final ZonedDateTime from, final ZonedDateTime to) {
return randomZonedDateTimeUtc(JVM_RANDOM, from, to);
}
public static long timeBetween(
final ZonedDateTime firstDate,
final ZonedDateTime secondDate,
final ChronoUnit units) {
Preconditions.checkArgument(units != null, "units must be non-null");
final LocalDate first = toUtc(firstDate).toLocalDate();
final LocalDate after = toUtc(secondDate).toLocalDate();
return Math.abs(units.between(first, after));
}
public static Date toDate(final Instant instant) {
return instant != null ? Date.from(instant) : null;
}
public static Date toDate(final LocalDateTime date) {
return date != null ? toDate(date.atZone(ZoneOffset.systemDefault())) : null;
}
public static Date toDate(final LocalDateTime date, final ZoneOffset offset) {
return date != null ? toDate(date.toInstant(offset)) : null;
}
public static Date toDate(final ZonedDateTime date) {
return date != null ? Date.from(date.toInstant()) : null;
}
public static long toEpochMillisUtc(final ZonedDateTime date) {
Preconditions.checkArgument(date != null, "date must be non-null");
return toUtc(date).toInstant().toEpochMilli();
}
@SuppressWarnings("JavaUtilDate")
private static Instant toInstant(final Date date) {
final Instant instant;
if (date instanceof java.sql.Date) {
// SQL dates do not contain time information and they throw an exception if toInstant()
// is called
instant = Instant.ofEpochMilli(date.getTime());
} else {
instant = date.toInstant();
}
return instant;
}
public static LocalDateTime toLocalDateTime(final String text) {
return parse(text, ZoneOffset.systemDefault(), LocalDateTime::from);
}
public static LocalDateTime toLocalDateTimeChecked(final String text) throws DateTimeParseException {
return parseChecked(text, ZoneOffset.systemDefault(), LocalDateTime::from);
}
public static long toNanos(final int millis) {
return TimeUnit.MILLISECONDS.toNanos(millis);
}
public static String toStringIsoFormat(final String date) {
return toStringIsoFormat(toZonedDateTimeUtc(date));
}
/**
*
* Returns the given ZonedDateTime
in ISO format that readable by a wide range of
* clients.
*
*
*
* In general the format has these components:
*
*
* - A date:
yyyy '-' MM '-' dd
* - A time:
'T' HH ':' mm ':' ss
* - Milliseconds:
('.' | ',') digit+
* - UTC offset:
'Z'
*
*
* Example: 2017-12-29T03:21:24.564000000Z
*
* @param date
* a zoned date/time value
* @return the given ZonedDateTime
in ISO format
*
* @see dateOptionalTimeParser
* @see strict_date_optional_time
*/
public static String toStringIsoFormat(final ZonedDateTime date) {
// we convert given date to UTC timezone and output in UTC format (ends with Z)
return date != null ? date.withZoneSameInstant(ZoneOffset.UTC).format(ISO_8601_NANOS) : null;
}
public static ZonedDateTime toUtc(final ZonedDateTime date) {
return date != null ? date.withZoneSameInstant(ZoneOffset.UTC) : null;
}
public static ZonedDateTime toZonedDateTimeUtc(final Date date) {
return date != null ? toZonedDateTimeUtc(toInstant(date)) : null;
}
public static ZonedDateTime toZonedDateTimeUtc(final Instant instant) {
return instant != null ? ZonedDateTime.ofInstant(instant, ZoneOffset.UTC) : null;
}
public static ZonedDateTime toZonedDateTimeUtc(final int year, final Month month, final int dayOfMonth) {
return toZonedDateTimeUtc(LocalDate.of(year, month, dayOfMonth));
}
public static ZonedDateTime toZonedDateTimeUtc(final LocalDate date) {
return date != null ? date.atStartOfDay(UTC_ZONE) : null;
}
/**
* Returns a ZonedDateTime
from the given epoch value.
*
* Note that this implementation attempts to protect against caller providing timestamps in
* different units. For example, Unix timestamps are the number of SECONDS since January 1,
* 1970, while Java Dates are number of MILLISECONDS since January 1, 1970.
*
* @param epoch
* timestamp value in seconds, milliseconds or microseconds
* @return a ZonedDateTime
or null if the date is not valid
*/
public static ZonedDateTime toZonedDateTimeUtc(final long epoch) {
final EpochUnits units = EpochUnits.valueOf(epoch);
return toZonedDateTimeUtc(epoch, units);
}
/**
* Returns a ZonedDateTime
from the given epoch value.
*
* @param epochValue
* value in milliseconds
* @param units
* epoch units
* @return a ZonedDateTime
or null if the date is not valid
*/
public static ZonedDateTime toZonedDateTimeUtc(final long epochValue, final EpochUnits units) {
Preconditions.checkArgument(units != null, "units must be non-null");
final Instant instant = units.toInstant(epochValue);
return ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
}
public static ZonedDateTime toZonedDateTimeUtc(final String text) {
final ZonedDateTime date = parse(text, ZoneOffset.systemDefault(), ZonedDateTime::from);
return date != null ? toUtc(date) : null;
}
public static ZonedDateTime toZonedDateTimeUtcChecked(final String text) throws DateTimeParseException {
final ZonedDateTime date = parseChecked(text, ZoneOffset.systemDefault(), ZonedDateTime::from);
return date != null ? toUtc(date) : null;
}
public static ZonedDateTime withDatePrecision(final ZonedDateTime date) {
return DateUtils.toZonedDateTimeUtc(DateUtils.toDate(date));
}
public static DateTimeFormatter yearMonthDay(
final TextStyle monthStyle,
final String yearSeparator,
final String monthSeparator,
final int yearDigits,
final boolean time) {
return build(builder -> {
if (yearDigits == 2) {
builder.appendValueReduced(YEAR, 2, 2, LocalDate.now(ZoneId.systemDefault()).minusYears(80));
} else if (yearDigits == 4) {
builder.appendValue(YEAR, 4, 10, SignStyle.NEVER);
} else {
throw new IllegalStateException();
}
builder.appendLiteral(yearSeparator);
if (monthStyle != null) {
builder.appendText(MONTH_OF_YEAR, monthStyle);
} else {
builder.appendValue(MONTH_OF_YEAR);
}
builder.appendLiteral(monthSeparator);
builder.appendValue(DAY_OF_MONTH);
if (time) {
builder.appendLiteral(' ').append(TIME);
}
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy