@parischap/conversions
Version:
A functional library to replace partially the native Intl API
1,338 lines • 84.2 kB
JavaScript
/**
* This module implements an immutable `CVDateTime` object.
*
* `CVDateTime` objects keep an internal state. But all provided functions are pure insofar as they
* always yield the same result whatever the state the object is in. The state is only used to
* improve performance but does not alter the results.
*
* Unlike the Javascript `Date` objects and the Effect `DateTime` objects, `CVDateTime` objects
* handle both the Gregorian and Iso calendars. So you can easily get/set the iso year and iso week
* of a `CVDateTime` object.
*
* A `CVDateTime` object has a `zoneOffset` which is the difference in hours between the time in the
* local zone and UTC time (e.g `zoneOffset=1` for timezone +1:00). All the data in a `CVDateTime`
* object is `zoneOffset-dependent`, except `timestamp`. An important thing to note is that a
* `CVDateTime` object with a timestamp `t` and a zoneOffset `zo` has exactly the same date parts
* (`year`, `ordinalDay`, `month`, `monthDay`, `isoYear`...) as a `CVDateTime` object with
* `timestamp = t+zox3600` and `zoneOffset = 0`. That's the reason for the _zonedTimestamp field
* which is equal to `t+zox3600`. All calculations are performed UTC using _zonedTimestamp instead
* of timestamp.
*/
import * as MArray from '@parischap/effect-lib/MArray';
import * as MFunction from '@parischap/effect-lib/MFunction';
import * as MInputError from '@parischap/effect-lib/MInputError';
import * as MInspectable from '@parischap/effect-lib/MInspectable';
import * as MNumber from '@parischap/effect-lib/MNumber';
import * as MPipeable from '@parischap/effect-lib/MPipeable';
import * as MStruct from '@parischap/effect-lib/MStruct';
import * as MTypes from '@parischap/effect-lib/MTypes';
import * as DateTime from 'effect/DateTime';
import * as Either from 'effect/Either';
import * as Equal from 'effect/Equal';
import * as Function from 'effect/Function';
import * as Hash from 'effect/Hash';
import * as Number from 'effect/Number';
import * as Option from 'effect/Option';
import * as Predicate from 'effect/Predicate';
import * as Struct from 'effect/Struct';
import {flow} from 'effect/Function';
import {pipe} from 'effect/Function';
import * as CVNumberBase10Format from './NumberBase10Format.js';
import * as CVTemplate from './Template.js';
import * as CVTemplatePlaceholder from './TemplatePlaceholder.js';
import * as CVTemplateSeparator from './TemplateSeparator.js';
/**
* Module tag
*
* @category Module markers
*/
export const moduleTag = '@parischap/conversions/DateTime/';
const _TypeId = /*#__PURE__*/Symbol.for(moduleTag);
/**
* Duration of a second in milliseconds
*
* @category Constants
*/
export const SECOND_MS = 1_000;
/**
* Duration of a minute in milliseconds
*
* @category Constants
*/
export const MINUTE_MS = 60 * SECOND_MS;
/**
* Duration of an hour in milliseconds
*
* @category Constants
*/
export const HOUR_MS = 60 * MINUTE_MS;
/**
* Duration of a day in milliseconds
*
* @category Constants
*/
export const DAY_MS = 24 * HOUR_MS;
/**
* Duration of a week in milliseconds
*
* @category Constants
*/
export const WEEK_MS = 7 * DAY_MS;
/**
* Duration of a normal year in milliseconds
*
* @category Constants
*/
export const COMMON_YEAR_MS = 365 * DAY_MS;
/**
* Duration of a leap year in milliseconds
*
* @category Constants
*/
export const LEAP_YEAR_MS = COMMON_YEAR_MS + DAY_MS;
/**
* Duration of a short iso year in milliseconds
*
* @category Constants
*/
export const SHORT_YEAR_MS = 52 * WEEK_MS;
/**
* Duration of a long iso year in milliseconds
*
* @category Constants
*/
export const LONG_YEAR_MS = SHORT_YEAR_MS + WEEK_MS;
/**
* Local time zone offset in hours of the machine on which this code runs. The value is calculated
* once at startup.
*
* @category Constants
*/
export const LOCAL_TIME_ZONE_OFFSET = -(/*#__PURE__*/new Date().getTimezoneOffset() / 60);
/**
* Namespace for the data relative to a Month
*
* @category Models
*/
const MAX_FULL_YEAR_OFFSET = 273_790;
/**
* Maximal usable year (ECMA-262)
*
* @category Constants
*/
export const MAX_FULL_YEAR = 1970 + MAX_FULL_YEAR_OFFSET;
/**
* Minimal usable year (ECMA-262)
*
* @category Constants
*/
export const MIN_FULL_YEAR = 1970 - MAX_FULL_YEAR_OFFSET - 1;
/**
* Maximal usable timestamp (ECMA-262)
*
* @category Constants
*/
export const MAX_TIMESTAMP = 8_640_000_000_000_000;
/**
* Minimal usable timestamp (ECMA-262)
*
* @category Constants
*/
export const MIN_TIMESTAMP = -MAX_TIMESTAMP;
const _integer = CVNumberBase10Format.integer;
const _params = {
fillChar: '0',
numberBase10Format: _integer
};
const _fixedLengthToReal = CVTemplatePlaceholder.fixedLengthToReal;
const _sep = CVTemplateSeparator;
/**
* Namespace for the a Gregorian date
*
* It is important to note that the Gregorian calendar is periodic with a 400-year period as far as
* leap years are concerned. Leap years are those that can be divided by 4, except those that can be
* divided by 100 except those that can be divided by 400. So 2100, 2200, 2300 are not leap years.
* But 2400 is a leap year.
*
* @category Models
*/
var GregorianDate;
(function (GregorianDate) {
const _namespaceTag = moduleTag + 'GregorianDate/';
const _TypeId = /*#__PURE__*/Symbol.for(_namespaceTag);
/**
* Duration in milliseconds of a four-year period containing a leap year
*
* @category Constants
*/
const FOUR_YEARS_MS = 3 * COMMON_YEAR_MS + LEAP_YEAR_MS;
/**
* Duration in milliseconds of a 100-year period that has a leap year every 4th year except the
* 100th year
*
* @category Constants
*/
const HUNDRED_YEARS_MS = 25 * FOUR_YEARS_MS - DAY_MS;
/**
* Duration in milliseconds of a 400-year period that has a leap year every 4th year except the
* 100th year. But the 400th year is a leap year
*
* @category Constants
*/
const FOUR_HUNDRED_YEARS_MS = 4 * HUNDRED_YEARS_MS + DAY_MS;
/** Timestamp of 1/1/2001 00:00:00:000+0:00 */
const YEAR_START_2001_MS = 978_307_200_000;
/** Number of days in each month of a leap year */
const LEAP_YEAR_DAYS_IN_MONTH = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
/** Number of days in each month of a leap year */
const COMMON_YEAR_DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
/**
* Type guard
*
* @category Guards
*/
GregorianDate.has = u => Predicate.hasProperty(u, _TypeId);
/** Proto */
const proto = {
[_TypeId]: _TypeId,
... /*#__PURE__*/MInspectable.BaseProto(_namespaceTag),
...MPipeable.BaseProto
};
/** Constructor */
const _make = params => MTypes.objectFromDataAndProto(proto, params);
const _makeWithInternals = params => _make({
...params,
_daysInMonth: params.yearIsLeap ? LEAP_YEAR_DAYS_IN_MONTH : COMMON_YEAR_DAYS_IN_MONTH
});
/**
* Constructs a GregorianDate from a timestamp
*
* @category Constructors
*/
GregorianDate.fromTimestamp = timestamp => {
/**
* The 100-year periods [2001, 2100], [2101, 2200], and [2201, 2300] all last HUNDRED_YEARS_MS.
* Those three 100-year periods can be divided in 24 periods that last FOUR_YEARS_MS
* (4xCOMMON_YEAR_MS + DAY_MS) and a final 4-year period that lasts FOUR_YEARS_MS - DAY_MS
* (4xCOMMON_YEAR_MS).
*
* The 100-year period [2301, 2400] lasts HUNDRED_YEARS_MS + DAY_MS. This period can be divided
* in 25 periods that last FOUR_YEARS_MS (4xCOMMON_YEAR_MS + DAY_MS).
*/
const offset2001 = timestamp - YEAR_START_2001_MS;
const q400Years = Math.floor(offset2001 / FOUR_HUNDRED_YEARS_MS);
const offset400Years = q400Years * FOUR_HUNDRED_YEARS_MS;
const r400Years = offset2001 - offset400Years;
// q100Years is equal to 4 on the last day of the 400-year period.
const q100Years = Math.min(3, Math.floor(r400Years / HUNDRED_YEARS_MS));
const offset100Years = q100Years * HUNDRED_YEARS_MS;
// r100Years is superior to HUNDRED_YEARS_MS on the last day of the 400-year period
const r100Years = r400Years - offset100Years;
const q4Years = Math.floor(r100Years / FOUR_YEARS_MS);
const offset4Years = q4Years * FOUR_YEARS_MS;
const r4Years = r100Years - offset4Years;
// q1Year is equal to 4 on the last day of each 4-year period except the last day of years 2100, 2200 and 2300.
const q1Year = Math.min(3, Math.floor(r4Years / COMMON_YEAR_MS));
const offset1Year = q1Year * COMMON_YEAR_MS;
const yearIsLeap = q1Year === 3 && (q4Years !== 24 || q100Years === 3);
const yearStartTimestamp = YEAR_START_2001_MS + offset400Years + offset100Years + offset4Years + offset1Year;
return _makeWithInternals({
timestamp,
year: 2001 + 400 * q400Years + 100 * q100Years + 4 * q4Years + q1Year,
yearIsLeap,
yearStartTimestamp,
ordinalDay: Math.floor((timestamp - yearStartTimestamp) / DAY_MS) + 1,
month: Option.none(),
monthDay: Option.none()
});
};
/**
* If possible, returns a new GregorianDate having `year` set to `year` and the same `month` and
* `monthDay` as `self`. Returns a left of an error otherwise. `year` must be an integer comprised
* in the range [MIN_FULL_YEAR, MAX_FULL_YEAR]. If `self` represents a 29th of february, `year`
* must be a leap year.
*
* @category Setters
*/
GregorianDate.setYear = year => self => Either.gen(function* () {
const validatedYear = yield* pipe(year, MInputError.assertInRange({
min: MIN_FULL_YEAR,
max: MAX_FULL_YEAR,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'year'"
}));
const offset2001 = validatedYear - 2001;
const q400Years = Math.floor(offset2001 / 400);
const r400Years = offset2001 - 400 * q400Years;
const q100Years = Math.floor(r400Years / 100);
const r100Years = r400Years - 100 * q100Years;
const q4Years = Math.floor(r100Years / 4);
const r4Years = r100Years - 4 * q4Years;
const yearIsLeap = r4Years === 3 && (r100Years !== 99 || r400Years === 399);
const yearStartTimestamp = YEAR_START_2001_MS + q400Years * FOUR_HUNDRED_YEARS_MS + q100Years * HUNDRED_YEARS_MS + q4Years * FOUR_YEARS_MS + r4Years * COMMON_YEAR_MS;
const selfYearIsLeap = self.yearIsLeap;
const selfOrdinalDay = self.ordinalDay;
const ordinalDayOffset = yield* selfYearIsLeap === yearIsLeap || selfOrdinalDay < 60 ? Either.right(0) : selfYearIsLeap ? selfOrdinalDay === 60 ? Either.left(new MInputError.Type({
message: `No February 29th on year ${year} which is not a leap year`
})) : Either.right(-1) : Either.right(1);
return _makeWithInternals({
timestamp: self.timestamp + yearStartTimestamp - self.yearStartTimestamp + ordinalDayOffset * DAY_MS,
year: validatedYear,
yearIsLeap,
yearStartTimestamp,
ordinalDay: selfOrdinalDay + ordinalDayOffset,
month: self.month,
monthDay: self.monthDay
});
});
/**
* If possible, returns a new GregorianDate having `month` set to `month` and the same `year` and
* `monthDay` as `self`. Returns a left of an error otherwise. `month` must be an integer greater
* than or equal to 1 (January) and less than or equal to 12 (December). `month` must also have at
* least `monthDay` days.
*
* @category Setters
*/
GregorianDate.setMonth = month => self => Either.gen(function* () {
const validatedMonth = yield* pipe(month, MInputError.assertInRange({
min: 1,
max: 12,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'month'"
}));
const monthDay = yield* pipe(self, GregorianDate.getMonthDay, Either.liftPredicate(Predicate.or(Number.lessThanOrEqualTo(28), Number.lessThanOrEqualTo(GregorianDate.getNumberOfDaysInMonth(validatedMonth)(self))), selfMonthDay => new MInputError.Type({
message: `Month ${month} of year ${self.year} does not have ${selfMonthDay} days`
})));
const ordinalDay = GregorianDate.getMonthOffset(validatedMonth)(self) + monthDay;
return pipe(self, MStruct.append({
timestamp: self.timestamp + (ordinalDay - self.ordinalDay) * DAY_MS,
ordinalDay,
month: Option.some(validatedMonth)
}), _make);
});
/**
* If possible, returns a new GregorianDate having `monthDay` set to `monthDay` and the same
* `year` and `month` as `self`. Returns a left of an error otherwise. `monthDay` must be an
* integer greater than or equal to 1 and less than or equal to the number of days in the current
* month.
*
* @category Setters
*/
GregorianDate.setMonthDay = monthDay => self => Either.gen(function* () {
const validatedMonthDay = monthDay <= 28 ? monthDay : yield* pipe(monthDay, MInputError.assertInRange({
min: 1,
max: GregorianDate.getNumberOfDaysInMonth(GregorianDate.getMonth(self))(self),
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'monthDay'"
}));
const ordinalDayOffset = validatedMonthDay - GregorianDate.getMonthDay(self);
return pipe(self, MStruct.append({
timestamp: self.timestamp + ordinalDayOffset * DAY_MS,
ordinalDay: self.ordinalDay + ordinalDayOffset,
monthDay: Option.some(validatedMonthDay)
}), _make);
});
/**
* If possible, returns a new GregorianDate having `ordinalDay` set to `ordinalDay` and the same
* `year` as `self`. Returns a left of an error otherwise. `ordinalDay` must be an integer greater
* than or equal to 1 and less than or equal to the number of days in the current year
*
* @category Setters
*/
GregorianDate.setOrdinalDay = ordinalDay => self => Either.gen(function* () {
const validatedOrdinalDay = yield* pipe(ordinalDay, MInputError.assertInRange({
min: 1,
max: GregorianDate.getYearDurationInDays(self),
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'ordinalDay'"
}));
return pipe(self, MStruct.append({
timestamp: self.timestamp + (validatedOrdinalDay - self.ordinalDay) * DAY_MS,
ordinalDay: validatedOrdinalDay,
month: Option.none(),
monthDay: Option.none()
}), _make);
});
/**
* Returns the `timestamp` property of `self`
*
* @category Destructors
*/
GregorianDate.timestamp = /*#__PURE__*/Struct.get('timestamp');
/**
* Returns the `year` property of `self`
*
* @category Destructors
*/
GregorianDate.year = /*#__PURE__*/Struct.get('year');
/**
* Returns the `yearIsLeap` property of `self`
*
* @category Predicates
*/
GregorianDate.yearIsLeap = /*#__PURE__*/Struct.get('yearIsLeap');
/**
* Returns the `yearStartTimestamp` property of `self`
*
* @category Destructors
*/
GregorianDate.yearStartTimestamp = /*#__PURE__*/Struct.get('yearStartTimestamp');
/**
* Returns the `ordinalDay` property of `self`
*
* @category Destructors
*/
GregorianDate.ordinalDay = /*#__PURE__*/Struct.get('ordinalDay');
/**
* Returns the `month` of `self`
*
* @category Destructors
*/
GregorianDate.getMonth = self => pipe(self.month, Option.getOrElse(() => {
const ordinalDay = self.ordinalDay;
const yearIsLeap = self.yearIsLeap;
const adjustedOrdinalDay = ordinalDay - (yearIsLeap ? 1 : 0);
const result = ordinalDay <= 31 ? 1 : adjustedOrdinalDay <= 59 ? 2 : Math.floor((adjustedOrdinalDay - 59) / 30.6 - 0.018) + 3;
/* eslint-disable-next-line functional/immutable-data, functional/no-expression-statements */
self.month = Option.some(result);
return result;
}));
/**
* Returns the `monthDay` of `self`
*
* @category Destructors
*/
GregorianDate.getMonthDay = self => pipe(self.monthDay, Option.getOrElse(() => {
const result = self.ordinalDay - GregorianDate.getMonthOffset(GregorianDate.getMonth(self))(self);
/* eslint-disable-next-line functional/immutable-data, functional/no-expression-statements */
self.monthDay = Option.some(result);
return result;
}));
/**
* Returns the duration of the year described by `self` in milliseconds
*
* @category Destructors
*/
GregorianDate.getYearDurationInMs = self => self.yearIsLeap ? LEAP_YEAR_MS : COMMON_YEAR_MS;
/**
* Returns the duration of the year described by `self` in days
*
* @category Destructors
*/
GregorianDate.getYearDurationInDays = self => self.yearIsLeap ? 366 : 365;
/**
* Returns the number of days from the start of the `year` property of `self` to the day before
* the first day of month `month`
*
* @category Destructors
*/
GregorianDate.getMonthOffset = month => self => month === 1 ? 0 : month === 2 ? 31 : 30 * (month - 1) + Math.floor(0.6 * (month + 1)) - (self.yearIsLeap ? 2 : 3);
/**
* Returns the number of days of month `month` of the `year` property of `self`
*
* @category Destructors
*/
GregorianDate.getNumberOfDaysInMonth = month => flow(Struct.get('_daysInMonth'), MArray.unsafeGet(month - 1));
const _formatter = /*#__PURE__*/flow(/*#__PURE__*/CVTemplate.toFormatter(/*#__PURE__*/CVTemplate.make(/*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'year',
length: 4
}), _sep.hyphen, /*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'month',
length: 2
}), _sep.hyphen, /*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'monthDay',
length: 2
}))), /*#__PURE__*/Either.getOrThrowWith(Function.identity));
/**
* Returns the ISO representation of this Gregorian Date
*
* @category Destructors
*/
GregorianDate.getIsoString = /*#__PURE__*/flow(/*#__PURE__*/MStruct.enrichWith({
month: GregorianDate.getMonth,
monthDay: GregorianDate.getMonthDay
}), _formatter);
})(GregorianDate || (GregorianDate = {}));
/**
* Namespace for an IsoDate.
*
* An iso year starts on the first day of the first iso week. An iso week starts on a monday and
* ends on a sunday. The first iso week of the year is the one that contains January 4th (see
* Wikipedia).
*
* @category Models
*/
var IsoDate;
(function (IsoDate) {
const _namespaceTag = moduleTag + 'IsoDate/';
const _TypeId = /*#__PURE__*/Symbol.for(_namespaceTag);
/**
* Duration in milliseconds of a 6-iso-year period comprised of 1 long year and 5 short years (see
* Wikipedia)
*
* @category Constants
*/
const SIX_YEARS_MS = LONG_YEAR_MS + 5 * SHORT_YEAR_MS;
/**
* Duration in milliseconds of an 11-iso-year period comprised of 2 long years and 9 short years
* (see Wikipedia)
*
* @category Constants
*/
const ELEVEN_YEARS_MS = 2 * LONG_YEAR_MS + 9 * SHORT_YEAR_MS;
/**
* Duration in milliseconds of a 28-iso-year period comprised of 5 long years and 23 short years
* (see Wikipedia)
*
* @category Constants
*/
const TWENTY_EIGHT_YEARS_MS = 5 * LONG_YEAR_MS + 23 * SHORT_YEAR_MS;
/**
* Duration in milliseconds of a 96-iso-year period comprised of 17 long years and 79 short years
* (see Wikipedia)
*
* @category Constants
*/
const NINETY_SIX_YEARS_MS = 17 * LONG_YEAR_MS + 79 * SHORT_YEAR_MS;
/**
* Duration in milliseconds of a 100-iso-year period comprised of 18 long years and 82 short years
* (see Wikipedia)
*
* @category Constants
*/
const ONE_HUNDRED_YEARS_MS = 18 * LONG_YEAR_MS + 82 * SHORT_YEAR_MS;
/**
* Duration in milliseconds of a 400-iso-year period comprised of 71 long years and 329 short
* years (see Wikipedia)
*
* @category Constants
*/
const FOUR_HUNDRED_YEARS_MS = 71 * LONG_YEAR_MS + 329 * SHORT_YEAR_MS;
/**
* Timestamp of 03/01/2000 00:00:00:000+0:00
*
* @category Constants
*/
const YEAR_START_2000_MS = 946_857_600_000;
/**
* Timestamp of 04/01/2010 00:00:00:000+0:00
*
* @category Constants
*/
const YEAR_START_2010_MS = 1_262_563_200_000;
/**
* Type guard
*
* @category Guards
*/
IsoDate.has = u => Predicate.hasProperty(u, _TypeId);
/** Proto */
const proto = {
[_TypeId]: _TypeId,
... /*#__PURE__*/MInspectable.BaseProto(_namespaceTag),
...MPipeable.BaseProto
};
/** Constructor */
const _make = params => MTypes.objectFromDataAndProto(proto, params);
/**
* Constructs an IsoDate from a timestamp
*
* @category Constructors
*/
IsoDate.fromTimestamp = timestamp => {
const offset = timestamp - YEAR_START_2000_MS;
const q400Years = Math.floor(offset / FOUR_HUNDRED_YEARS_MS);
const r400Years = offset - q400Years * FOUR_HUNDRED_YEARS_MS;
// The second one-hundred year period is a week shorter because it has 17 long years instead of 18
// Also the hundred-th year must be put in the first one-hundred year period because it is not long
const q100Years = r400Years < ONE_HUNDRED_YEARS_MS + SHORT_YEAR_MS ? 0 : Math.floor((r400Years + WEEK_MS) / ONE_HUNDRED_YEARS_MS);
const adjustedR400Years = r400Years - q100Years * NINETY_SIX_YEARS_MS + SHORT_YEAR_MS;
const q28Years = Math.floor(adjustedR400Years / TWENTY_EIGHT_YEARS_MS);
const r28Years = adjustedR400Years - q28Years * TWENTY_EIGHT_YEARS_MS;
const adjustedR28Years = r28Years - ELEVEN_YEARS_MS;
const q11Years = Math.floor(adjustedR28Years / ELEVEN_YEARS_MS);
const r11Years = adjustedR28Years - q11Years * ELEVEN_YEARS_MS;
const q6Years = Math.floor(r11Years / SIX_YEARS_MS);
const r6Years = r11Years - q6Years * SIX_YEARS_MS;
const isFirstSixYearPeriod = q6Years === 0;
const q1Year = Math.min(Math.floor(r6Years / SHORT_YEAR_MS), isFirstSixYearPeriod ? 5 : 4);
//console.log(q400Years, q100Years, q28Years, q11Years, q6Years, q1Year);
return _make({
timestamp,
year: 2010 + q400Years * 400 + q100Years * 96 + q28Years * 28 + q11Years * 11 + q6Years * 6 + q1Year,
yearStartTimestamp: YEAR_START_2010_MS + q400Years * FOUR_HUNDRED_YEARS_MS + q100Years * NINETY_SIX_YEARS_MS + q28Years * TWENTY_EIGHT_YEARS_MS + q11Years * ELEVEN_YEARS_MS + q6Years * SIX_YEARS_MS + q1Year * SHORT_YEAR_MS,
yearIsLong: isFirstSixYearPeriod && q1Year == 5 || !isFirstSixYearPeriod && q1Year == 4,
isoWeek: Option.none(),
weekday: Option.none()
});
};
/**
* Constructs an IsoDate from a GregorianDate
*
* @category Constructors
*/
IsoDate.fromGregorianDate = gregorianDate => {
// 0 is friday, 6 is thursday
const yearStartWeekday = MNumber.intModulo(7)(Math.floor((gregorianDate.yearStartTimestamp - DAY_MS) / DAY_MS));
const yearIsLeap = gregorianDate.yearIsLeap;
const minOrdinalDayIndex = 3 - yearStartWeekday;
const ordinalDay = gregorianDate.ordinalDay;
if (ordinalDay <= minOrdinalDayIndex) {
const year = gregorianDate.year - 1;
const yearIsLong = yearStartWeekday === 0 || yearStartWeekday === 1 && !yearIsLeap && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0);
return _make({
timestamp: gregorianDate.timestamp,
year,
yearStartTimestamp: gregorianDate.yearStartTimestamp + (minOrdinalDayIndex - (yearIsLong ? 371 : 364)) * DAY_MS,
yearIsLong,
isoWeek: Option.none(),
weekday: Option.none()
});
}
const yearIsLong = yearStartWeekday === 6 || yearStartWeekday === 5 && yearIsLeap;
const maxOrdinalDay = minOrdinalDayIndex + (yearIsLong ? 371 : 364);
if (ordinalDay > maxOrdinalDay) {
const year = gregorianDate.year + 1;
const yearIsLong = yearIsLeap ? yearStartWeekday === 4 : yearStartWeekday === 5 || yearStartWeekday === 4 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0);
return _make({
timestamp: gregorianDate.timestamp,
year,
yearStartTimestamp: gregorianDate.yearStartTimestamp + maxOrdinalDay * DAY_MS,
yearIsLong,
isoWeek: Option.none(),
weekday: Option.none()
});
}
return _make({
timestamp: gregorianDate.timestamp,
year: gregorianDate.year,
yearStartTimestamp: gregorianDate.yearStartTimestamp + minOrdinalDayIndex * DAY_MS,
yearIsLong,
isoWeek: Option.none(),
weekday: Option.none()
});
};
/**
* If possible, returns a new IsoDate having `year` set to `year` and the same `isoWeek` and
* `weekday` as `self`. Returns a left of an error otherwise. `year` must be an integer comprised
* in the range [MIN_FULL_YEAR, MAX_FULL_YEAR]. If the isoWeek of `self` is equal to 53, `year`
* must be a long year.
*
* @category Setters
*/
IsoDate.setYear = year => self => Either.gen(function* () {
const validatedYear = yield* pipe(year, MInputError.assertInRange({
min: MIN_FULL_YEAR,
max: MAX_FULL_YEAR,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'year'"
}));
const offset = validatedYear - 2000;
const q400Years = Math.floor(offset / 400);
const r400Years = offset - q400Years * 400;
// year 100 needs to be treated in the first one-hundred year period because it is not a long year
const q100Years = r400Years === 100 ? 0 : Math.floor(r400Years / 100);
const adjustedR400Years = r400Years - q100Years * 96 + 1;
const q28Years = Math.floor(adjustedR400Years / 28);
const r28Years = adjustedR400Years - q28Years * 28;
const adjustedR28Years = r28Years - 11;
const q11Years = Math.floor(adjustedR28Years / 11);
const r11Years = adjustedR28Years - q11Years * 11;
const yearStartTimestamp = YEAR_START_2010_MS + q400Years * FOUR_HUNDRED_YEARS_MS + q100Years * NINETY_SIX_YEARS_MS + q28Years * TWENTY_EIGHT_YEARS_MS + q11Years * ELEVEN_YEARS_MS + r11Years * SHORT_YEAR_MS + (r11Years > 5 ? WEEK_MS : 0);
return yield* pipe(_make({
timestamp: self.timestamp + yearStartTimestamp - self.yearStartTimestamp,
year: validatedYear,
yearStartTimestamp,
yearIsLong: r11Years === 5 || r11Years === 10,
isoWeek: Option.some(IsoDate.getIsoWeek(self)),
weekday: Option.some(IsoDate.getWeekday(self))
}), Either.liftPredicate(Predicate.or(IsoDate.yearIsLong, flow(IsoDate.getIsoWeek, Number.lessThan(53))), () => new MInputError.Type({
message: `No 53rd week on iso year ${year} which is not a short year`
})));
});
/**
* If possible, returns a new IsoDate having `isoWeek` set to `isoWeek` and the same `year` and
* `weekday` as `self`. Returns a left of an error otherwise. `isoWeek` must be an integer greater
* than or equal to 1 and less than or equal to the number of iso weeks in the current year.
*
* @category Setters
*/
IsoDate.setIsoWeek = isoWeek => self => Either.gen(function* () {
const validatedIsoWeek = yield* pipe(isoWeek, MInputError.assertInRange({
min: 1,
max: IsoDate.getLastIsoWeek(self),
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'isoWeek'"
}));
const offset = validatedIsoWeek - IsoDate.getIsoWeek(self);
return pipe(self, MStruct.evolve({
timestamp: Number.sum(offset * WEEK_MS),
isoWeek: pipe(validatedIsoWeek, Option.some, Function.constant)
}), _make);
});
/**
* If possible, returns a new IsoDate having `weekday` set to `weekday` and the same `year` and
* `isoWeek` as `self`. Returns a left of an error otherwise. `weekday` must be an integer greater
* than or equal to 1 (monday) and less than or equal to 7 (sunday).
*
* @category Setters
*/
IsoDate.setWeekday = weekday => self => Either.gen(function* () {
const validatedWeekday = yield* pipe(weekday, MInputError.assertInRange({
min: 1,
max: 7,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'weekday'"
}));
const offset = validatedWeekday - IsoDate.getWeekday(self);
return pipe(self, MStruct.evolve({
timestamp: Number.sum(offset * DAY_MS),
weekday: pipe(validatedWeekday, Option.some, Function.constant)
}), _make);
});
/**
* Returns the `timestamp` property of `self`
*
* @category Destructors
*/
IsoDate.timestamp = /*#__PURE__*/Struct.get('timestamp');
/**
* Returns the `year` property of `self`
*
* @category Destructors
*/
IsoDate.year = /*#__PURE__*/Struct.get('year');
/**
* Returns the `yearStartTimestamp` property of `self`
*
* @category Destructors
*/
IsoDate.yearStartTimestamp = /*#__PURE__*/Struct.get('yearStartTimestamp');
/**
* Returns the `yearIsLong` property of `self`
*
* @category Predicates
*/
IsoDate.yearIsLong = /*#__PURE__*/Struct.get('yearIsLong');
/**
* Returns the `isoWeek` of `self`
*
* @category Destructors
*/
IsoDate.getIsoWeek = self => pipe(self.isoWeek, Option.getOrElse(() => {
const result = Math.floor((self.timestamp - self.yearStartTimestamp) / WEEK_MS) + 1;
/* eslint-disable-next-line functional/immutable-data, functional/no-expression-statements */
self.isoWeek = Option.some(result);
return result;
}));
/**
* Returns the `weekday` of `self`
*
* @category Destructors
*/
IsoDate.getWeekday = self => pipe(self.weekday, Option.getOrElse(() => {
const result = Math.floor((self.timestamp - self.yearStartTimestamp - (IsoDate.getIsoWeek(self) - 1) * WEEK_MS) / DAY_MS) + 1;
/* eslint-disable-next-line functional/immutable-data, functional/no-expression-statements */
self.weekday = Option.some(result);
return result;
}));
/**
* Returns the duration of the year described by `self` in milliseconds
*
* @category Destructors
*/
IsoDate.getMsDuration = self => self.yearIsLong ? LONG_YEAR_MS : SHORT_YEAR_MS;
/**
* Returns the duration of the year described by `self` in milliseconds
*
* @category Destructors
*/
IsoDate.getLastIsoWeek = self => self.yearIsLong ? 53 : 52;
const _formatter = /*#__PURE__*/flow(/*#__PURE__*/CVTemplate.toFormatter(/*#__PURE__*/CVTemplate.make(/*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'year',
length: 4
}), /*#__PURE__*/_sep.make('-W'), /*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'isoWeek',
length: 2
}), _sep.hyphen, /*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'weekday',
length: 2
}))), /*#__PURE__*/Either.getOrThrowWith(Function.identity));
/**
* Returns the ISO representation of this Gregorian Date
*
* @category Destructors
*/
IsoDate.getIsoString = /*#__PURE__*/flow(/*#__PURE__*/MStruct.enrichWith({
isoWeek: IsoDate.getIsoWeek,
weekday: IsoDate.getWeekday
}), _formatter);
})(IsoDate || (IsoDate = {}));
/**
* Namespace for the data relative to the time
*
* @category Models
*/
var Time;
(function (Time) {
const _namespaceTag = moduleTag + 'Time/';
const _TypeId = /*#__PURE__*/Symbol.for(_namespaceTag);
/**
* Type guard
*
* @category Guards
*/
Time.has = u => Predicate.hasProperty(u, _TypeId);
/** Proto */
const proto = {
[_TypeId]: _TypeId,
... /*#__PURE__*/MInspectable.BaseProto(_namespaceTag),
...MPipeable.BaseProto
};
/** Constructor */
const _make = params => MTypes.objectFromDataAndProto(proto, params);
/**
* Constructs the Time that corresponds to the passed `timestampOffset` which is the number of
* milliseconds from the start of the current day
*
* @category Constructors
*/
Time.fromTimestamp = timestampOffset => {
const hour23 = Math.floor(timestampOffset / HOUR_MS);
const rHour23 = timestampOffset - hour23 * HOUR_MS;
const [hour11, meridiem] = hour23 >= 12 ? [hour23 - 12, 12] : [hour23, 0];
const minute = Math.floor(rHour23 / MINUTE_MS);
const rMinute = rHour23 - minute * MINUTE_MS;
const second = Math.floor(rMinute / SECOND_MS);
return _make({
timestampOffset,
hour23,
hour11,
meridiem,
minute,
second,
millisecond: rMinute - second * SECOND_MS
});
};
/**
* If possible, returns a right of a copy of `self` with `hour23` set to `hour23`. Returns a left
* of an error otherwise. `hour23` must be an integer greater than or equal to 0 and less than or
* equal to 23
*
* @category Setters
*/
Time.setHour23 = hour23 => self => Either.gen(function* () {
const validatedHour23 = yield* pipe(hour23, MInputError.assertInRange({
min: 0,
max: 23,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'hour23'"
}));
const isPast12 = validatedHour23 >= 12;
return _make({
...self,
timestampOffset: self.timestampOffset + (validatedHour23 - self.hour23) * HOUR_MS,
hour23: validatedHour23,
hour11: isPast12 ? hour23 - 12 : hour23,
meridiem: isPast12 ? 12 : 0
});
});
/**
* If possible, returns a right of a copy of `self` with `hour11` set to `hour11`. Returns a left
* of an error otherwise. `hour11` must be an integer greater than or equal to 0 and less than or
* equal to 11
*
* @category Setters
*/
Time.setHour11 = hour11 => self => Either.gen(function* () {
const validatedHour11 = yield* pipe(hour11, MInputError.assertInRange({
min: 0,
max: 11,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'hour11'"
}));
const validatedHour23 = self.meridiem + validatedHour11;
return _make({
...self,
timestampOffset: self.timestampOffset + (validatedHour23 - self.hour23) * HOUR_MS,
hour23: validatedHour23,
hour11: validatedHour11
});
});
/**
* Returns a copy of `self` with `meridiem` set to `merdiem`.
*
* @category Setters
*/
Time.setMeridiem = meridiem => self => {
const validatedHour23 = self.hour11 + meridiem;
return _make({
...self,
timestampOffset: self.timestampOffset + (validatedHour23 - self.hour23) * HOUR_MS,
hour23: validatedHour23,
meridiem
});
};
/**
* If possible, returns a right of a copy of `self` with `minute` set to `minute`. Returns a left
* of an error otherwise. `minute` must be an integer greater than or equal to 0 and less than or
* equal to 59
*
* @category Setters
*/
Time.setMinute = minute => self => Either.gen(function* () {
const validatedMinute = yield* pipe(minute, MInputError.assertInRange({
min: 0,
max: 59,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'minute'"
}));
return _make({
...self,
timestampOffset: self.timestampOffset + (validatedMinute - self.minute) * MINUTE_MS,
minute: validatedMinute
});
});
/**
* If possible, returns a right of a copy of `self` with `second` set to `second`. Returns a left
* of an error otherwise. `second` must be an integer greater than or equal to 0 and less than or
* equal to 59
*
* @category Setters
*/
Time.setSecond = second => self => Either.gen(function* () {
const validatedSecond = yield* pipe(second, MInputError.assertInRange({
min: 0,
max: 59,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'second'"
}));
return _make({
...self,
timestampOffset: self.timestampOffset + (validatedSecond - self.second) * SECOND_MS,
second: validatedSecond
});
});
/**
* If possible, returns a right of a copy of `self` with `millisecond` set to `millisecond`.
* Returns a left of an error otherwise. `millisecond` must be an integer greater than or equal to
* 0 and less than or equal to 999
*
* @category Setters
*/
Time.setMillisecond = millisecond => self => Either.gen(function* () {
const validatedMillisecond = yield* pipe(millisecond, MInputError.assertInRange({
min: 0,
max: 999,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'millisecond'"
}));
return _make({
...self,
timestampOffset: self.timestampOffset + validatedMillisecond - self.millisecond,
millisecond: validatedMillisecond
});
});
/**
* Returns the `timestampOffset` property of `self`
*
* @category Destructors
*/
Time.timestampOffset = /*#__PURE__*/Struct.get('timestampOffset');
/**
* Returns the `hour23` property of `self`
*
* @category Destructors
*/
Time.hour23 = /*#__PURE__*/Struct.get('hour23');
/**
* Returns the `hour11` property of `self`
*
* @category Destructors
*/
Time.hour11 = /*#__PURE__*/Struct.get('hour11');
/**
* Returns the `meridiem` property of `self`
*
* @category Destructors
*/
Time.meridiem = /*#__PURE__*/Struct.get('meridiem');
/**
* Returns the `minute` property of `self`
*
* @category Destructors
*/
Time.minute = /*#__PURE__*/Struct.get('minute');
/**
* Returns the `second` property of `self`
*
* @category Destructors
*/
Time.second = /*#__PURE__*/Struct.get('second');
/**
* Returns the `millisecond` property of `self`
*
* @category Destructors
*/
Time.millisecond = /*#__PURE__*/Struct.get('millisecond');
const _formatter = /*#__PURE__*/flow(/*#__PURE__*/CVTemplate.toFormatter(/*#__PURE__*/CVTemplate.make(/*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'hour23',
length: 2
}), _sep.colon, /*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'minute',
length: 2
}), _sep.colon, /*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'second',
length: 2
}), _sep.dot, /*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'millisecond',
length: 3
}))), /*#__PURE__*/Either.getOrThrowWith(Function.identity));
/**
* Returns the ISO representation of this Gregorian Date
*
* @category Destructors
*/
Time.getIsoString = _formatter;
})(Time || (Time = {}));
/**
* Namespace for the data relative to the parts of a zone offset
*
* @category Models
*/
var ZoneOffsetParts;
(function (ZoneOffsetParts) {
const _namespaceTag = moduleTag + 'ZoneOffsetParts/';
const _TypeId = /*#__PURE__*/Symbol.for(_namespaceTag);
/**
* Type guard
*
* @category Guards
*/
ZoneOffsetParts.has = u => Predicate.hasProperty(u, _TypeId);
/** Proto */
const proto = {
[_TypeId]: _TypeId,
... /*#__PURE__*/MInspectable.BaseProto(_namespaceTag),
...MPipeable.BaseProto
};
ZoneOffsetParts._make = params => MTypes.objectFromDataAndProto(proto, params);
/**
* Builds a ZoneOffsetParts from `zoneOffset`
*
* @category Constructors
*/
ZoneOffsetParts.fromZoneOffset = zoneOffset => {
const zoneHour = Math.trunc(zoneOffset);
const minutesSeconds = Math.abs(zoneOffset - zoneHour) * 60;
const zoneMinute = Math.trunc(minutesSeconds);
const zoneSecond = Math.trunc((minutesSeconds - zoneMinute) * 60);
return ZoneOffsetParts._make({
zoneHour,
zoneMinute,
zoneSecond
});
};
/**
* Tries to build a ZoneOffsetParts from `zoneHour`, `zoneMinute`, `zoneSecond`. Returns a `some`
* if successful. A `none` otherwise.
*
* - `zoneHour` must be greater than or equal to -12 and less than or equal to 14.
* - `zoneMinute` must be greater than or equal to 0 and less than or equal to 59.
* - `zoneSecond` must be greater than or equal to 0 and less than or equal to 59.
*
* @category Constructors
*/
ZoneOffsetParts.fromParts = ({
zoneHour,
zoneMinute,
zoneSecond
}) => Either.gen(function* () {
const validatedHour = yield* pipe(zoneHour, MInputError.assertInRange({
min: -12,
max: 14,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'zoneHour'"
}));
const validatedMinute = yield* pipe(zoneMinute, MInputError.assertInRange({
min: 0,
max: 59,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'zoneMinute'"
}));
const validatedSecond = yield* pipe(zoneSecond, MInputError.assertInRange({
min: 0,
max: 59,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: "'zoneSecond'"
}));
return ZoneOffsetParts._make({
zoneHour: validatedHour,
zoneMinute: validatedMinute,
zoneSecond: validatedSecond
});
});
/**
* Returns the `zoneHour` property of `self`
*
* @category Destructors
*/
ZoneOffsetParts.zoneHour = /*#__PURE__*/Struct.get('zoneHour');
/**
* Returns the `zoneMinute` property of `self`
*
* @category Destructors
*/
ZoneOffsetParts.zoneMinute = /*#__PURE__*/Struct.get('zoneMinute');
/**
* Returns the `zoneSecond` property of `self`
*
* @category Destructors
*/
ZoneOffsetParts.zoneSecond = /*#__PURE__*/Struct.get('zoneSecond');
const _formatter = /*#__PURE__*/flow(/*#__PURE__*/CVTemplate.toFormatter(/*#__PURE__*/CVTemplate.make(/*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'zoneHour',
length: 3,
numberBase10Format: /*#__PURE__*/pipe(_integer, CVNumberBase10Format.withSignDisplay)
}), _sep.colon, /*#__PURE__*/_fixedLengthToReal({
..._params,
name: 'zoneMinute',
length: 2
}))), /*#__PURE__*/Either.getOrThrowWith(Function.identity));
/**
* Returns the ISO representation of this Gregorian Date
*
* @category Destructors
*/
ZoneOffsetParts.getIsoString = _formatter;
/**
* Returns the value of `self` expressed in hours
*
* @category Destructors
*/
ZoneOffsetParts.toHour = self => {
const hour = self.zoneHour;
const sign = MNumber.sign2(hour);
return hour + sign * (self.zoneMinute / 60 + self.zoneSecond / 3600);
};
})(ZoneOffsetParts || (ZoneOffsetParts = {}));
/**
* Type guard
*
* @category Guards
*/
export const has = u => Predicate.hasProperty(u, _TypeId);
/**
* Equivalence
*
* @category Equivalences
*/
export const equivalence = (self, that) => self.timestamp === that.timestamp;
/** Proto */
const _TypeIdHash = /*#__PURE__*/Hash.hash(_TypeId);
const proto = {
[_TypeId]: _TypeId,
[Equal.symbol](that) {
return has(that) && equivalence(this, that);
},
[Hash.symbol]() {
return pipe(this.timestamp, Hash.hash, Hash.combine(_TypeIdHash), Hash.cached(this));
},
[MInspectable.IdSymbol]() {
return getIsoString(this);
},
... /*#__PURE__*/MInspectable.BaseProto(moduleTag),
...MPipeable.BaseProto
};
/** Constructor */
const _make = params => MTypes.objectFromDataAndProto(proto, params);
/**
* Returns the ISO representation of this DateTime
*
* @category Destructors
*/
export const getIsoString = self => GregorianDate.getIsoString(_gregorianDate(self)) + 'T' + Time.getIsoString(_time(self)) + ZoneOffsetParts.getIsoString(_zoneOffsetParts(self));
const _uncalculated = {
gregorianDate: /*#__PURE__*/Option.none(),
isoDate: /*#__PURE__*/Option.none(),
time: /*#__PURE__*/Option.none(),
zoneOffsetParts: /*#__PURE__*/Option.none()
};
/**
* Constructor that creates a DateTime from a timestamp and a zoneOffset for which no calculations
* have been carried out yet. The `_zonedTimestamp` field is automatically calculated. Does not
* check any input parameters
*/
const _uncalculatedFromTimestamp = (timestamp, zoneOffset) => _make({
..._uncalculated,
timestamp,
zoneOffset,
_zonedTimestamp: timestamp + zoneOffset * HOUR_MS
});
/**
* Constructor that creates a DateTime from a zonedTimestamp and a zoneOffset for which no
* calculations have been carried out yet. The `timestamp` field is automatically calculated. Does
* not check any input parameters
*/
const _uncalculatedFromZonedTimestamp = (zonedTimestamp, zoneOffset) => _make({
..._uncalculated,
timestamp: zonedTimestamp - zoneOffset * HOUR_MS,
zoneOffset,
_zonedTimestamp: zonedTimestamp
});
/** Instance of an uncalculated DateTime that represents 1/1/1970 00:00:00:000+0:00 */
const _uncalculatedOrigin = /*#__PURE__*/_uncalculatedFromTimestamp(0, 0);
/**
* Tries to build a `CVDateTime` from `timestamp`, the number of milliseconds since 1/1/1970
* 00:00:00:000+0:00, and `zoneOffset` which gives the offset between the local time and the UTC
* time. Returns a `Right` if successful, a `Left` otherwise.
*
* `timestamp` must be greater than or equal to MIN_TIMESTAMP and less than or equal to
* MAX_TIMESTAMP.
*
* If `zoneOffset` is omitted, the local time zone offset of the machine this code is running on is
* used.
*
* `zoneOffset` can be expressed as as a number of hours. In this case, it must be strictly greater
* to -13 and strictly less than 15.
*
* It can also be expressed as an object containing three components:
*
* - `zoneHour` which must be greater than or equal to -12 and less than or equal to 14.
* - `zoneMinute` which must be greater than or equal to 0 and less than or equal to 59.
* - `zoneSecond` which must be greater than or equal to 0 and less than or equal to 59.
*
* Note that zoneHour=-0, zoneMinute=10, zoneSecond=0 is different from zoneHour=0, zoneMinute=10,
* zoneSecond=0. The first corresponds to the string 'GMT-00:10', a negative 10-minute offset, the
* second one to the string 'GMT+00:10', a positive 10-minute offset.
*
* `timestamp`, `zoneHour`, `zoneMinute` and `zoneSecond` should be integers. `zoneOffset`, when
* expressed as a number of hours, does not need to be an integer.
*
* @category Constructors
*/
export const fromTimestamp = (timestamp, zoneOffset) => pipe(_uncalculatedOrigin, _setTimestamp(timestamp), Either.flatMap(setZoneOffsetKeepTimestamp(zoneOffset)));
/**
* Same as `fromTimestamp` but returns directly a `CVDateTime` or throws if it cannot be built
*
* @category Constructors
*/
export const fromTimestampOrThrow = /*#__PURE__*/flow(fromTimestamp, /*#__PURE__*/Either.getOrThrowWith(Function.identity));
/**
* Builds a `CVDateTime` using Date.now() as `timestamp`. `zoneOffset` is set to 0.
*
* @category Constructors
*/
export const now = () => _uncalculatedFromTimestamp(Date.now(), 0);
/**
* Tries to build a `CVDateTime` from the provided parts. Returns a `Right` if successful, a `Left`
* otherwise.
*
* `year` must comprised in the range [MIN_FULL_YEAR, MAX_FULL_YEAR]. `ordinalDay` must be greater
* than or equal to 1 and less than or equal to the number of days in the current year. `month` must
* be greater than or equal to 1 (January) and less than or equal to 12 (December). `monthDay` must
* be greater than or equal to 1 and less than or equal to the number of days in the current month.
*
* `isoYear` must be comprised in the range [MIN_FULL_YEAR, MAX_FULL_YEAR]. `isoWeek` must be
* greater than or equal to 1 and less than or equal to the number of iso weeks in the current year.
* `weekday` must be greater than or equal to 1 (monday) and less than or equal to 7 (sunday).
*
* If there is not sufficient information to determine the exact day of the year, i.e. none of the
* three following tuples is fully determined [year, ordinalDay], [year, month, monthDay], [isoYear,
* isoWeek, weekday], default values are determined in the following order (the first match stops
* the process):
*
* - If `year` and `month` are set, `monthDay` is taken equal to 1.
* - If `year` and `monthDay` are set, `month` is taken equal to 1.
* - If `year` is set and both `month` and `monthDay` are undefined, the day is taken to be the first
* one in the year.
* - If `isoYear` and `isoWeek` are set, `weekday` is taken equal to 1.
* - If `isoYear` and `weekday` are set, `isoWeek` is taken equal to 1.
* - If `isoYear` is set and both `isoWeek` and `weekday` are undefined, the day is taken to be the
* first one in the iso year.
* - If both `year` and `isoYear` are undefined, an error is raised.
*
* `hour23` must be greater than or equal to 0 and less than or equal to 23. `hour11` must be
* greater than or equal to 0 and less than or equal to 11. `meridiem` must be one of 0 (AM) or 12
* (PM). If there is not sufficient information to determine the hour of the day, i.e. none of the
* two following tuples is fully determined [hour23], [hour11, meridiem], default values are
* determined as follows:
*
* - If `meridiem` is set, `hour11` is taken equal to 0.
* - If `hour11` is set, `meridiem` is taken equal to 0.
* - Otherwise, `meridiem` and `hour11` are taken equal to 0.
*
* `minute` must be greater than or equal to 0 and less than or equal to 59. If omitted, minute is
* assumed to be 0.
*
* `second` must be greater than or equal to 0 and less than or equal to 59. If omitted, second is
* assumed to be 0.
*
* `millisecond` must be greater than or equal to 0 and less than or equal to 999. If omitted,
* millisecond is assumed to be 0.
*
* `zoneOffset` must be strictly greater to -13 and strictly less than 15. `zoneHour` must be
* greater than or equal to -12 and less than or equal to 14. `zoneMinute` must be greater than or
* equal to 0 and less than or equal to 59. `zoneSecond` must be greater than or equal to 0 and less
* than or equal to 59.
*
* If there is not sufficient information to determine the exact time zone offset, i.e. none of the
* two following tuples is fully determined [zoneOffset], [zoneHour, zoneMinute, zoneSecond],
* default values are determined as follows :
*
* - If all parameters are undefined, the local time zone offset of the machine this code is running
* on is used.
* - If any of `zoneHour`, `zoneMinute`, `zoneSecond`, the undefined parameters are taken equal to 0.
*
* Note that zoneHour=-0, zoneMinute=10, zoneSecond=0 is different from zoneHour=0, zoneMinute=10,
* zoneSecond=0. The first corresponds to the string 'GMT-00:10', a negative 10-minute offset, the
* second one to the string 'GMT+00:10', a positive 10-minute offset.
*
* `year`, `ordinalDay`, `month`, `monthDay`, `isoYear`, `isoWeek`, `weekDay`, `hour23`, `hour11`,
* `minute`, `second`, `millisecond`, `zoneHour`, `zoneMinute` and `zoneSecond` should be integers.
* `zoneOffset` does not need to be an integer.
*
* All parameters must be coherent. For instance, `year=1970`, `month=1`, `monthDay=1`, `weekday=0`
* `zoneHour=0`, `zoneMinute=0` and `zoneSecond=0` will trigger an error because 1/1/1970
* 00:00:00:000+0:00 is a thursday. `hour23=