packages/core/src/format/DateTimeBuilder.js
/*
* @copyright (c) 2016, Philipp Thürwächter & Pattrick Hüper
* @copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
* @license BSD-3-Clause (see LICENSE in the root directory of this source tree)
*/
import { requireNonNull } from '../assert';
import { DateTimeException } from '../errors';
import { MathUtil } from '../MathUtil';
import { EnumMap } from './EnumMap';
import { ResolverStyle } from './ResolverStyle';
import { IsoChronology } from '../chrono/IsoChronology';
import { ChronoLocalDate } from '../chrono/ChronoLocalDate';
import { ChronoLocalDateTime } from '../chrono/ChronoLocalDateTime';
import { ChronoField } from '../temporal/ChronoField';
import { TemporalAccessor } from '../temporal/TemporalAccessor';
import { TemporalQueries } from '../temporal/TemporalQueries';
import { Instant } from '../Instant';
import { LocalTime } from '../LocalTime';
import { LocalDate } from '../LocalDate';
import { Period } from '../Period';
import { ZonedDateTime } from '../ZonedDateTime';
import { ZoneOffset } from '../ZoneOffset';
/**
* Builder that can holds date and time fields and related date and time objects.
*
* The builder is used to hold onto different elements of date and time.
* It is designed as two separate maps:
*
* * from {@link TemporalField} to `long` value, where the value may be
* outside the valid range for the field
* * from {@link Class} to {@link TemporalAccessor}, holding larger scale objects
* like {@link LocalDateTime}.
*
* @private
*/
export class DateTimeBuilder extends TemporalAccessor {
/**
* Creates a new instance of the builder with a single field-value.
*
* This is equivalent to using {@link addFieldValue} on an empty builder.
*
* @param {TemporalField} field - the field to add, not null
* @param {number} value - the value to add, not null
* @return {DateTimeBuilder}
*/
static create(field, value) {
const dtb = new DateTimeBuilder();
dtb._addFieldValue(field, value);
return dtb;
}
constructor(){
super();
/**
* The map of other fields.
*/
this.fieldValues = new EnumMap();
/**
* The chronology.
*/
this.chrono = null;
/**
* The zone.
*/
this.zone = null;
/**
* The date.
*/
this.date = null;
/**
* The time.
*/
this.time = null;
/**
* The leap second flag.
*/
this.leapSecond = false;
/**
* The excess days.
*/
this.excessDays = null;
}
/**
*
* @param {TemporalField} field
* @return {Number} field value
*/
getFieldValue0(field) {
return this.fieldValues.get(field);
}
/**
* Adds a field-value pair to the builder.
*
* This adds a field to the builder.
* If the field is not already present, then the field-value pair is added to the map.
* If the field is already present and it has the same value as that specified, no action occurs.
* If the field is already present and it has a different value to that specified, then
* an exception is thrown.
*
* @param {TemporalField} field - the field to add, not null
* @param {Number} value - the value to add, not null
* @return {DateTimeBuilder}, this for method chaining
* @throws DateTimeException if the field is already present with a different value
*/
_addFieldValue(field, value) {
requireNonNull(field, 'field');
const old = this.getFieldValue0(field); // check first for better error message
if (old != null && old !== value) {
throw new DateTimeException(`Conflict found: ${field} ${old} differs from ${field} ${value}: ${this}`);
}
return this._putFieldValue0(field, value);
}
/**
* @param {TemporalField} field
* @param {Number} value
* @return {DateTimeBuilder}, this for method chaining
*/
_putFieldValue0(field, value) {
this.fieldValues.put(field, value);
return this;
}
/**
* Resolves the builder, evaluating the date and time.
*
* This examines the contents of the build.er and resolves it to produce the best
* available date and time, throwing an exception if a problem occurs.
* Calling this method changes the state of the builder.
*
* @param {ResolverStyle} resolverStyle - how to resolve
* @param {TemporalField[]} resolverFields
* @return {DateTimeBuilder} this, for method chaining
*/
resolve(resolverStyle, resolverFields) {
if (resolverFields != null) {
this.fieldValues.retainAll(resolverFields);
}
// handle standard fields
this._mergeInstantFields();
this._mergeDate(resolverStyle);
this._mergeTime(resolverStyle);
// the other fields may need fields produced by merging, e.g. YEAR from YEAR_OF_ERA
if (this._resolveFields(resolverStyle)) {
this._mergeInstantFields();
this._mergeDate(resolverStyle);
this._mergeTime(resolverStyle);
}
this._resolveTimeInferZeroes(resolverStyle);
this._crossCheck();
if (this.excessDays != null && this.excessDays.isZero() === false && this.date != null && this.time != null) {
this.date = this.date.plus(this.excessDays);
this.excessDays = Period.ZERO;
}
this._resolveFractional();
this._resolveInstant();
return this;
}
/**
* Resolves the fields that are not {@link ChronoField}s, such as {@link IsoFields} or the
* fields of `@js-joda/locale` WeekFields, by delegating to their own `resolve()`, as
* java.time does. A field may resolve to a date, a time or a date-time, or replace itself
* by other fields (for example a localized day-of-week by {@link ChronoField.DAY_OF_WEEK}),
* so the fields are visited again after each change. Returns whether anything changed.
*
* @param {ResolverStyle} resolverStyle
* @return {boolean}
* @private
*/
_resolveFields(resolverStyle) {
let changes = 0;
let changed = true;
while (changed && changes < 100) {
changed = false;
const fields = this.fieldValues.keys();
for (let i = 0; i < fields.length; i++) {
const field = fields[i];
if (field instanceof ChronoField || typeof field.resolve !== 'function') {
continue;
}
const resolved = field.resolve(this.fieldValues, this, resolverStyle);
if (resolved != null) {
if (resolved instanceof ChronoLocalDateTime) {
this._resolveMakeChanges(field, resolved.toLocalDate());
this._resolveMakeChanges(field, resolved.toLocalTime());
} else if (resolved instanceof ChronoLocalDate || resolved instanceof LocalTime) {
this._resolveMakeChanges(field, resolved);
} else {
throw new DateTimeException(`Unknown type: ${resolved}`);
}
changed = true;
} else if (this.fieldValues.containsKey(field) === false) {
changed = true;
}
if (changed) {
changes++;
break; // start again, the fields have changed
}
}
}
if (changes === 100) {
throw new DateTimeException('Badly written field');
}
return changes > 0;
}
/**
* Adds a date or time resolved by a field as {@link ChronoField.EPOCH_DAY} or
* {@link ChronoField.NANO_OF_DAY}, to be merged with the other fields.
*
* @param {TemporalField} targetField
* @param {ChronoLocalDate|LocalTime} dateOrTime
* @private
*/
_resolveMakeChanges(targetField, dateOrTime) {
const isDate = dateOrTime instanceof ChronoLocalDate;
const field = isDate ? ChronoField.EPOCH_DAY : ChronoField.NANO_OF_DAY;
const value = isDate ? dateOrTime.toEpochDay() : dateOrTime.toNanoOfDay();
const old = this.fieldValues.get(field);
if (old != null && old !== value) {
const oldDateOrTime = isDate ? LocalDate.ofEpochDay(old) : LocalTime.ofNanoOfDay(old);
throw new DateTimeException(`Conflict found: ${oldDateOrTime} differs from ${dateOrTime} while resolving ${targetField}`);
}
this.fieldValues.put(field, value);
}
/**
*
* @param {ResolverStyle} resolverStyle
* @private
*/
_mergeDate(resolverStyle) {
//if (this.chrono instanceof IsoChronology) {
this._checkDate(IsoChronology.INSTANCE.resolveDate(this.fieldValues, resolverStyle));
//} else {
// if (this.fieldValues.containsKey(ChronoField.EPOCH_DAY)) {
// this._checkDate(LocalDate.ofEpochDay(this.fieldValues.remove(ChronoField.EPOCH_DAY)));
// return;
// }
//}
}
/**
*
* @param {LocalDate} date
* @private
*/
_checkDate(date) {
if (date != null) {
if (this.date != null && this.date.equals(date) === false) {
throw new DateTimeException(`Conflict found: Fields resolved to two different dates: ${this.date} ${date}`);
}
this._addObject(date);
for (const field of this.fieldValues.keys()) {
if (field instanceof ChronoField && field.isDateBased()) {
let val1;
try {
val1 = date.getLong(field);
} catch (ex) {
if (ex instanceof DateTimeException) {
continue;
} else {
throw ex;
}
}
const val2 = this.fieldValues.get(field);
if (val1 !== val2) {
throw new DateTimeException(`Conflict found: Field ${field} ${val1} differs from ${field} ${val2} derived from ${date}`);
}
}
}
}
}
/**
*
* @param {ResolverStyle} resolverStyle
* @private
*/
_mergeTime(resolverStyle) {
if (this.fieldValues.containsKey(ChronoField.CLOCK_HOUR_OF_DAY)) {
const ch = this.fieldValues.remove(ChronoField.CLOCK_HOUR_OF_DAY);
if (resolverStyle !== ResolverStyle.LENIENT) {
if (resolverStyle === ResolverStyle.SMART && ch === 0) {
// ok
} else {
ChronoField.CLOCK_HOUR_OF_DAY.checkValidValue(ch);
}
}
this._addFieldValue(ChronoField.HOUR_OF_DAY, ch === 24 ? 0 : ch);
}
if (this.fieldValues.containsKey(ChronoField.CLOCK_HOUR_OF_AMPM)) {
const ch = this.fieldValues.remove(ChronoField.CLOCK_HOUR_OF_AMPM);
if (resolverStyle !== ResolverStyle.LENIENT) {
if (resolverStyle === ResolverStyle.SMART && ch === 0) {
// ok
} else {
ChronoField.CLOCK_HOUR_OF_AMPM.checkValidValue(ch);
}
}
this._addFieldValue(ChronoField.HOUR_OF_AMPM, ch === 12 ? 0 : ch);
}
if (resolverStyle !== ResolverStyle.LENIENT) {
if (this.fieldValues.containsKey(ChronoField.AMPM_OF_DAY)) {
ChronoField.AMPM_OF_DAY.checkValidValue(this.fieldValues.get(ChronoField.AMPM_OF_DAY));
}
if (this.fieldValues.containsKey(ChronoField.HOUR_OF_AMPM)) {
ChronoField.HOUR_OF_AMPM.checkValidValue(this.fieldValues.get(ChronoField.HOUR_OF_AMPM));
}
}
if (this.fieldValues.containsKey(ChronoField.AMPM_OF_DAY) && this.fieldValues.containsKey(ChronoField.HOUR_OF_AMPM)) {
const ap = this.fieldValues.remove(ChronoField.AMPM_OF_DAY);
const hap = this.fieldValues.remove(ChronoField.HOUR_OF_AMPM);
this._addFieldValue(ChronoField.HOUR_OF_DAY, ap * 12 + hap);
}
// if (timeFields.containsKey(HOUR_OF_DAY) && timeFields.containsKey(MINUTE_OF_HOUR)) {
// const hod = timeFields.remove(HOUR_OF_DAY);
// const moh = timeFields.remove(MINUTE_OF_HOUR);
// this._addFieldValue(MINUTE_OF_DAY, hod * 60 + moh);
// }
// if (timeFields.containsKey(MINUTE_OF_DAY) && timeFields.containsKey(SECOND_OF_MINUTE)) {
// const mod = timeFields.remove(MINUTE_OF_DAY);
// const som = timeFields.remove(SECOND_OF_MINUTE);
// this._addFieldValue(SECOND_OF_DAY, mod * 60 + som);
// }
if (this.fieldValues.containsKey(ChronoField.NANO_OF_DAY)) {
const nod = this.fieldValues.remove(ChronoField.NANO_OF_DAY);
if (resolverStyle !== ResolverStyle.LENIENT) {
ChronoField.NANO_OF_DAY.checkValidValue(nod);
}
this._addFieldValue(ChronoField.SECOND_OF_DAY, MathUtil.intDiv(nod, 1000000000));
this._addFieldValue(ChronoField.NANO_OF_SECOND, MathUtil.intMod(nod, 1000000000));
}
if (this.fieldValues.containsKey(ChronoField.MICRO_OF_DAY)) {
const cod = this.fieldValues.remove(ChronoField.MICRO_OF_DAY);
if (resolverStyle !== ResolverStyle.LENIENT) {
ChronoField.MICRO_OF_DAY.checkValidValue(cod);
}
this._addFieldValue(ChronoField.SECOND_OF_DAY, MathUtil.intDiv(cod, 1000000));
this._addFieldValue(ChronoField.MICRO_OF_SECOND, MathUtil.intMod(cod, 1000000));
}
if (this.fieldValues.containsKey(ChronoField.MILLI_OF_DAY)) {
const lod = this.fieldValues.remove(ChronoField.MILLI_OF_DAY);
if (resolverStyle !== ResolverStyle.LENIENT) {
ChronoField.MILLI_OF_DAY.checkValidValue(lod);
}
this._addFieldValue(ChronoField.SECOND_OF_DAY, MathUtil.intDiv(lod, 1000));
this._addFieldValue(ChronoField.MILLI_OF_SECOND, MathUtil.intMod(lod, 1000));
}
if (this.fieldValues.containsKey(ChronoField.SECOND_OF_DAY)) {
const sod = this.fieldValues.remove(ChronoField.SECOND_OF_DAY);
if (resolverStyle !== ResolverStyle.LENIENT) {
ChronoField.SECOND_OF_DAY.checkValidValue(sod);
}
this._addFieldValue(ChronoField.HOUR_OF_DAY, MathUtil.intDiv(sod, 3600));
this._addFieldValue(ChronoField.MINUTE_OF_HOUR, MathUtil.intMod(MathUtil.intDiv(sod, 60), 60));
this._addFieldValue(ChronoField.SECOND_OF_MINUTE, MathUtil.intMod(sod, 60));
}
if (this.fieldValues.containsKey(ChronoField.MINUTE_OF_DAY)) {
const mod = this.fieldValues.remove(ChronoField.MINUTE_OF_DAY);
if (resolverStyle !== ResolverStyle.LENIENT) {
ChronoField.MINUTE_OF_DAY.checkValidValue(mod);
}
this._addFieldValue(ChronoField.HOUR_OF_DAY, MathUtil.intDiv(mod, 60));
this._addFieldValue(ChronoField.MINUTE_OF_HOUR, MathUtil.intMod(mod, 60));
}
// const sod = MathUtil.intDiv(nod, 1000000000L);
// this._addFieldValue(HOUR_OF_DAY, MathUtil.intDiv(sod, 3600));
// this._addFieldValue(MINUTE_OF_HOUR, MathUtil.intMod(MathUtil.intDiv(sod, 60), 60));
// this._addFieldValue(SECOND_OF_MINUTE, MathUtil.intMod(sod, 60));
// this._addFieldValue(NANO_OF_SECOND, MathUtil.intMod(nod, 1000000000L));
if (resolverStyle !== ResolverStyle.LENIENT) {
if (this.fieldValues.containsKey(ChronoField.MILLI_OF_SECOND)) {
ChronoField.MILLI_OF_SECOND.checkValidValue(this.fieldValues.get(ChronoField.MILLI_OF_SECOND));
}
if (this.fieldValues.containsKey(ChronoField.MICRO_OF_SECOND)) {
ChronoField.MICRO_OF_SECOND.checkValidValue(this.fieldValues.get(ChronoField.MICRO_OF_SECOND));
}
}
if (this.fieldValues.containsKey(ChronoField.MILLI_OF_SECOND) && this.fieldValues.containsKey(ChronoField.MICRO_OF_SECOND)) {
const los = this.fieldValues.remove(ChronoField.MILLI_OF_SECOND);
const cos = this.fieldValues.get(ChronoField.MICRO_OF_SECOND);
this._putFieldValue0(ChronoField.MICRO_OF_SECOND, los * 1000 + (MathUtil.intMod(cos, 1000)));
}
if (this.fieldValues.containsKey(ChronoField.MICRO_OF_SECOND) && this.fieldValues.containsKey(ChronoField.NANO_OF_SECOND)) {
const nos = this.fieldValues.get(ChronoField.NANO_OF_SECOND);
this._putFieldValue0(ChronoField.MICRO_OF_SECOND, MathUtil.intDiv(nos, 1000));
this.fieldValues.remove(ChronoField.MICRO_OF_SECOND);
}
if (this.fieldValues.containsKey(ChronoField.MILLI_OF_SECOND) && this.fieldValues.containsKey(ChronoField.NANO_OF_SECOND)) {
const nos = this.fieldValues.get(ChronoField.NANO_OF_SECOND);
this._putFieldValue0(ChronoField.MILLI_OF_SECOND, MathUtil.intDiv(nos, 1000000));
this.fieldValues.remove(ChronoField.MILLI_OF_SECOND);
}
if (this.fieldValues.containsKey(ChronoField.MICRO_OF_SECOND)) {
const cos = this.fieldValues.remove(ChronoField.MICRO_OF_SECOND);
this._putFieldValue0(ChronoField.NANO_OF_SECOND, cos * 1000);
} else if (this.fieldValues.containsKey(ChronoField.MILLI_OF_SECOND)) {
const los = this.fieldValues.remove(ChronoField.MILLI_OF_SECOND);
this._putFieldValue0(ChronoField.NANO_OF_SECOND, los * 1000000);
}
}
/**
*
* @param {ResolverStyle} resolverStyle
* @private
*/
_resolveTimeInferZeroes(resolverStyle) {
let hod = this.fieldValues.get(ChronoField.HOUR_OF_DAY);
const moh = this.fieldValues.get(ChronoField.MINUTE_OF_HOUR);
const som = this.fieldValues.get(ChronoField.SECOND_OF_MINUTE);
let nos = this.fieldValues.get(ChronoField.NANO_OF_SECOND);
if (hod == null) {
return;
}
if (moh == null && (som != null || nos != null)) {
return;
}
if (moh != null && som == null && nos != null) {
return;
}
if (resolverStyle !== ResolverStyle.LENIENT) {
if (hod != null) {
if (resolverStyle === ResolverStyle.SMART &&
hod === 24 &&
(moh == null || moh === 0) &&
(som == null || som === 0) &&
(nos == null || nos === 0)) {
hod = 0;
this.excessDays = Period.ofDays(1);
}
const hodVal = ChronoField.HOUR_OF_DAY.checkValidIntValue(hod);
if (moh != null) {
const mohVal = ChronoField.MINUTE_OF_HOUR.checkValidIntValue(moh);
if (som != null) {
const somVal = ChronoField.SECOND_OF_MINUTE.checkValidIntValue(som);
if (nos != null) {
const nosVal = ChronoField.NANO_OF_SECOND.checkValidIntValue(nos);
this._addObject(LocalTime.of(hodVal, mohVal, somVal, nosVal));
} else {
this._addObject(LocalTime.of(hodVal, mohVal, somVal));
}
} else {
if (nos == null) {
this._addObject(LocalTime.of(hodVal, mohVal));
}
}
} else {
if (som == null && nos == null) {
this._addObject(LocalTime.of(hodVal, 0));
}
}
}
} else {
if (hod != null) {
let hodVal = hod;
if (moh != null) {
if (som != null) {
if (nos == null) {
nos = 0;
}
let totalNanos = MathUtil.safeMultiply(hodVal, 3600000000000);
totalNanos = MathUtil.safeAdd(totalNanos, MathUtil.safeMultiply(moh, 60000000000));
totalNanos = MathUtil.safeAdd(totalNanos, MathUtil.safeMultiply(som, 1000000000));
totalNanos = MathUtil.safeAdd(totalNanos, nos);
const excessDays = MathUtil.floorDiv(totalNanos, 86400000000000); // safe int cast
const nod = MathUtil.floorMod(totalNanos, 86400000000000);
this._addObject(LocalTime.ofNanoOfDay(nod));
this.excessDays = Period.ofDays(excessDays);
} else {
let totalSecs = MathUtil.safeMultiply(hodVal, 3600);
totalSecs = MathUtil.safeAdd(totalSecs, MathUtil.safeMultiply(moh, 60));
const excessDays = MathUtil.floorDiv(totalSecs, 86400); // safe int cast
const sod = MathUtil.floorMod(totalSecs, 86400);
this._addObject(LocalTime.ofSecondOfDay(sod));
this.excessDays = Period.ofDays(excessDays);
}
} else {
const excessDays = MathUtil.safeToInt(MathUtil.floorDiv(hodVal, 24));
hodVal = MathUtil.floorMod(hodVal, 24);
this._addObject(LocalTime.of(hodVal, 0));
this.excessDays = Period.ofDays(excessDays);
}
}
}
this.fieldValues.remove(ChronoField.HOUR_OF_DAY);
this.fieldValues.remove(ChronoField.MINUTE_OF_HOUR);
this.fieldValues.remove(ChronoField.SECOND_OF_MINUTE);
this.fieldValues.remove(ChronoField.NANO_OF_SECOND);
}
/**
*
* @param {ChronoLocalDate|LocalTime} dateOrTime
* @private
*/
_addObject(dateOrTime) {
if (dateOrTime instanceof ChronoLocalDate){
this.date = dateOrTime;
} else if (dateOrTime instanceof LocalTime){
this.time = dateOrTime;
}
}
/**
* Converts a parsed instant-seconds into a date and a second-of-day, using the parsed zone
* or, if there is none, the parsed offset. Without a zone or offset, the instant-seconds
* are left unchanged.
*
* @private
*/
_mergeInstantFields() {
if (this.fieldValues.containsKey(ChronoField.INSTANT_SECONDS)) {
if (this.zone != null) {
this._mergeInstantFields0(this.zone);
} else {
const offsetSecs = this.fieldValues.get(ChronoField.OFFSET_SECONDS);
if (offsetSecs != null) {
this._mergeInstantFields0(ZoneOffset.ofTotalSeconds(offsetSecs));
}
}
}
}
/**
* @param {ZoneId} selectedZone
* @private
*/
_mergeInstantFields0(selectedZone) {
const instant = Instant.ofEpochSecond(this.fieldValues.remove(ChronoField.INSTANT_SECONDS));
const zdt = ZonedDateTime.ofInstant(instant, selectedZone);
if (this.date == null) {
this._addObject(zdt.toLocalDate());
} else {
this._resolveMakeChanges(ChronoField.INSTANT_SECONDS, zdt.toLocalDate());
}
this._addFieldValue(ChronoField.SECOND_OF_DAY, zdt.toLocalTime().toSecondOfDay());
}
/**
* Checks the remaining field values against the resolved date and time, and removes the
* ones that match.
*
* @private
*/
_crossCheck() {
if (this.date != null && this.time != null) {
this._crossCheck0(this.date.atTime(this.time));
} else if (this.date != null) {
this._crossCheck0(this.date);
} else if (this.time != null) {
this._crossCheck0(this.time);
}
}
/**
* @param {TemporalAccessor} temporal
* @private
*/
_crossCheck0(temporal) {
for (const field of this.fieldValues.keys()) {
if (temporal.isSupported(field)) {
let temporalValue;
try {
temporalValue = temporal.getLong(field);
} catch (ex) {
if (ex instanceof DateTimeException) {
continue;
}
throw ex;
}
const value = this.fieldValues.get(field);
if (temporalValue !== value) {
throw new DateTimeException(`Cross check failed: ${field} ${temporalValue} vs ${field} ${value}`);
}
this.fieldValues.remove(field);
}
}
}
/**
* Without a time, adds the fraction-of-second fields to a parsed instant-seconds,
* second-of-day or second-of-minute, with zero if no nano-of-second was parsed.
*
* @private
*/
_resolveFractional() {
if (this.time == null &&
(this.fieldValues.containsKey(ChronoField.INSTANT_SECONDS) ||
this.fieldValues.containsKey(ChronoField.SECOND_OF_DAY) ||
this.fieldValues.containsKey(ChronoField.SECOND_OF_MINUTE))) {
if (this.fieldValues.containsKey(ChronoField.NANO_OF_SECOND)) {
const nos = this.fieldValues.get(ChronoField.NANO_OF_SECOND);
this.fieldValues.put(ChronoField.MICRO_OF_SECOND, MathUtil.intDiv(nos, 1000));
this.fieldValues.put(ChronoField.MILLI_OF_SECOND, MathUtil.intDiv(nos, 1000000));
} else {
this.fieldValues.put(ChronoField.NANO_OF_SECOND, 0);
this.fieldValues.put(ChronoField.MICRO_OF_SECOND, 0);
this.fieldValues.put(ChronoField.MILLI_OF_SECOND, 0);
}
}
}
_resolveInstant() {
if (this.date != null && this.time != null) {
const offsetSecs = this.fieldValues.get(ChronoField.OFFSET_SECONDS);
if (offsetSecs != null) {
const offset = ZoneOffset.ofTotalSeconds(offsetSecs);
const instant = this.date.atTime(this.time).atZone(offset).getLong(ChronoField.INSTANT_SECONDS);
this.fieldValues.put(ChronoField.INSTANT_SECONDS, instant);
} else if (this.zone != null) {
const instant = this.date.atTime(this.time).atZone(this.zone).getLong(ChronoField.INSTANT_SECONDS);
this.fieldValues.put(ChronoField.INSTANT_SECONDS, instant);
}
}
}
/**
* Builds the specified type from the values in this builder.
*
* This attempts to build the specified type from this builder.
* If the builder cannot return the type, an exception is thrown.
*
* @param {!TemporalQuery} type - the type to invoke `from` on, not null
* @return {*} the extracted value, not null
* @throws DateTimeException if an error occurs
*/
build(type) {
return type.queryFrom(this);
}
/**
*
* @param {TemporalField} field
* @returns {number}
*/
isSupported(field) {
if (field == null) {
return false;
}
return (this.fieldValues.containsKey(field) && this.fieldValues.get(field) !== undefined) ||
(this.date != null && this.date.isSupported(field)) ||
(this.time != null && this.time.isSupported(field));
}
/**
*
* @param {TemporalField} field
* @returns {number}
*/
getLong(field) {
requireNonNull(field, 'field');
const value = this.getFieldValue0(field);
if (value == null) {
if (this.date != null && this.date.isSupported(field)) {
return this.date.getLong(field);
}
if (this.time != null && this.time.isSupported(field)) {
return this.time.getLong(field);
}
throw new DateTimeException(`Field not found: ${field}`);
}
return value;
}
/**
*
* @param {!TemporalQuery} query
* @returns {*}
*/
query(query) {
if (query === TemporalQueries.zoneId()) {
return this.zone;
} else if (query === TemporalQueries.chronology()) {
return this.chrono;
} else if (query === TemporalQueries.localDate()) {
return this.date != null ? LocalDate.from(this.date) : null;
} else if (query === TemporalQueries.localTime()) {
return this.time;
} else if (query === TemporalQueries.zone() || query === TemporalQueries.offset()) {
return query.queryFrom(this);
} else if (query === TemporalQueries.precision()) {
return null; // not a complete date/time
}
// inline TemporalAccessor.super.query(query) as an optimization
// non-JDK classes are not permitted to make this optimization
return query.queryFrom(this);
}
}