Last active
April 20, 2024 15:35
-
-
Save dmikurube/3ef5f0c204be579360f1474352a476f2 to your computer and use it in GitHub Desktop.
DateTimeFormatter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
plugins { | |
id "java" | |
id "application" | |
} | |
configurations { | |
compileClasspath.resolutionStrategy.activateDependencyLocking() | |
runtimeClasspath.resolutionStrategy.activateDependencyLocking() | |
} | |
repositories { | |
mavenCentral() | |
} | |
java { | |
toolchain { | |
languageVersion = JavaLanguageVersion.of(8) | |
} | |
} | |
tasks.withType(JavaCompile) { | |
options.compilerArgs << "-Xlint:deprecation" << "-Xlint:unchecked" | |
options.encoding = "UTF-8" | |
} | |
sourceSets { | |
main { | |
java { | |
srcDir '.' | |
} | |
} | |
} | |
dependencies { | |
} | |
application { | |
mainClass = "Main" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.time.DateTimeException; | |
import java.time.LocalDate; | |
import java.time.chrono.ChronoLocalDate; | |
import java.time.chrono.Chronology; | |
import java.time.chrono.IsoChronology; | |
import java.time.format.ResolverStyle; | |
import java.time.temporal.ChronoField; | |
import java.time.temporal.ChronoUnit; | |
import java.time.temporal.IsoFields; | |
import java.time.temporal.Temporal; | |
import java.time.temporal.TemporalAccessor; | |
import java.time.temporal.TemporalField; | |
import java.time.temporal.TemporalUnit; | |
import java.time.temporal.UnsupportedTemporalTypeException; | |
import java.time.temporal.ValueRange; | |
import java.util.Map; | |
public class DayOfQuarter implements TemporalField { | |
@Override | |
public TemporalUnit getBaseUnit() { | |
return ChronoUnit.DAYS; | |
} | |
@Override | |
public TemporalUnit getRangeUnit() { | |
return IsoFields.QUARTER_YEARS; | |
} | |
@Override | |
public ValueRange range() { | |
return ValueRange.of(1, 90, 92); | |
} | |
@Override | |
public boolean isSupportedBy(TemporalAccessor temporal) { | |
return temporal.isSupported(ChronoField.DAY_OF_YEAR) && temporal.isSupported(ChronoField.MONTH_OF_YEAR) && | |
temporal.isSupported(ChronoField.YEAR) && isIso(temporal); | |
} | |
@Override | |
public ValueRange rangeRefinedBy(TemporalAccessor temporal) { | |
if (isSupportedBy(temporal) == false) { | |
throw new UnsupportedTemporalTypeException("Unsupported field: DayOfQuarter"); | |
} | |
long qoy = temporal.getLong(IsoFields.QUARTER_OF_YEAR); | |
if (qoy == 1) { | |
long year = temporal.getLong(ChronoField.YEAR); | |
return (IsoChronology.INSTANCE.isLeapYear(year) ? ValueRange.of(1, 91) : ValueRange.of(1, 90)); | |
} else if (qoy == 2) { | |
return ValueRange.of(1, 91); | |
} else if (qoy == 3 || qoy == 4) { | |
return ValueRange.of(1, 92); | |
} // else value not from 1 to 4, so drop through | |
return range(); | |
} | |
@Override | |
public long getFrom(TemporalAccessor temporal) { | |
if (isSupportedBy(temporal) == false) { | |
throw new UnsupportedTemporalTypeException("Unsupported field: DayOfQuarter"); | |
} | |
int doy = temporal.get(ChronoField.DAY_OF_YEAR); | |
int moy = temporal.get(ChronoField.MONTH_OF_YEAR); | |
long year = temporal.getLong(ChronoField.YEAR); | |
return doy - QUARTER_DAYS[((moy - 1) / 3) + (IsoChronology.INSTANCE.isLeapYear(year) ? 4 : 0)]; | |
} | |
@SuppressWarnings("unchecked") | |
@Override | |
public <R extends Temporal> R adjustInto(R temporal, long newValue) { | |
// calls getFrom() to check if supported | |
long curValue = getFrom(temporal); | |
range().checkValidValue(newValue, this); // leniently check from 1 to 92 TODO: check | |
return (R) temporal.with(ChronoField.DAY_OF_YEAR, temporal.getLong(ChronoField.DAY_OF_YEAR) + (newValue - curValue)); | |
} | |
@Override | |
public boolean isTimeBased() { | |
return false; | |
} | |
@Override | |
public boolean isDateBased() { | |
return true; | |
} | |
@Override | |
public ChronoLocalDate resolve( | |
Map<TemporalField, Long> fieldValues, TemporalAccessor partialTemporal, ResolverStyle resolverStyle) { | |
Long yearLong = fieldValues.get(ChronoField.YEAR); | |
Long qoyLong = fieldValues.get(IsoFields.QUARTER_OF_YEAR); | |
if (yearLong == null || qoyLong == null) { | |
return null; | |
} | |
int y = ChronoField.YEAR.checkValidIntValue(yearLong); // always validate | |
long doq = fieldValues.get(IsoFields.DAY_OF_QUARTER); | |
ensureIso(partialTemporal); | |
LocalDate date; | |
if (resolverStyle == ResolverStyle.LENIENT) { | |
date = LocalDate.of(y, 1, 1).plusMonths(Math.multiplyExact(Math.subtractExact(qoyLong, 1), 3)); | |
doq = Math.subtractExact(doq, 1); | |
} else { | |
int qoy = IsoFields.QUARTER_OF_YEAR.range().checkValidIntValue(qoyLong, IsoFields.QUARTER_OF_YEAR); // validated | |
date = LocalDate.of(y, ((qoy - 1) * 3) + 1, 1); | |
if (doq < 1 || doq > 90) { | |
if (resolverStyle == ResolverStyle.STRICT) { | |
rangeRefinedBy(date).checkValidValue(doq, this); // only allow exact range | |
} else { // SMART | |
range().checkValidValue(doq, this); // allow 1-92 rolling into next quarter | |
} | |
} | |
doq--; | |
} | |
fieldValues.remove(this); | |
fieldValues.remove(ChronoField.YEAR); | |
fieldValues.remove(IsoFields.QUARTER_OF_YEAR); | |
return date.plusDays(doq); | |
} | |
@Override | |
public String toString() { | |
return "DayOfQuarter"; | |
} | |
private static boolean isIso(TemporalAccessor temporal) { | |
return Chronology.from(temporal).equals(IsoChronology.INSTANCE); | |
} | |
private static void ensureIso(TemporalAccessor temporal) { | |
if (isIso(temporal) == false) { | |
throw new DateTimeException("Resolve requires IsoChronology"); | |
} | |
} | |
private static final int[] QUARTER_DAYS = {0, 90, 181, 273, 0, 91, 182, 274}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.time.Instant; | |
import java.time.OffsetDateTime; | |
import java.time.format.DateTimeFormatter; | |
import java.time.format.DateTimeFormatterBuilder; | |
import java.time.temporal.ChronoField; | |
import java.time.temporal.TemporalAccessor; | |
public class Main { | |
public static void main(final String[] args) { | |
padLiteral(); | |
} | |
private static void padLiteral() { | |
final DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); | |
builder.appendValue(ChronoField.DAY_OF_MONTH, 2); | |
builder.padNext(10, '0'); | |
builder.appendLiteral("foo"); | |
final DateTimeFormatter formatter = builder.toFormatter(); | |
System.out.println(formatter.format(OffsetDateTime.now())); | |
final TemporalAccessor accessor = formatter.parse("190000000foo"); | |
System.out.println(accessor.getLong(ChronoField.DAY_OF_MONTH)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment