Files
kjol/go/jsruntime/runtime/timedotgo/dist/Time.js

1531 lines
52 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// for time zones, see here: https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations#cite_note-18
// and see if you can scrape them to make a working parser.
/**
* Millisecond is the base duration unit.
*/
export const Millisecond = 1;
/**
* Second = 1000 * Millisecond
*/
export const Second = 1000 * Millisecond;
/**
* Minute = 60 * Second
*/
export const Minute = 60 * Second;
/**
* Hour = 60 * Minute
*/
export const Hour = 60 * Minute;
/**
* These are predefined layouts for use in Time.Format and time.Parse.
* The reference time used in these layouts is the specific time stamp:
*
* 01/02 03:04:05PM '06 -0700
*
* (January 2, 15:04:05, 2006, in time zone seven hours west of GMT).
* That value is recorded as the constant named Layout, listed below. As a
* Unix time, this is 1136239445. Since MST is GMT-0700, the reference would be
* printed by the Unix date command as:
*
* Mon Jan 2 15:04:05 MST 2006
*
* It is a regrettable historic error that the date uses the American
* convention of putting the numerical month before the day.
*
* The example for Time.Format demonstrates the working of the layout string in
* detail and is a good reference.
*
* Note that the RFC822, RFC850, and RFC1123 formats should be applied only
* to local times. Applying them to UTC times will use "UTC" as the time zone
* abbreviation, while strictly speaking those RFCs require the use of "GMT"
* in that case. When using the RFC1123 or RFC1123Z formats for parsing,
* note that these formats define a leading zero for the day-in-month portion,
* which is not strictly allowed by RFC 1123. This will result in an error
* when parsing date strings that occur in the first 9 days of a given month.
* In general RFC1123Z should be used instead of RFC1123 for servers that
* insist on that format, and RFC3339 should be preferred for new protocols.
* RFC3339, RFC822, RFC822Z, RFC1123, and RFC1123Z are useful for formatting;
* when used with time.Parse they do not accept all the time formats permitted
* by the RFCs and they do accept time formats not formally defined. The
* RFC3339Nano format removes trailing zeros from the seconds field and thus
* may not sort correctly once formatted.
*
* Most programs can use one of the defined constants as the layout passed
* to Format or Parse. The rest of this comment can be ignored unless you are
* creating a custom layout string.
*
* To define your own format, write down what the reference time would look
* like formatted your way; see the values of constants like ANSIC, StampMicro
* or Kitchen for examples. The model is to demonstrate what the reference
* time looks like so that the Format and Parse methods can apply the same
* transformation to a general time value.
*
* Here is a summary of the components of a layout string. Each element shows
* by example the formatting of an element of the reference time. Only these
* values are recognized. Text in the layout string that is not recognized as
* part of the reference time is echoed verbatim during Format and expected to
* appear verbatim in the input to Parse.
*
* Year: "2006" "06"
* Month: "Jan" "January" "01" "1"
* Day of the week: "Mon" "Monday"
* Day of the month: "2" "_2" "02"
* Day of the year: "__2" "002"
* Hour: "15" "3" "03" (PM or AM)
* Minute: "4" "04"
* Second: "5" "05"
* AM/PM mark: "PM"
*
* Numeric time zone offsets format as follows:
*
* "-0700" ±hhmm
* "-07:00" ±hh:mm
* "-07" ±hh
* "-070000" ±hhmmss
* "-07:00:00" ±hh:mm:ss
*
* Replacing the sign in the format with a Z triggers the ISO 8601 behavior of
* printing Z instead of an offset for the UTC zone. Thus:
*
* "Z0700" Z or ±hhmm
* "Z07:00" Z or ±hh:mm
* "Z07" Z or ±hh
* "Z070000" Z or ±hhmmss
* "Z07:00:00" Z or ±hh:mm:ss
*
* Within the format string, the underscores in "_2" and "__2" represent spaces
* that may be replaced by digits if the following number has multiple digits,
* for compatibility with fixed-width Unix time formats. A leading zero
* represents a zero-padded value.
*
* The formats __2 and 002 are space-padded and zero-padded three-character day
* of year; there is no unpadded day of year format.
*
* A comma or decimal point followed by one or more zeros represents a
* fractional second, printed to the given number of decimal places. A comma or
* decimal point followed by one or more nines represents a fractional second,
* printed to the given number of decimal places, with trailing zeros removed.
* For example "15:04:05,000" or "15:04:05.000" formats or parses with
* millisecond precision.
*
* Some valid layouts are invalid time values for time.Parse, due to formats
* such as _ for space padding and Z for zone information.
*/
export const Layout = "01/02 03:04:05PM '06 -0700"; // The reference time, in numerical order.
export const ANSIC = "Mon Jan _2 15:04:05 2006";
export const UnixDate = "Mon Jan _2 15:04:05 MST 2006";
export const RubyDate = "Mon Jan 02 15:04:05 -0700 2006";
export const RFC822 = "02 Jan 06 15:04 MST";
export const RFC822Z = "02 Jan 06 15:04 -0700"; // RFC822 with numeric zone
export const RFC850 = "Monday, 02-Jan-06 15:04:05 MST";
export const RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST";
export const RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700"; // RFC1123 with numeric zone
export const RFC3339 = "2006-01-02T15:04:05Z07:00";
export const RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00";
export const Kitchen = "3:04PM";
export const Stamp = "Jan _2 15:04:05";
export const StampMilli = "Jan _2 15:04:05.000";
export const StampMicro = "Jan _2 15:04:05.000000";
export const StampNano = "Jan _2 15:04:05.000000000";
export const DateTime = "2006-01-02 15:04:05";
export const DateOnly = "2006-01-02";
export const TimeOnly = "15:04:05";
/**
* Local represents the system's local time zone.
*/
export const Local = String(Intl.DateTimeFormat().resolvedOptions().timeZone);
/**
* UTC represents Universal Coordinated Time (UTC).
*/
export const UTC = "Etc/UTC";
/**
* List available locations/IANA names.
*/
export function ListAvailableIANAs() {
// @ts-ignore
return Intl.supportedValuesOf("timeZone");
}
/**
* A Month specifies a month of the year (January = 1, ...).
*/
export var Month;
(function (Month) {
Month[Month["January"] = 1] = "January";
Month[Month["February"] = 2] = "February";
Month[Month["March"] = 3] = "March";
Month[Month["April"] = 4] = "April";
Month[Month["May"] = 5] = "May";
Month[Month["June"] = 6] = "June";
Month[Month["July"] = 7] = "July";
Month[Month["August"] = 8] = "August";
Month[Month["September"] = 9] = "September";
Month[Month["October"] = 10] = "October";
Month[Month["November"] = 11] = "November";
Month[Month["December"] = 12] = "December";
})(Month || (Month = {}));
/**
* A Weekday specifies a day of the week (Sunday = 0, ...).
*/
export var Weekday;
(function (Weekday) {
Weekday[Weekday["Sunday"] = 0] = "Sunday";
Weekday[Weekday["Monday"] = 1] = "Monday";
Weekday[Weekday["Tuesday"] = 2] = "Tuesday";
Weekday[Weekday["Wednesday"] = 3] = "Wednesday";
Weekday[Weekday["Thursday"] = 4] = "Thursday";
Weekday[Weekday["Friday"] = 5] = "Friday";
Weekday[Weekday["Saturday"] = 6] = "Saturday";
})(Weekday || (Weekday = {}));
/**
* FromJSDate to convert a javascript Date object to Time.
*/
export function FromJSDate(jsDate) {
return new _Time(jsDate.getTime(), UTCLocation);
}
/**
* Now returns the current local time.
*/
export function Now() {
return new _Time(Date.now(), LocalLocation);
}
/**
* Unix returns the local Time corresponding to the given Unix time,
* sec seconds since January 1, 1970 UTC.
*/
export function Unix(seconds) {
return new _Time(seconds * 1000, LocalLocation);
}
/**
* UnixMilli returns the local Time corresponding to the given Unix time,
* msec milliseconds since January 1, 1970 UTC.
*/
export function UnixMilli(millis) {
return new _Time(millis, LocalLocation);
}
/**
* Since returns the time elapsed since t. It is shorthand for
* time.Now().Sub(t).
*/
export function Since(t) {
return Now().Sub(t);
}
/**
* Until returns the duration until t. It is shorthand for t.Sub(time.Now()).
*/
export function Until(t) {
return t.Sub(Now());
}
/**
* Replicates golangs time.Date(...) function for creating Time objects.
* note that nanoseconds is replaced with milliseconds for javascript
* and the name is DateAt (to avoid conflict with JS built-in Date).
*/
export function DateAt(year, month, day, hour, min, sec, milli, loc) {
return _DateAt(year, month, day, hour, min, sec, milli, loc);
}
/**
* ParseInLocation is like Parse but in the absence of time zone information,
* Parse interprets a time as UTC and ParseInLocation interprets the time
* as in the given location. Unlike go, no attempt is made to match an abbreviation
* inside the given timezone. Location should be a valid IANA timezone identifier.
*/
export function ParseInLocation(layout, value, location) {
return parseInternal(layout, value, location);
}
/**
* Parse parses a formatted string and returns the time value it represents.
* See the documentation for the constant called Layout to see how to represent
* the format. The second argument must be parseable using the format string
* (layout) provided as the first argument.
*/
export function Parse(layout, value) {
return parseInternal(layout, value, UTC);
}
class _Time {
#unixMilli; // always in UTC.
#location;
#displayInfo = void 0;
constructor(utcUnixMilli, loc) {
this.#unixMilli = utcUnixMilli;
this.#location = loc;
}
#getDisplayInfo() {
if (this.#displayInfo)
return this.#displayInfo;
const info = _displayInfoFor(this.#unixMilli, this.#location);
this.#displayInfo = info;
return info;
}
UnixMilli() {
// returns milliseconds.
return this.#unixMilli;
}
Add(d) {
return new _Time(this.#unixMilli + d, this.#location);
}
In(location) {
return new _Time(this.#unixMilli, new _IANALocation(location));
}
Clock() {
const { hour, minute, second } = this.#getDisplayInfo();
return { hour, minute, second };
}
Date() {
const { year, month, day } = this.#getDisplayInfo();
return { year, month, day };
}
Weekday() {
return this.#getDisplayInfo().weekdayInt;
}
YearDay() {
const { year, month, day } = this.Date();
return yearDay(year, month, day);
}
Year() {
return this.Date().year;
}
Month() {
return this.Date().month;
}
Day() {
return this.Date().day;
}
Hour() {
return this.Clock().hour;
}
Minute() {
return this.Clock().minute;
}
Second() {
return this.Clock().second;
}
Millisecond() {
return this.#getDisplayInfo().millisecond;
}
Zone() {
const { timeZoneName, timeZoneOffset } = this.#getDisplayInfo();
return { name: timeZoneName, offset: Math.trunc(timeZoneOffset / Second) };
}
UTC() {
return this.In(UTC);
}
Local() {
return this.In(Local);
}
JSDate() {
return new Date(this.#unixMilli);
}
String() {
return this.Format("2006-01-02 15:04:05.999999999 -0700 MST");
}
Unix() {
return Math.floor(this.#unixMilli / 1000);
}
After(u) {
return this.#unixMilli > u.UnixMilli();
}
Before(u) {
return this.#unixMilli < u.UnixMilli();
}
Equal(u) {
return this.#unixMilli === u.UnixMilli();
}
Format(layout) {
return formatInternal(this.#getDisplayInfo(), layout);
}
Sub(u) {
return this.#unixMilli - u.UnixMilli();
}
}
function _DateAt(year, month, day, hour, min, sec, milli, loc) {
const location = loc === UTC ? UTCLocation : new _IANALocation(loc);
return _DateAtLocation(year, month, day, hour, min, sec, milli, location);
}
function _DateAtLocation(year, month, day, hour, min, sec, milli, loc) {
const utcUnixMs = Date.UTC(year, month - 1, day, hour, min, sec, milli);
if (loc === UTCLocation)
return new _Time(utcUnixMs, UTCLocation);
// 1. Build unix millis,
// 2. create date,
// 3. adjust based on locale.
const offset1 = loc.OffsetAt(utcUnixMs);
const offset2 = loc.OffsetAt(utcUnixMs - offset1);
if (offset1 === offset2) {
return new _Time(utcUnixMs - offset1, loc);
}
return new _Time(utcUnixMs - offset2, loc);
}
class _FixedLocation {
#name;
#offset;
constructor(name, offset) {
this.#name = name;
this.#offset = offset;
}
AbbrvAt(_unixMilli) {
return this.#name;
}
OffsetAt(_unixMilli) {
return this.#offset;
}
}
class _IANALocation {
#iana;
constructor(iana) {
this.#iana = iana;
}
AbbrvAt(unixMilli) {
return _getAbbrvAt(unixMilli, this.#iana);
}
OffsetAt(unixMilli) {
return _getOffsetAt(unixMilli, this.#iana);
}
}
const LocalLocation = new _IANALocation(Local);
const UTCLocation = new _IANALocation(UTC);
/**
* _GMTOffsetToDuration: takes GMT+03:00 -> (3 * 60 * 60) * 1000 (milliseconds offset)
*/
function _GMTOffsetToDuration(offsetStr) {
if (offsetStr === "GMT") {
return 0;
}
const throwErr = () => {
throw new Error(`invalid GMT offset format '${offsetStr}'`);
};
if (offsetStr.substring(0, 3) !== "GMT")
throwErr();
const subs = offsetStr.substring(3);
let sign = 1, hours = 0, minutes = 0, seconds = 0;
const signChar = subs[0];
if (signChar === "+") {
sign = 1;
}
else if (signChar === "-") {
sign = -1;
}
else {
throwErr();
}
let i = 1;
const [hrs, hrsL] = getnum(subs.substring(i), true);
i += hrsL;
hours = hrs;
if (subs[i] === ":") {
i++;
const [mins, minL] = getnum(subs.substring(i), true);
minutes = mins;
i += minL;
}
if (subs[i] == ":") {
i++;
const [secs, secL] = getnum(subs.substring(i), true);
seconds = secs;
i += secL;
}
return sign * (hours * Hour + minutes * Minute + seconds * Second);
}
function _getOffsetAt(unixMilli, iana) {
const intl = new Intl.DateTimeFormat("en-US", {
timeZone: iana,
timeZoneName: "longOffset",
}).formatToParts(new Date(unixMilli));
for (const item of intl) {
if (item.type === "timeZoneName")
return _GMTOffsetToDuration(item.value);
}
throw new Error(`offset not found for: ${iana}`);
}
function _getAbbrvAt(unixMilli, iana) {
const intl = new Intl.DateTimeFormat("en-US", {
timeZone: iana,
timeZoneName: "short",
}).formatToParts(new Date(unixMilli));
for (const item of intl) {
if (item.type === "timeZoneName")
return item.value;
}
throw new Error(`time zone abbreviation not found for: ${iana}`);
}
// get the display information for a unix time + location.
// we use the location to get the offset information.
// then shift the date and get the display info (like year, month, day, hour, min, second, weekday, etcetera) by pretending that we are in UTC.
function _displayInfoFor(unixMilli, loc) {
const abbrv = loc.AbbrvAt(unixMilli);
const offset = loc.OffsetAt(unixMilli);
const fakeUTCUnix = unixMilli + offset;
const fakeDate = new Date(fakeUTCUnix);
return {
year: fakeDate.getUTCFullYear(),
month: fakeDate.getUTCMonth() + 1,
weekday: longDayNames[fakeDate.getUTCDay()],
weekdayInt: fakeDate.getUTCDay(),
day: fakeDate.getUTCDate(),
hour: fakeDate.getUTCHours(),
minute: fakeDate.getUTCMinutes(),
second: fakeDate.getUTCSeconds(),
millisecond: fakeDate.getUTCMilliseconds(),
timeZoneName: abbrv,
timeZoneOffset: offset,
};
}
// function formatInternal(t: _Time, layout: string): string {
function formatInternal(info, layout) {
const out = [];
const parts = parseLayout(layout, false);
const { year, month, day, weekday, hour, minute, second, millisecond, timeZoneName, timeZoneOffset, } = info;
const zoneOffset = splitDuration(timeZoneOffset);
const monthIndex = month - 1;
for (const p of parts) {
if (typeof p === "string") {
out.push(p);
continue;
}
switch (65535 /* std.MaskLower */ & p) {
case 0 /* std.LongMonth */:
out.push(longMonthNames[monthIndex]);
break;
case 1 /* std.Month */:
out.push(shortMonthNames[monthIndex]);
break;
case 2 /* std.NumMonth */:
out.push(String(month));
break;
case 3 /* std.ZeroMonth */:
out.push(String(month).padStart(2, "0"));
break;
case 4 /* std.LongWeekDay */:
out.push(weekday);
break;
case 5 /* std.WeekDay */:
out.push(weekday.substr(0, 3));
break;
case 6 /* std.Day */:
out.push(String(day));
break;
case 7 /* std.UnderDay */:
out.push(String(day).padStart(2, " "));
break;
case 8 /* std.ZeroDay */:
out.push(String(day).padStart(2, "0"));
break;
case 9 /* std.UnderYearDay */:
out.push(String(yearDay(year, month, day)).padStart(3, " "));
break;
case 10 /* std.ZeroYearDay */:
out.push(String(yearDay(year, month, day)).padStart(3, "0"));
break;
case 11 /* std.Hour */:
out.push(String(hour).padStart(2, "0"));
break;
case 12 /* std.Hour12 */:
out.push(String(hour % 12 || 12));
break;
case 13 /* std.ZeroHour12 */:
out.push(String(hour % 12 || 12).padStart(2, "0"));
break;
case 14 /* std.Minute */:
out.push(String(minute));
break;
case 15 /* std.ZeroMinute */:
out.push(String(minute).padStart(2, "0"));
break;
case 16 /* std.Second */:
out.push(String(second));
break;
case 17 /* std.ZeroSecond */:
out.push(String(second).padStart(2, "0"));
break;
case 18 /* std.LongYear */:
out.push(String(year));
break;
case 19 /* std.Year */:
out.push(String(year).substring(2));
break;
case 20 /* std.PM */:
out.push(hour >= 12 ? "PM" : "AM");
break;
case 21 /* std.pm */:
out.push(hour >= 12 ? "pm" : "am");
break;
case 22 /* std.TZ */:
if (timeZoneName.length > 0) {
out.push(timeZoneName);
}
else {
// No time zone known for this time, but we must print one.
// Use the -0700 format.
out.push(zoneOffset.sign < 0 ? "-" : "+");
out.push(String(zoneOffset.hours).padStart(2, "0"));
out.push(String(zoneOffset.minutes).padStart(2, "0"));
}
break;
case 23 /* std.ISO8601TZ */:
if (timeZoneOffset === 0) {
out.push("Z");
break;
}
// fallthrough
case 28 /* std.NumTZ */:
out.push(zoneOffset.sign < 0 ? "-" : "+");
out.push(String(zoneOffset.hours).padStart(2, "0"));
out.push(String(zoneOffset.minutes).padStart(2, "0"));
break;
case 24 /* std.ISO8601SecondsTZ */:
if (timeZoneOffset === 0) {
out.push("Z");
break;
}
// fallthrough
case 29 /* std.NumSecondsTZ */:
out.push(zoneOffset.sign < 0 ? "-" : "+");
out.push(String(zoneOffset.hours).padStart(2, "0"));
out.push(String(zoneOffset.minutes).padStart(2, "0"));
out.push(String(zoneOffset.seconds).padStart(2, "0"));
break;
case 25 /* std.ISO8601ShortTZ */:
if (timeZoneOffset === 0) {
out.push("Z");
break;
}
// fallthrough
case 30 /* std.NumShortTZ */:
out.push(zoneOffset.sign < 0 ? "-" : "+");
out.push(String(zoneOffset.hours).padStart(2, "0"));
break;
case 26 /* std.ISO8601ColonTZ */:
if (timeZoneOffset === 0) {
out.push("Z");
break;
}
// fallthrough
case 31 /* std.NumColonTZ */:
out.push(zoneOffset.sign < 0 ? "-" : "+");
out.push(String(zoneOffset.hours).padStart(2, "0"));
out.push(":");
out.push(String(zoneOffset.minutes).padStart(2, "0"));
break;
case 27 /* std.ISO8601ColonSecondsTZ */:
if (timeZoneOffset === 0) {
out.push("Z");
break;
}
//fallthroguh
case 32 /* std.NumColonSecondsTZ */:
out.push(zoneOffset.sign < 0 ? "-" : "+");
out.push(String(zoneOffset.hours).padStart(2, "0"));
out.push(":");
out.push(String(zoneOffset.minutes).padStart(2, "0"));
out.push(":");
out.push(String(zoneOffset.seconds).padStart(2, "0"));
break;
case 33 /* std.FracSecond0 */:
out.push(fracSecondsStr(p, millisecond));
break;
case 34 /* std.FracSecond9 */:
const fracS = fracSecondsStr(p, millisecond);
if (fracS.length === 0) {
out.pop(); // remove the comma/period.
break;
}
out.push(fracS);
break;
default:
console.warn("UNHANDLED", p);
break;
}
}
return out.join("");
}
function parseInternal(layout, value, defaultLocation) {
const parts = parseLayout(layout, true);
let year = 0, month = -1, day = -1, yearday = -1, hour = 0, minute = 0, second = 0, milli = 0, pmSet = false, amSet = false, tzString = "", zone = null, zoneOffsetHr = 0, zoneOffsetMin = 0, zoneOffsetSec = 0, zoneOffsetSign = 1;
let vi = 0;
for (let partIndex = 0; partIndex < parts.length; partIndex++) {
const p = parts[partIndex];
if (typeof p === "string") {
if (p === " ") {
while (value[vi] === " ")
vi++;
}
else {
vi += p.length;
}
continue;
}
const mP = p & 65535 /* std.MaskLower */;
// console.log(std[mP], `'${value.substring(vi)}'`, vi);
switch (mP) {
case 1 /* std.Month */:
// fallthrough
case 0 /* std.LongMonth */:
const [monthS, monthIndex] = (mP === 1 /* std.Month */ ? getShortMonthPrefix : getLongMonthPrefix)(value.substring(vi));
month = monthIndex + 1;
vi += monthS.length;
break;
case 2 /* std.NumMonth */:
// fallthrough
case 3 /* std.ZeroMonth */:
const [nm, nml] = getnum(value.substring(vi), mP === 3 /* std.ZeroMonth */);
if (nm <= 0 || nm > 12)
throw new Error(`invalid month ${nm}: ${value}`);
month = nm;
vi += nml;
break;
case 4 /* std.LongWeekDay */:
vi += getLongDayPrefix(value.substring(vi))[0].length;
break;
case 5 /* std.WeekDay */:
vi += getShortDayPrefix(value.substring(vi))[0].length;
break;
case 6 /* std.Day */:
// fallthrough
case 7 /* std.UnderDay */:
if (value[vi] === " ")
vi++;
// fallthrough
case 8 /* std.ZeroDay */:
const [dayVal, dayLength] = getnum(value.substring(vi), mP === 8 /* std.ZeroDay */);
day = dayVal;
vi += dayLength;
break;
case 9 /* std.UnderYearDay */:
case 10 /* std.ZeroYearDay */: {
for (let ydi = 0; ydi < 2; ydi++) {
if (mP === 9 /* std.UnderYearDay */ && vi < value.length && value[0] == " ") {
vi++;
}
}
const [yval, ylen] = getnum3(value.substring(vi), mP === 10 /* std.ZeroYearDay */);
vi += ylen;
yearday = yval;
break;
}
case 11 /* std.Hour */: {
const [hourNum, hourNumL] = getnum(value.substring(vi), false);
if (hourNum < 0 || 24 <= hourNum)
throw new Error(`invalid hour ${hourNum}`);
hour = hourNum;
vi += hourNumL;
break;
}
case 12 /* std.Hour12 */:
// fallthrough
case 13 /* std.ZeroHour12 */: {
const [hourNum, hourNumL] = getnum(value.substring(vi), mP == 13 /* std.ZeroHour12 */);
if (hourNum < 0 || 12 < hourNum)
throw new Error(`invalid hour12: ${hourNum}`);
hour = hourNum;
vi += hourNumL;
break;
}
case 14 /* std.Minute */:
// fallthrough
case 15 /* std.ZeroMinute */:
const [minNum, minNumL] = getnum(value.substring(vi), mP === 15 /* std.ZeroMinute */);
if (minNum < 0 || minNum > 60)
throw new Error(`invalid minute: ${minNum}`);
minute = minNum;
vi += minNumL;
break;
case 16 /* std.Second */:
case 17 /* std.ZeroSecond */: {
const [secNum, setNumL] = getnum(value.substring(vi), mP === 17 /* std.ZeroSecond */);
if (secNum < 0 || secNum > 60)
throw new Error(`invalid second: ${secNum}`);
second = secNum;
vi += setNumL;
// Special case: do we have a fractional second but no
// fractional second in the format?
const subv = value.substring(vi);
if (subv.length >= 2 && isCommaOrPeriod(subv[0]) && isDigit(subv[1])) {
const nextP = parts[partIndex + 1];
const nextMP = typeof nextP === "string" ? -1 : nextP & 65535 /* std.MaskLower */;
if (nextMP === 33 /* std.FracSecond0 */ || nextMP == 34 /* std.FracSecond9 */) {
// Fractional second in the layout; proceed normally
break;
}
// No fractional second in the layout but we have one in the input.
let n = 2;
while (n < subv.length && isDigit(subv[n])) {
n++;
}
const [millisV, millisL] = parseFracSeconds(subv.substring(0, n));
milli = millisV;
vi += millisL;
}
break;
}
case 18 /* std.LongYear */:
// fallthrough
case 19 /* std.Year */:
const yearS = value.substring(vi, vi + (mP === 19 /* std.Year */ ? 2 : 4));
throwInvalidNumber(yearS, `year in '${value}'`);
year = Number(yearS);
if (mP === 19 /* std.Year */) {
if (year >= 69) {
year += 1900;
}
else {
year += 2000;
}
}
vi += yearS.length;
break;
case 20 /* std.PM */: {
const pmS = value.substring(vi, vi + 2);
if (pmS === "PM") {
pmSet = true;
}
else if (pmS == "AM") {
amSet = true;
}
else {
throw new Error(`invalid AM/PM: ${pmS}`);
}
vi += pmS.length;
break;
}
case 21 /* std.pm */: {
const pmS = value.substring(vi, vi + 2);
if (pmS === "pm") {
pmSet = true;
}
else if (pmS == "am") {
amSet = true;
}
else {
throw new Error(`invalid AM/PM: ${pmS}`);
}
vi += pmS.length;
break;
}
case 22 /* std.TZ */:
tzString = parseTimeZoneStr(value.substring(vi));
vi += tzString.length;
if (tzString === "UTC")
zone = UTC;
break;
case 23 /* std.ISO8601TZ */:
case 25 /* std.ISO8601ShortTZ */:
case 26 /* std.ISO8601ColonTZ */:
case 24 /* std.ISO8601SecondsTZ */:
case 27 /* std.ISO8601ColonSecondsTZ */:
if (value[vi] === "Z") {
zone = UTC;
vi++;
break;
}
case 28 /* std.NumTZ */:
case 30 /* std.NumShortTZ */:
case 31 /* std.NumColonTZ */:
case 29 /* std.NumSecondsTZ */:
case 32 /* std.NumColonSecondsTZ */:
let zsign = "", zhour = "", zmin = "", zsec = "";
const subv = value.substring(vi);
const errTooShort = () => {
throw new Error(`tz too short: '${subv}'`);
};
if (mP === 26 /* std.ISO8601ColonTZ */ || mP === 31 /* std.NumColonTZ */) {
if (subv.length < 6)
errTooShort();
if (subv[3] !== ":")
throw new Error(`tz missing colon: ${subv[3]} (${subv} -- ${value})`);
zsign = subv[0];
zhour = subv.substring(1, 3);
zmin = subv.substring(4, 6);
zsec = "00";
vi += 6;
}
else if (mP === 30 /* std.NumShortTZ */ || mP == 25 /* std.ISO8601ShortTZ */) {
if (subv.length < 3)
errTooShort();
zsign = subv[0];
zhour = subv.substring(1, 3);
zmin = "00";
zsec = "00";
vi += 3;
}
else if (mP === 27 /* std.ISO8601ColonSecondsTZ */ ||
mP == 32 /* std.NumColonSecondsTZ */) {
if (subv.length < 9)
errTooShort();
if (subv[3] !== ":" || subv[6] !== ":")
throw new Error(`tz missing colo: ${subv[3]} ${subv[6]}`);
zsign = subv[0];
zhour = subv.substring(1, 3);
zmin = subv.substring(4, 6);
zsec = subv.substring(7, 9);
vi += 9;
}
else if (mP === 24 /* std.ISO8601SecondsTZ */ || mP === 29 /* std.NumSecondsTZ */) {
if (subv.length < 7)
errTooShort();
zsign = subv[0];
zhour = subv.substring(1, 3);
zmin = subv.substring(3, 5);
zsec = subv.substring(5, 7);
vi += 7;
}
else {
if (subv.length < 5)
errTooShort();
zsign = subv[0];
zhour = subv.substring(1, 3);
zmin = subv.substring(3, 5);
zsec = "00";
vi += 5;
}
const zhr = getnum(zhour, true)[0];
const zmm = getnum(zmin, true)[0];
const zss = getnum(zsec, true)[0];
if (zhr > 24)
throw new Error(`invalid offset hour: ${zhr}`);
if (zmm > 60)
throw new Error(`invalid offset minute: ${zmm}`);
if (zss > 60)
throw new Error(`invalid offset second: ${zss}`);
zoneOffsetHr = zhr;
zoneOffsetMin = zmm;
zoneOffsetSec = zss;
if (zsign === "+") {
zoneOffsetSign = 1;
}
else if (zsign === "-") {
zoneOffsetSign = -1;
}
else {
throw new Error(`invalid zone sign: '${zsign}'`);
}
break;
case 34 /* std.FracSecond9 */: {
const subv = value.substring(vi);
if (subv.length < 2)
break;
if (!(isCommaOrPeriod(subv[0]) && isDigit(subv[1])))
break;
let endIndex = 1;
while (endIndex < subv.length && isDigit(subv[endIndex])) {
endIndex++;
}
const [millisV, millisL] = parseFracSeconds(subv.substring(0, endIndex));
vi += millisL;
milli = millisV;
break;
}
case 33 /* std.FracSecond0 */: {
const numDigits = (p >> 16) & 0xfff;
const subv = value.substring(vi);
// exact num digits.
if (subv.length < numDigits + 1) {
throw new Error(`not enough digits ${subv}`);
}
const [millisV, millisL] = parseFracSeconds(subv.substring(0, numDigits + 1));
vi += millisL;
milli = millisV;
break;
}
default:
console.warn(`unhandled`, p);
break;
}
}
if (pmSet && hour < 12) {
hour += 12;
}
else if (amSet && hour == 12) {
hour = 0;
}
if (yearday >= 0) {
// console.log({ yearday });
let monthTemp = 0, dayTemp = 0;
if (isLeap(year)) {
if (yearday === 31 + 29) {
monthTemp = Month.February;
dayTemp = 29;
}
else if (yearday > 31 + 29) {
yearday--;
}
}
if (yearday < 1 || yearday > 365) {
throw new Error(`invalid year day: ${yearday}`);
}
if (monthTemp === 0) {
monthTemp = Math.trunc((yearday - 1) / 31) + 1;
if (daysBefore(monthTemp + 1) < yearday) {
monthTemp++;
}
dayTemp = yearday - daysBefore(monthTemp);
}
if (month >= 0 && month !== monthTemp) {
throw new Error(`day-of-year does not match month`);
}
month = monthTemp;
if (day >= 0 && day !== dayTemp) {
throw new Error(`day-of-year does not match day`);
}
day = dayTemp;
}
else {
if (month < 0)
month = Month.January;
if (day < 0)
day = 1;
}
// console.log({ year, month, day, hour, minute, second, milli });
if (day < 1 || day > daysIn(month, year)) {
throw new Error(`day ${day} out of range for month+year`);
}
if (zone !== null) {
// handles UTC
return _DateAt(year, month, day, hour, minute, second, milli, zone);
}
if (zoneOffsetHr !== 0 || zoneOffsetMin !== 0 || zoneOffsetSec !== 0) {
const offsetSec = zoneOffsetSign * (zoneOffsetHr * 60 + zoneOffsetMin) * 60 + zoneOffsetSec;
const loc = new _FixedLocation(tzString, offsetSec * Second);
return _DateAtLocation(year, month, day, hour, minute, second, milli, loc);
}
if (tzString.length > 0) {
const t = _DateAtLocation(year, month, day, hour, minute, second, milli, UTCLocation);
let offset = 0;
if (tzString.length > 3 && tzString.substring(0, 3) === "GMT") {
offset = throwInvalidNumber(tzString.substring(3), tzString); // like GMT-8, GMT+8
}
const loc = new _FixedLocation(tzString, offset * Hour);
return new _Time(t.UnixMilli(), loc);
}
return _DateAt(year, month, day, hour, minute, second, milli, defaultLocation);
}
function daysBefore(m) {
let adj = 0;
if (m >= Month.March) {
adj = -2;
}
// With the -2 adjustment after February,
// we need to compute the running sum of:
// 0 31 30 31 30 31 30 31 31 30 31 30 31
// which is:
// 0 31 61 92 122 153 183 214 245 275 306 336 367
// This is almost exactly 367/12×(m-1) except for the
// occasonal off-by-one suggesting there may be an
// integer approximation of the form (a×m + b)/c.
// A brute force search over small a, b, c finds that
// (214×m - 211) / 7 computes the function perfectly.
// return (214*int(m)-211)/7 + adj
return Math.trunc((214 * m - 211) / 7) + adj;
}
const longDayNames = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
const shortDayNames = [
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
];
const shortMonthNames = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
const longMonthNames = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
function getnum3(s, fixed) {
let n = 0, i = 0;
while (i < 3 && isDigit(s[i])) {
n = n * 10 + throwInvalidNumber(s[i], "year day");
i++;
}
if (i === 0 || (fixed && i !== 3)) {
throw new Error(`not enough digits: '${s}' ${fixed}`);
}
return [n, i];
}
// get 2 digit num
function getnum(s, fixed) {
if (!isDigit(s[0])) {
throw new Error(`not a digit: '${s[0]}' (${s})`);
}
if (!isDigit(s[1])) {
if (fixed) {
throw new Error(`not a digit: '${s[1]}' (${s})`);
}
return [Number(s[0]), 1];
}
return [Number(s.substring(0, 2)), 2];
}
function _findPrefix(s, opts, typeName) {
for (let i = 0; i < opts.length; i++) {
const opt = opts[i];
if (s.toLowerCase().startsWith(opt.toLowerCase())) {
return [opt, i];
}
}
throw new Error(`invalid ${typeName}: '${s}'`);
}
function getShortDayPrefix(s) {
return _findPrefix(s, shortDayNames, "short day name");
}
function getLongDayPrefix(s) {
return _findPrefix(s, longDayNames, "long day name");
}
function getShortMonthPrefix(s) {
return _findPrefix(s, shortMonthNames, "short month name");
}
function getLongMonthPrefix(s) {
return _findPrefix(s, longMonthNames, "long month name");
}
function parseSignedOffset(s) {
const sign = s[0];
if (sign !== "+" && sign !== "-") {
throw new Error(`bad signed tz offset ${s}`);
}
let i = 1;
while (i < s.length && isDigit(s[i])) {
i++;
}
return s.substring(0, i);
}
function parseTimeZoneStr(s) {
const _doThrow = () => {
throw new Error(`invalid time zone: '${s}'`);
};
if (s.length < 3) {
_doThrow();
}
const first3 = s.substring(0, 3);
if (first3 === "UTC") {
return first3;
}
// Special case 1: ChST and MeST are the only zones with a lower-case letter.
const first4 = s.substring(0, 4);
if (first4.length === 4 && (first4 === "ChST" || first4 === "MeST")) {
return first4;
}
// Special case 2: GMT may have an hour offset; treat it specially.
if (first3 === "GMT") {
if (first4[3] === "+" || first4[3] === "-") {
return first3 + parseSignedOffset(s.substring(3));
}
return first3;
}
if (s[0] === "+" || s[1] === "-") {
return parseSignedOffset(s);
}
let nUpper = 0;
while (nUpper < 6) {
if (nUpper >= s.length) {
break;
}
if (!isUppercase(s[nUpper])) {
break;
}
nUpper++;
}
switch (nUpper) {
case 0:
case 1:
case 2:
case 6:
_doThrow();
break;
case 5:
if (s[4] === "T") {
return s.substring(0, 5);
}
break;
case 4:
// Must end in T, except one special case.
if (first4[3] === "T" || first4 === "WITA") {
return first4;
}
break;
case 3:
return first3;
}
_doThrow();
return "";
}
function parseFracSeconds(s) {
if (!isCommaOrPeriod(s[0])) {
throw new Error(`must start with comma or period '${s}'`);
}
let n = s.length;
if (n > 10)
n = 10;
const numS = s.substring(1, n);
const value = throwInvalidNumber(numS, "fractional seconds");
if (value < 0) {
throw new Error(`cannot be negative ${s}`);
}
let nano = value;
for (let i = 0; i < 10 - n; i++) {
nano *= 10;
}
const MILLI_TO_NANO = 1000000;
const NANO_TO_MILLI = 1 / MILLI_TO_NANO;
const millis = Math.round(nano * NANO_TO_MILLI);
return [millis, n];
}
function fracSecondsStr(code, millis) {
const count = (code >> 16) & 0xfff;
let padded = String(Math.floor(millis)).padStart(3, "0");
if (count < padded.length) {
padded = padded.substring(0, count);
}
switch (65535 /* std.MaskLower */ & code) {
case 33 /* std.FracSecond0 */: // trailing zeroes included.
return padded.padEnd(count, "0");
case 34 /* std.FracSecond9 */: // trailing zeroes omitted
return trimTrailingZeroes(padded);
default:
throw new Error(`invalid code for fractional seconds ${code}`);
}
}
function trimTrailingZeroes(s) {
const parts = [...s];
let lastZero = s.length - 1;
while (lastZero >= 0) {
if (parts[lastZero] !== "0")
break;
lastZero--;
}
return parts.slice(0, lastZero + 1).join("");
}
const std0x = {
"1": 3 /* std.ZeroMonth */,
"2": 8 /* std.ZeroDay */,
"3": 13 /* std.ZeroHour12 */,
"4": 15 /* std.ZeroMinute */,
"5": 17 /* std.ZeroSecond */,
"6": 19 /* std.Year */,
};
function charsMatch(chars, startIndex, matchS) {
const matchPoints = [...matchS];
for (let i = 0; i < matchPoints.length; i++) {
const c = chars[i + startIndex];
if (!c)
return false;
const mC = matchPoints[i];
if (mC !== c)
return false;
}
return true;
}
// const layoutCache: Record<string, (std | string)[]> = {};
// here is a native date method to get local time offset vs. UTC.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset
// internal function
function parseLayout(layout, forTimeParse) {
// const cached = layoutCache[layout];
// if (cached) return cached;
// throw new GoTimeError(ErrorCode.ParseError, `bullshit layout: ${layout}`);
const parts = [];
const chars = [...layout];
let i = 0;
while (i < chars.length) {
const c = chars[i];
const nC = chars[i + 1];
switch (c) {
case "J": // January, Jan
if (charsMatch(chars, i, "January")) {
if (isLowercase(chars[i + 7])) {
break;
}
parts.push(0 /* std.LongMonth */);
i += 7;
continue;
}
if (charsMatch(chars, i, "Jan")) {
if (isLowercase(chars[i + 3])) {
break;
}
parts.push(1 /* std.Month */);
i += 3;
continue;
}
break;
case "M": // Monday, Mon, MST
if (charsMatch(chars, i, "Monday")) {
parts.push(4 /* std.LongWeekDay */);
i += 6;
continue;
}
if (charsMatch(chars, i, "Mon")) {
if (isLowercase(chars[i + 3])) {
break;
}
parts.push(5 /* std.WeekDay */);
i += 3;
continue;
}
if (charsMatch(chars, i, "MST")) {
parts.push(22 /* std.TZ */);
i += 3;
continue;
}
break;
case "0": // 01, 02, 03, 04, 05, 06, 002
if (charsMatch(chars, i, "002")) {
parts.push(10 /* std.ZeroYearDay */);
i += 3;
continue;
}
if (nC === void 0)
break;
const m = std0x[nC];
if (m === void 0)
break;
parts.push(m);
i += 2;
continue;
case "1": // 15, 1
if (charsMatch(chars, i, "15")) {
parts.push(11 /* std.Hour */);
i += 2;
continue;
}
parts.push(2 /* std.NumMonth */);
i++;
continue;
case "2": // 2006, 2
if (charsMatch(chars, i, "2006")) {
parts.push(18 /* std.LongYear */);
i += 4;
continue;
}
parts.push(6 /* std.Day */);
i++;
continue;
case "_": // _2, _2006, __2
if (charsMatch(chars, i, "_2006")) {
// _2006 is really a literal _, followed by stdLongYear
parts.push("_");
parts.push(18 /* std.LongYear */);
i += 5;
continue;
}
if (charsMatch(chars, i, "__2")) {
parts.push(9 /* std.UnderYearDay */);
i += 3;
continue;
}
if (charsMatch(chars, i, "_2")) {
parts.push(7 /* std.UnderDay */);
i += 2;
continue;
}
break;
case "3":
parts.push(12 /* std.Hour12 */);
i++;
continue;
case "4":
parts.push(14 /* std.Minute */);
i++;
continue;
case "5":
parts.push(16 /* std.Second */);
i++;
continue;
case "P": // PM
if (charsMatch(chars, i, "PM")) {
parts.push(20 /* std.PM */);
i += 2;
continue;
}
break;
case "p": // pm
if (charsMatch(chars, i, "pm")) {
parts.push(21 /* std.pm */);
i += 2;
continue;
}
break;
case "-": // -070000, -07:00:00, -0700, -07:00, -07
if (charsMatch(chars, i, "-070000")) {
parts.push(29 /* std.NumSecondsTZ */);
i += 7;
continue;
}
if (charsMatch(chars, i, "-07:00:00")) {
parts.push(32 /* std.NumColonSecondsTZ */);
i += 9;
continue;
}
if (charsMatch(chars, i, "-0700")) {
parts.push(28 /* std.NumTZ */);
i += 5;
continue;
}
if (charsMatch(chars, i, "-07:00")) {
parts.push(31 /* std.NumColonTZ */);
i += 6;
continue;
}
if (charsMatch(chars, i, "-07")) {
parts.push(30 /* std.NumShortTZ */);
i += 3;
continue;
}
break;
case "Z": // Z070000, Z07:00:00, Z0700, Z07:00,
if (charsMatch(chars, i, "Z070000")) {
parts.push(24 /* std.ISO8601SecondsTZ */);
i += 7;
continue;
}
if (charsMatch(chars, i, "Z07:00:00")) {
parts.push(27 /* std.ISO8601ColonSecondsTZ */);
i += 9;
continue;
}
if (charsMatch(chars, i, "Z0700")) {
parts.push(23 /* std.ISO8601TZ */);
i += 5;
continue;
}
if (charsMatch(chars, i, "Z07:00")) {
parts.push(26 /* std.ISO8601ColonTZ */);
i += 6;
continue;
}
if (charsMatch(chars, i, "Z07")) {
parts.push(25 /* std.ISO8601ShortTZ */);
i += 3;
continue;
}
break;
case ".": // // ,000, or .000, or ,999, or .999 - repeated digits for fractional seconds.
/* fallthrough */
case ",":
if (nC === void 0)
break;
if (nC !== "9" && nC !== "0")
break;
let j = 1;
while (i + j < chars.length && chars[i + j] === nC) {
j++;
}
if (isDigit(chars[i + j]))
break; // must not end with another digit.
if (!forTimeParse)
parts.push(c); // push the comma or whatever.
let stdCode = nC === "0" ? 33 /* std.FracSecond0 */ : 34 /* std.FracSecond9 */;
stdCode |= ((j - 1) & 0xfff) << 16;
parts.push(stdCode);
i += j;
continue;
default:
break;
}
parts.push(c);
i++;
}
return parts;
}
function isDigit(c) {
if (c === void 0)
return false;
return c >= "0" && c <= "9";
}
function isLowercase(c) {
if (c === void 0)
return false;
return c >= "a" && c <= "z";
}
function isCommaOrPeriod(c) {
return c === "." || c === ",";
}
function isUppercase(c) {
if (c === void 0)
return false;
return c >= "A" && c <= "Z";
}
function isLeap(year) {
// year%4 == 0 && (year%100 != 0 || year%400 == 0)
// Bottom 2 bits must be clear.
// For multiples of 25, bottom 4 bits must be clear.
// Thanks to Cassio Neri for this trick.
let mask = 0xf;
if (year % 25 !== 0) {
mask = 3;
}
return (year & mask) === 0;
}
function daysIn(m, year) {
if (m === Month.February) {
return isLeap(year) ? 29 : 28;
}
// 30 + ((m + (m >> 3)) & 1)
return 30 + ((m + (m >> 3)) & 1);
}
function yearDay(year, month, day) {
let yDay = 0;
let m = Month.January;
while (m < month) {
yDay += daysIn(m, year);
m++;
}
yDay += day;
return yDay;
}
function isValidNumber(str) {
if (str.trim() === "")
return false; // reject empty or whitespace strings
return !isNaN(Number(str));
}
function throwInvalidNumber(str, name) {
if (!isValidNumber(str))
throw new Error(`'${str}' not a valid number for ${name}`);
return Number(str);
}
function splitDuration(ms) {
const sign = ms < 0 ? -1 : 1;
ms = Math.abs(ms);
const hours = Math.trunc(ms / Hour);
ms %= Hour;
const minutes = Math.trunc(ms / Minute);
ms %= Minute;
const seconds = Math.trunc(ms / Second);
ms %= Second;
return {
sign,
hours,
minutes,
seconds,
milliseconds: ms,
};
}
//# sourceMappingURL=Time.js.map