npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@tubular/time

v3.9.1

Published

Date/time, IANA timezones, leap seconds, TAI/UTC conversions, calendar with settable Julian/Gregorian switchover

Downloads

609

Readme

@tubular/time

Not all days are 24 hours. Some are 23 hours, or 25, or even 23.5 or 24.5 or 47 hours. Some minutes are 61 seconds long. How about a Thursday followed directly by a Saturday, giving Friday the slip? Or a September only 19 days long? This is a date/time library for handling both day-to-day situations (so to speak) and some weird ones too.

npm Build Status Coverage Status npm downloads npm bundle size (scoped) license

Key features

  • Mutable and immutable DateTime objects supporting the Gregorian and Julian calendar systems, with settable crossover.
  • IANA timezone support, with features beyond formatting using timezones, such as parsing, accessible listings of all available timezones (single-array list, grouped by UTC offset, or grouped by region), and live updates of timezone definitions.
  • Supports leap seconds and conversions between TAI (International Atomic Time) and UTC (Universal Coordinated Time).
  • Supports and recognizes negative Daylight Saving Time.
  • Extensive date/time manipulation and calculation capabilities.
  • Many features available using a familiar Moment.js-style API.
  • Astronomical time conversions among TDT (Terrestrial Dynamic Time), UT1, UTC and TAI.
  • Local mean time, by geographic longitude, to one minute (of time) resolution.
  • Astronomical time conversions among TDT (Terrestrial Dynamic Time), UT1, UTC and TAI, as well as local mean time, by geographic longitude, to one minute (of time) resolution.
  • Internationalization via JavaScript’s Intl Internationalization API, with additional built-in i18n support for issues not covered by Intl, and US-English fallback for environments without Intl support.
  • Package suitable for tree shaking and Angular optimization.
  • Full TypeScript typing support.

@tubular/time is a collection of date and time classes and functions, providing extensive internationalized date/time parsing and formatting capabilities, date/time manipulations such as field-specific add/subtract, set, and roll; calendar computations; support for live-updatable IANA time zones; and a settable Julian/Gregorian calendar switchover date.

This library was originally developed for an astronomy website, https://skyviewcafe.com, and has some features of particular interest for astronomy and historical events, but has been expanded to provide many features similar to the now-legacy-status Moment.js.

Unlike Moment.js, IANA timezone handling is built in, not a separate module, with a compact set of timezone definitions that reach roughly five years into the past and five years into the future, expanded into the past and future using Daylight Saving Time rules and/or values extracted from Intl.DateTimeFormat. Unlike the Intl API, the full list of available timezones is exposed, facilitating the creation of timezone selection interfaces.

Two alternate large timezone definition sets, of approximately 280K each, are available, each serving slightly different purposes. These definitions can be bundled at compile time, or loaded dynamically at run time. You can also download live updates when the IANA Time Zone Database is updated.

Installation

Via npm

npm install @tubular/time

import { ttime, DateTime, Timezone...} from '@tubular/time'; // ESM

...or...

const { ttime, DateTime, Timezone...} = require('@tubular/time/cjs'); // CommonJS

Documentation examples will assume @tubular/time has been imported as above.

Via <script> tag

To remotely download the full code as an ES module:

<script type="module">
  import('https://unpkg.com/@tubular/time/dist/fesm2015/index.mjs').then(pkg => {
    const { ttime, DateTime, Timezone} = pkg;

    // ...
  });
</script>

For the old-fashioned UMD approach (which can save you from about 560K of extra data):

<script src="https://unpkg.com/@tubular/time/dist/data/timezone-large-alt.js"></script>
<script src="https://unpkg.com/@tubular/time/dist/umd/index.js"></script>

(Or .../data/timezone-large.js")

The script element just above the index.js URL is an example of optionally loading extended timezone definitions. Such a script element, if used, should precede the index.js script.

The @tubular/time package will be available via the global variable tbTime. tbTime.ttime is the default function, and other functions, classes, and constants will also be available on this variable, such as tbTime.DateTime, tbTime.julianDay, tbTime.TIME_MS, etc.

Basic usage

While there are a wide range of functions and classes available from @tubular/time, the workhorse is the ttime() function, which produces immutable instances of the DateTime class.

function ttime(initialTime?: number | string | DateAndTime | Date | number[] | null, format?: string, locale?: string | string[]): DateTime

Creating immutable DateTime instances with ttime()

DateTime instances can be created in many ways. The simplest way is to create a current-time instance, done by passing no arguments at all. Dates and times can also be expressed as strings, objects, and arrays of numbers.

| | | .toString() | |---------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ttime() | Current time. | DateTime<2021‑01‑28T03:29:12.040 ‑05:00> | | ttime('1969‑07‑12T20:17')ttime('1969‑07‑12T20:17Z')ttime('20210704T0945-03')ttime('2021‑W04‑4') | DateTime from an ISO-8601 date/time string.A trailing Z causes the time to be parsed as UTC. Without it, your default timezone is assumed. | DateTime<1969‑07‑12T20:17:00.000 ‑04:00§>DateTime<1969-07-12T20:17:00.000 +00:00>DateTime<2021-07-04T09:45:00.000 -03:00>DateTime<2021-01-28T00:00:00.000 -05:00> | | ttime('2021-w05-5') | DateTime from an ISO-8601-like date/time variant (lowercase w instead of uppercase W) for locale-based week numbering. | DateTime<2021-01-28T00:00:00.000 -05:00> | | ttime('2017‑03‑02 14:45 Europe/Paris') | From an ISO-8601 date/time (variant with space instead of T) and IANA timezone. | DateTime<2017-03-02T14:45:00.000 +01:00> | | ttime('20:17:15') | Dateless time from an ISO-8601 time string. | DateTime<20:17:15.000> | | ttime(1200848400000) | From a millisecond timestamp. | DateTime<2008-01-20T12:00:00.000 -05:00> | | ttime({ tai: 1200848400000 }) | From a TAI millisecond timestamp. | DateTime<2008-01-20T12:00:00.000 -05:00> | | ttime({ y: 2008, m: 1, d: 20, hrs: 12, min: 0 }) | From a DateAndTime object, short-style field names. | DateTime<2008-01-20T12:00:00.000 -05:00> | | ttime({ year: 2008, month: 1, day: 20, hour: 12, minute: 0 }) | From a DateAndTime object, long-style field names. | DateTime<2008-01-20T12:00:00.000 -05:00> | | ttime([2013, 12, 11, 10, 9, 8, 765]) | From a numeric array: year, month, day, (hour (0-23), minute, second, millisecond), in that order. | DateTime<2013-12-11T10:09:08.765 -05:00> | | ttime(new Date(2008, 0, 20, 12, 0)) | From a JavaScript Date object. | DateTime<2008-01-20T12:00:00.000 -05:00> | | ttime('Feb 26 2021 11:00:00 GMT‑0500') | From an ECMA-262 string(Parsing performed by JavaScript Date('time_string')). | DateTime<2021-02-26T11:00:00.000 ‑05:00> | | ttime.unix(1318781876.721) | From a Unix timestamp. | DateTime<2011-10-16T12:17:56.721 -04:00§> | | ttime.unix(1318781876.721, 'UTC') | From a Unix timestamp, with timezone. | DateTime<2011-10-16T16:17:56.721 +00:00> |

When dealing with Daylight Saving Time, and days when clocks are turned backward, some hour/minute combinations are repeated. The time might be 1:59, go back to 1:00, then forward again to 1:59, and only after hitting 1:59 for this second time during the day, move forward to 2:00.

By default, any ambiguous time is treated as the earlier time, the first occurrence of that time during a day. You can, however, use either an explicit UTC offset, or a subscript 2 (₂), to indicate the later time.

ttime('11/7/2021 1:25 AM America/Denver', 'MM/DD/YYYY h:m a z').toString()DateTime<2021-11-07T01:25:00.000 -06:00§>

ttime('11/7/2021 1:25₂ AM America/Denver', 'MM/DD/YYYY h:m a z').toString()DateTime<2021-11-07T01:25:00.000₂-07:00>

ttime('2021-11-07 01:25 -07:00 America/Denver').toString()DateTime<2021-11-07T01:25:00.000₂-07:00>

Formatting output

Dates and times can be formatted in many ways, using a broad selection of format tokens, described in the table below.

For the greatest adherence to localized formats for dates and times, you can use the IXX format strings, which call directly upon Intl.DateTimeFormat (if available) to create localized dates, times, and combined date/times.

You can also produce more customized, flexible formatting, specifying the order, positioning, and style (text vs. number, fully spelled out or abbreviated, with or without leading zeros) of each date/time field, with embedded punctuation and text as desired.

For example:

ttime().format('ddd MMM D, y N [at] h:mm A z')Wed Feb 3, 2021 AD at 8:59 PM EST

ttime().toLocale('de').format('ddd MMM D, y N [at] h:mm A z')Mi 02 3, 2021 n. Chr. at 9:43 PM GMT-5

Please note that most unaccented Latin letters (a-z, A-Z) are interpreted as special formatting characters, as well as the tilde (~), so when using those characters as literal text they should be surrounded with square brackets, as with the word “at” in the example above.

Special CJK date formatting options

A few of the formatting tokens below can have an optional trailing tilde (~) added. This is for special handling of Chinese, Japanese, and Korean (CJK) date notation. The ~ is replaced, where appropriate, with , , or for Chinese and Japanese, and with , , or for Korean. Korean formatting also adds a space character when the following character is a letter or digit, but not when punctuation or the end of the format string comes next.

For all other languages, ~ is replaced with a space character when the following character is a letter or digit, or simply removed when followed by punctuation or the end of the format string.

For example:

ttime().toLocale('zh').format('MMM~YYYY~')8月2021年

ttime().toLocale('es').format('MMM~YYYY~')ago 2021

Format string tokens

| | Token | Output | |-----------------------------------|------------------------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Era | NNNNNNNN, NN, N | BC ADAbbreviated era (no distinction between narrow and abbreviated, as in Moment.js). | | | NNNN | Before Christ, Anno DominiLong-form era. | | | n | BCAbbreviated era, only shows for BC, not AD. When year is AD, leading space before n token is removed. | | Year | YYYYYY | -001970 -001971 ... +001907 +001971Always-signed years, padded to six digits. | | | YYYY  YYYY~ | 1970 1971 ... 2029 2030Padded to at least four digits. With ~, or is added when needed for CJK locales, otherwise replaced by a space character or empty string. | | | YY | 70 71 ... 29 30Padded to two digits with leading zero if necessary. | | | Y  Y~ | 1970 1971 ... 9999 +10000 +10001Padded to at least four digits, + sign shown when over 9999. With ~, or is added when needed for CJK locales, otherwise ~ is replaced by a space character or empty string. | | | y  y~ | 1 2 ... 2020 ...Era year, for use with BC/AD, never 0 or negative. With ~, or is added when needed for CJK locales, otherwise ~ is replaced by a space character or empty string. | | Week year (ISO) | GGGG | 1970 1971 ... 2029 2030, + sign shown when over 9999. | | | GG | 70 71 ... 29 30Padded to two digits with leading zero if necessary. | | Week year (locale) | gggg | 1970 1971 ... 2029 2030, + sign shown when over 9999. | | | gg | 70 71 ... 29 30Padded to two digits with leading zero if necessary. | | Quarter | Qo | 1st 2nd 3rd 4th | | | Q | 1 2 3 4 | | Month | MMMM  MMMM~ | January February ... November December1月 2月 ... 11月 12月 • 一月 二月 ... 十一月 十二月 • 1월 2월 ... 11월 12월For CJK locales, or is added when using either the MMMM and MMMM~ token, but using MMMM~ allows the position of the ~ to be replaced with a blank, when appropriate, for other languages. | | | MMM  MMM~ | Jan Feb ... Nov Dec1月 2月 ... 11月 12月 • 1월 2월 ... 11월 12월With ~, or is added when needed for CJK locales, otherwise ~ is replaced by a space character or empty string. | | | MM  MM~ | 01 02 ... 11 1201月 02月 ... 11月 12月 • 01월 02월 ... 11월 12월With ~, or is added when needed for CJK locales, otherwise ~ is replaced by a space character or empty string. | | | M  M~ | 1 2 ... 11 121月 2月 ... 11月 12月 • 1월 2월 ... 11월 12월With ~, or is added when needed for CJK locales, otherwise ~ is replaced by a space character or empty string. | | | Mo | 1st 2nd ... 11th 12th | | Week (ISO) | WW | 01 02 ... 52 53 | | | W | 1 2 ... 52 53 | | Week (locale) | ww | 01 02 ... 52 53 | | | w | 1 2 ... 52 53 | | Day of month | DD  DD~ | 01 02 ... 30 31With ~, or is added when needed for CJK locales, otherwise ~ is replaced by a space character or empty string. | | | D  D~ | 1 2 ... 30 31With ~, or is added when needed for CJK locales, otherwise ~ is replaced by a space character or empty string. | | | Do | 1st 2nd ... 30th 31st | | Day of year | DDDD | 001 002 ... 364 365 366 | | | DDD | 1 2 ... 364 365 366 | | Day of week | dddd | Sunday Monday ... Friday Saturday | | | ddd | Sun Mon ... Fri Sat | | | dd | Su Mo ... Fr Sa | | | d | 0 1 ... 5 6 | | | do | 0th 1st ... 5th 6th | | Day of Week (ISO) | E | 1 2 ... 6 7 | | Day of Week (locale) | e | 1 2 ... 6 7Note: this is 1-based, not 0-based, as in Moment.js. | | Hour | HH | 00-23 | | | H | 0-23 | | | hh | 01-12, for use with AM/PM | | | h | 1-12, for use with AM/PM | | | KK | 00-11, for use with AM/PM | | | K | 0-11, for use with AM/PM | | | kk | 01-24 | | | k | 1-24 | | Day period | A | AM PM | | | a | am pm | | Minute | mm | 00-59 | | | m | 0-59 | | Second | ss | 00-59 | | | s | 0-59 | | Fractional seconds | S | 0-9 (tenths of a second) | | | SS | 00-99 (hundredths of a second) | | | SSS | 000-999 (milliseconds) | | | SSSS... | Additional zeros after milliseconds. | | Timezone | ZZZ | America/New_York, Europe/Paris, etc.IANA timezone, if available. | | | zzz | Australian Central Standard Time, Pacific Daylight Time, etc.Long form names are only for output — cannot be parsed. | | | ZZ | -0700 -0600 ... +0600 +0700If used with a TAI time, the displayed offset will be the difference between TAI and UTC at a given moment in time. Outside of the well-defined span of officially-declared leap seconds, this offset might be displayed with millisecond precision. | | | zz, z | EST, CDT, MST, PDT, AEST, etc.Please note that timezones in this format are not internationalized, and are not unambiguous when parsed. | | | Z | -07:00 -06:00 ... +06:00 +07:00 | | Unix timestamp, UTC | X | 1360013296 | | Unix millisecond timestamp, UTC | x | 1360013296123 | | Unix timestamp, epoch | XX | 1360013296 | | Unix millisecond timestamp, epoch | xx | 1360013296123 | | Unix timestamp, TAI | X | 1360013331 | | Unix millisecond timestamp, TAI | x | 1360013331123 | | Daylight Saving Time indicator | V | § # ^ ~ ❄Symbol indicating DST is in effect.This is typically §, meaning the clock has been turned forward one hour.# means two hours forward, ^ means half an hour, ~ is any other forward amount.❄ is negative DST, i.e. “Winter Time”.Renders one blank space when DST is not in effect. | | | v | Same as above, but no blank space when DST is not in effect. | | Occurrence indicator | R | 1:00 , 1:01 ... 1:58 , 1:59 , 1:00₂, 1:01₂ ... 1:58₂, 1:59₂, 2:00 , 2:01A subscript 2 (₂) that denotes the second occurrence of the same clock time during a day when clocks are turned back for Daylight Saving Time. | | | r | Same as above, but no blank space when subscript isn’t needed. |

Moment.js formats not supported by @tubular/time: DDDo, Wo, wo, yo

@tubular/time formats not supported by Moment.js: KK, K, kk, k, ZZZ, V, v, R, r, n, IXX (IFF, IFL, IFM... IxM, IxS)

Moment.js-style localized formats

| | Token | Output | | -------|------:|-----------------------------------------| | Month name, day of month, day of week, year, time | LLLL | Thursday, September 4, 1986 at 8:30 PM | | | llll | Thu, Sep 4, 1986 8:30 PM | | Month name, day of month, year, time | LLL | September 4, 1986 8:30 PM | | | lll | Sep 4, 1986 8:30 PM | | Month name, day of month, year | LL | September 4, 1986 | | | ll | Sep 4, 1986 | | Month numeral, day of month, year | L | 09/04/1986 | | | l | 9/4/1986 | | Time with seconds | LTS | 8:30:25 PM | | Time | LT | 8:30 PM |

@tubular/time Intl.DateTimeFormat shorthand string formats

These start with a capital letter I, followed by one letter for the date format, which corresponds to the dateStyle option of Intl.DateTimeFormat, and one letter for the time format, corresponding to the timeStyle option.

The capital letters F, L, M, and S correspond to the option values 'full', 'long', 'medium', and 'short'. ILS thus specifies a long style date and a short style time. IL is a long style date alone, without time. IxS is a short style time without a date.

Examples

| Format | Output | |---|---| | IFF | Thursday, September 4, 1986 at 8:30:00 PM Eastern Daylight Time | | ILM | September 4, 1986 at 8:30:00 PM | | IS | 9/4/86 | | IxL | 8:30:00 PM EDT |

You can also augment these formats with brace-enclosed Intl.DateTimeFormatOptions, such as:

IMM{hourCycle:23h}

...which will start with whatever the localized time formatting is and force it into 24-hour time, whether the standard localized form is a 12- or 24-hour format. Note that no quotes are placed around the option values, as they would be in JavaScript/TypeScript code.

Pre-defined formats

ttime.DATETIME_LOCAL         = 'Y-MM-DD[T]HH:mm';
ttime.DATETIME_LOCAL_SECONDS = 'Y-MM-DD[T]HH:mm:ss';
ttime.DATETIME_LOCAL_MS      = 'Y-MM-DD[T]HH:mm:ss.SSS';
ttime.DATE                   = 'Y-MM-DD';
ttime.TIME                   = 'HH:mm';
ttime.TIME_SECONDS           = 'HH:mm:ss';
ttime.TIME_MS                = 'HH:mm:ss.SSS';
ttime.WEEK                   = 'GGGG-[W]WW';
ttime.WEEK_AND_DAY           = 'GGGG-[W]WW-E';
ttime.WEEK_LOCALE            = 'gggg-[w]ww';
ttime.WEEK_AND_DAY_LOCALE    = 'gggg-[w]ww-e';
ttime.MONTH                  = 'Y-MM';

Parsing with a format string, and optionally a locale

(As viewed via formatted output)

| | .format('IMM') | |---|---| | ttime('02/03/32', 'MM-DD-YY') | Feb 3, 2032, 12:00:00 AM | | ttime('02/03/32', 'DD-MM-YY') | Mar 2, 2032, 12:00:00 AM | | ttime('02/03/32 4:30 pm', 'DD-MM-YY hh:mm a', 'fr') | 2 mars 2032 à 16:30:00 | | ttime('02/03/32', 'DD-MM-YYYY') | Mar 2, 0032, 12:00:00 AM | | ttime('2032-03-02T16:30', null, 'ru') | 2 мар. 2032 г., 16:30:00 | | ttime('2032-03-02T16:30', null, 'ar-sa') | ٠٢‏/٠٣‏/٢٠٣٢ ٤:٣٠:٠٠ م | | ttime('2032-03-02T16:30', null, 'zh-cn') | 2032年3月2日 下午4:30:00 |

Converting timezones

ttime('2005-10-10 16:30 America/Los_Angeles').tz('Europe/Warsaw').toString()DateTime<2005-10-11T01:30:00.000 +02:00>

Please note that if you pass a second argument of true, the timezone is changed, but the wall time stays the same. This same option to preserve wall time is available for the utc() and local() methods, where the optional boolean value will be the one and only argument.

ttime('2005-10-10 16:30 America/Los_Angeles').tz('Europe/Warsaw', true).toString()DateTime<2005-10-10T16:30:00.000 +02:00>

ttime('2005-10-10 16:30 America/Los_Angeles').utc().toString()DateTime<2005-10-10T23:30:00.000 +00:00>

ttime('2005-10-10 16:30 America/Los_Angeles').utc().toString(true)DateTime<2005-10-10T16:30:00.000 +00:00>

// Local zone is America/New_Yorkttime('2005-10-10 16:30 America/Los_Angeles').local().toString()DateTime<2005-10-10T19:30:00.000 +04:00>

Converting locales

ttime('7. helmikuuta 2021', 'IL', 'fi').toLocale('de').format('IL')7. Februar 2021

Defining and updating timezones

These functions define the size and behavior of the IANA timezone definitions used by @tubular/time:

ttime.initTimezoneSmall();
ttime.initTimezoneLarge();
ttime.initTimezoneLargeAlt();

By default, @tubular/time is set up using initTimezoneSmall(). This covers explicitly-defined timezone information for roughly the release date of the version of @tubular/time you’re using, +/- five years, supplemented by rules-based extensions (i.e. knowing that for a particular timezone, say, “DST starts on the last Sunday of March and ends on the last Sunday of October”), and further supplemented by information extracted from Intl, when available.

With proper tree-shaking, the code footprint of @tubular/time should be less than 150K when using the small timezone definitions.

Using initTimezoneLarge() provides the full IANA timezone database. Using this will increase code size by about 280K, presuming that your build process is smart enough to have otherwise excluded unused code in the first place.

initTimezoneLargeAlt() provides a slight variant of the full IANA timezone database, and is also roughly 280K. This variant rounds all timezone offsets to full minutes, and adjusts a small number of fairly old historical changes by a few hours so that only the time-of-day ever goes backward, never the calendar date. It’s generally more than enough trouble for software to cope with missing and/or repeated hours during a day; initTimezoneLargeAlt() makes sure the date/time can’t be, say, the 19th of the month, then the 18th, and then the 19th again, as happens with the unmodified America/Juneau timezone during October 1867.

For browser-based inclusion of timezone definitions, if not relying on a tool like webpack to handle such issues for you, you can also include full timezone definitions this way:

<script src="https://unpkg.com/@tubular/time/dist/data/timezone-large.js"></script>

...or...

<script src="https://unpkg.com/@tubular/time/dist/data/timezone-large-alt.js"></script>

Either of these should appear before the script tag that loads @tubular/time itself.

Live timezone updates

Timezone definitions can be updated live as well. Different polling methods are needed for Node.js code or browser-hosted code, since both environments access web resources in very different ways (and browsers have CORS issues, which Node.js does not).

To be informed when a live timezone update takes place, add and remove update listeners using these functions:

function addZonesUpdateListener(listener: (result: boolean | Error) => void): void;
function removeZonesUpdateListener(listener: (result: boolean | Error) => void): void;
function clearZonesUpdateListeners(): void

The result received by a callback is true if an update was successful, and caused changes in timezone definitions, false if successful, but no changes occurred, or an instance of Error, indicating an error (probably an HTTP failure) has occurred.

For example:

  const listener = result => console.log(result); // Keep in a variable if removal is needed later

  ttime.addZonesUpdateListener(listener);

  // Later on in the code...

  ttime.removeZonesUpdateListener(listener);

Why use a listener? Because you might want to recalculate previously calculated times, which possibly have changed due to timezone definition changes. For example, imagine you have a video meeting scheduled for 10:00 in a client’s timezone, which, when you first schedule it, was going to be 15:00 in your timezone. Between the time you scheduled the meeting, however, and when the meeting actually takes place, the switch to Daylight Saving Time is cancelled for the client’s timezone. If you still intend to talk to your client at 10:00 their time, you have to meet at 16:00 in your timezone instead.

To poll for for timezone updates at a regular interval, use:

function pollForTimezoneUpdates(zonePoller: IZonePoller | false, name: ZoneOptions = 'small', intervalDays = 1): void;
  • zonePoller: Either zonePollerBrowser (from tbTime.zonePollerBrowser) or zonePollerNode (using import or require, from '@tubular/time'). If you pass the boolean value false, polling ceases.
  • name: One of 'small', 'large', or 'large-alt'. Defaults to 'small'.
  • intervalDays: Frequency of polling, in days. Defaults to 1 day. The fastest allowed rate is once per hour (~0.04167 days).

You can also do a one-off request:

function getTimezones(zonePoller: IZonePoller | false, name: ZoneOptions = 'small'): Promise<boolean>;

zonePoller and name are the same as above. Any periodic polling done by pollForTimezoneUpdates() is canceled. You can get a response via registered listeners, but this function also returns a Promise. The promise either resolves to a boolean value, or is rejected with an Error.

The YMDDate and DateAndTime objects

YMDate:

{
  y: 2021, // short for year
  q: 1, // short for quarter
  m: 2, // short for month
  d: 4, // short for day
  dow: 4, // short for dayOfWeek (output only)
  dowmi: 1, // dayOfWeekMonthIndex (output only)
  dy: 35, // short for dayOfYear
  n: 18662, // short for epochDay
  j: false, // short for isJulian

  year: 2021,
  quarter: 1, // quarter of the year 1-4
  month: 2,
  day: 4,
  dayOfWeek: 4, // Day of week as 0-6 for Sunday-Saturday (output only)
  dayOfWeekMonthIndex: 1, // Day of week month index, 1-5, e.g. 2 for 2nd Tuesday of the month (output only)
  dayOfYear: 35,
  epochDay: 18662, // days since January, 1 1970
  isJulian: false, // true if a Julian calendar date instead of a Gregorian date

  yw: 2021, // short for yearByWeek
  w: 5, // short for week
  dw: 4, // short for dayByWeek
  yearByWeek: 2021, // year that accompanies an ISO year/week/day-of-week style date
  week: 5, // week that accompanies an ISO year/week/day-of-week style date
  dayByWeek: 4, // day that accompanies an ISO year/week/day-of-week style date

  ywl: 2021, // short for yearByWeekLocale
  wl: 6, // short for weekLocale
  dwl: 5, // short for dayByWeekLocale
  yearByWeekLocale: 2021, // year that accompanies a locale-specific year/week/day-of-week style date
  weekLocale: 6, // week that accompanies a locale-specific year/week/day-of-week style date
  dayByWeekLocale: 5, // day that accompanies a locale-specific year/week/day-of-week style date

  error: 'Error description if applicable, otherwise undefined'
}

DateAndTime, which extends the YMDDate interface:

{
  hrs: 0, // short for hour
  min: 18, // short for minute
  sec: 32, // short for second
  hour: 0,
  minute: 18,
  second: 32,
  millis: 125, // 0-999 milliseconds part of time

  utcOffset: -18000, // offset (in seconds) from UTC, negative west from 0°, including DST offset when applicable
  dstOffset: 0, // DST offset, in minutes - usually positive, but can be negative (output only)
  occurrence: 1, // usually 1, but can be 2 for the second occurrence of the same wall clock time during a single day, caused by clock being turned back for DST
  deltaTai: 37, // How much (in seconds) TAI exceeds UTC or UT1 at given moment in time (output only)
  /* In the well-defined range for UTC, deltaTai is always an integer value.
     Outside that range it can be a non-integer with millisecond precision. */

  jde: 2459249.722008264, // Julian days, ephemeris
  mjde: 59249.22200826416, // Modified Julian days, ephemeris
  jdu: 2459249.7212051502, // Julian days, UT
  mjdu: 59249.22120515024 // Modified Julian days, UT
}

When using a YMDDate or DateAndTime object to create a DateTime instance, you need only set a minimal number of fields to specify the date and/or time you are trying to specify. You can use either short or long names for fields (if you use both, the short form takes priority).

At minimum, you must specify a date or a time. If you only specify a date, the time will be treated as midnight at the start of that date. If you only specify a time, you can create a special dateless time instance. You can also, of course, specify both date and time together.

In specifying a date, the date fields have the following priority:

  • n / epochDay: Number of days before/after epoch day 0, which is January 1, 1970.
  • y / year: A normal calendar year. Along with the year, you can specify:
    • Nothing more, in which case the date is treated as January 1 of that year.
    • m / month: The month (a normal 1-12 month, not the weird 0-11 month the JavaScript Date uses!).
      • If nothing more is given, the date is treated as the first of the month.
      • d / day: The date of the month.
    • dy / dayOfYear: The 1-based number of days into the year, such that 32 means February 1.
  • yw / yearByWeek: An ISO week-based calendar year, where each week starts on Monday. This year is the same as the normal calendar year for most of the calendar year, except for, possibly, a few days at the beginning and end of the year. Week 1 is the first week which contains January 4. Along with this style of year, you can specify:
    • Nothing more, in which case the date is treated as the first day of the first week of the year.
    • w / week: The 1-based week number.
      • If nothing more, the date is treated as the first day of the given week.
      • dw / dayByWeek: The 1-based day of the given week.
  • ywl / yearByWeekLocale, etc.: These fields work the same as yw / yearByWeek, etc., except that they apply to locale-specific rules for the day of the week on which each week starts, and for the definition of the first week of the year.

In specifying a time, the minimum needed is a 0-23 value for hrs / hour. All other unspecified time fields will be treated as 0.

Astronomical time fields will supersede any of the above date fields.

As discussed earlier, concerning parsing time strings, ambiguous times due to Daylight Saving Time default to the earlier of two times. You can, however, use occurrence: 2 to explicitly specify the later time. An explicit utcOffset can also accomplish this disambiguation.

Reading individual DateTime fields

As an output from a DateTime instance, such as what you get from ttime().wallTime, all DateAndTime fields will be filled in with synchronized values. ttime().wallTime.hour provides the hour value, ttime().wallTime.utcOffset provides the UTC offset in seconds for the given time, etc.

ttime().wallTimeShort returns a DateAndTime object with all available short-form field names, and ttime().wallTimeLong only long-form field names. ttime().wallTimeSparse returns a DateAndTime object with a minimal set of short-form field names: y, m, d, hrs, min, sec, millis.

Modifying DateTime values

There are six main methods for modifying a DateTime value:

  • add(field: DateTimeField | DateTimeFieldName, amount: number, variableDays = true): DateTime
  • subtract(field: DateTimeField | DateTimeFieldName, amount: number, variableDays = true): DateTime
  • roll(field: DateTimeField | DateTimeFieldName, amount: number, minYear = 1900, maxYear = 2099)
  • set(field: DateTimeField | DateTimeFieldName, value: number, loose = false): DateTime
  • startOf(field: DateTimeField | DateTimeFieldName): DateTime
  • endOf(field: DateTimeField | DateTimeFieldName): DateTime

Before going further, it needs to be mentioned that DateTime instances can be either locked, and thus immutable, or unlocked. Instances generated using ttime(...) are locked. Instances created using the DateTime constructor (covered later in this document) are created unlocked, but can be locked after creation.

When you use the add/subtract/roll/set methods on a locked instance, a new modified and locked instance is returned. When used on an unlocked instance, these methods modify that instance itself, and a reference to the modified instance is returned.

Using add (and subtract)

subtract() is nothing more than a convenience method which negates the amount being added, and then calls add(). The documentation that follows is in terms of the add() method alone, but applies, with this negation, to the subtract() method as well.

An example of using add():

ttime().add('year', 1) or ttime().add(DateTimeField.YEAR, 1)

The above produces a date one year later than the current time. In most cases, this means that the resulting date has the same month and date, but in the case of a leap day:

ttime('2024-02-29').add('year', 1).toIsoString(10)2025-02-28

...the date is pinned to 28 so that an invalid date is not created. Similarly, when adding months, invalid dates are prevented:

ttime('2021-01-31').add(DateTimeField.MONTH, 1).toIsoString(10)2021-02-28

You can add using the following fields: MILLI, SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR, YEAR_WEEK, and YEAR_WEEK_LOCALE, as provided by the DateTimeField enum, or their string equivalents ('milli', 'millis', 'millisecond', 'milliseconds'... 'day', 'days', 'date', 'month', 'months', etc.).

(There are further fields defined for dealing with leap seconds and TAI, described later.)

For fields MILLI through HOUR, fixed units of time, multiplied by the amount you pass, are applied. When dealing with months, quarters, and years, the variable lengths of months, quarters, and years apply.

DAY amounts can be handled either way, as variable in length (due to possible effects of Daylight Saving Time), or as fixed units of 24 hours. The default for variableDays is true.

DST can alter the duration of days, typically adding or subtracting an hour, but other amounts of change are possible (like the half-hour shift used by Australia’s Lord Howe Island), so adding days can possibly cause the hour (and even minute) fields to change:

ttime('2021-02-28T07:00 Europe/London', false).add('days', 100).toIsoString()2021-06-08T08:00:00.000+01:00 (note shift from 7:00 to 8:00)

ttime('2021-02-28T07:00 Australia/Lord_Howe, false').add('days', 100).toIsoString() 2021-06-08T06:30:00.000+10:30` (note shift from 7:00 to 6:30)

By default, however, hour and minute fields remain unchanged.

ttime('2021-02-28T07:00 Australia/Lord_Howe').add('days', 100).toIsoString()2021-06-08T07:00:00.000+10:30

Even with the default behavior, however, it is still possibl