The easiest way to work with dates in JavaScript.

Introduction

Tempo is a new library in a proud tradition of JavaScript date and time libraries. Inspired by the likes of moment.js, day.js, and date-fns, Tempo is built from the ground up to be as small and easy to use as possible (all features ~5kB compressed) — including first-class support for timezone operations.

Tempo is best thought of as a collection of utilities for working with the native Date object — an important distinction from other libraries that provide custom date primitives. Under the hood, Tempo mines JavaScript's Intl.DateTimeFormat to extract complex data like timezones offsets and locale aware date formats, giving you a simple API to format, parse, and manipulate dates.

Install

Pshhh, come on — you already know how to do this. All you’re looking for is the package name (it’s @formkit/tempo), but to make it even easier just click your package manager of choice to copy the install command.

pnpmpnpm add @formkit/tempo
npmnpm install @formkit/tempo
yarnyarn add @formkit/tempo
bunbun install @formkit/tempo

Since tempo is just a collection of functions, you use them by importing them from the @formkit/tempo project — great for tree shaking.

Format

3.02 kB
function format(
date: Date | string,  // strings must be ISO 8601
format: string | object,
locale?: string
): string
// or
function format(
options: FormatOptions
): string

Tempo’s format() function outputs dates in two ways:

Format styles

When displaying dates to users, it’s a good idea to use the formats they are familiar with. Tempo uses Intl.DateTimeFormat’s language-sensitive date and time formatting to make this easy. By using a date or time "style", you indicate the level of specificity you’d like to show the date to the end user with, but no further details. It’s then up to the Intl.DateTimeFormat to decide how to most appropriately display the date.

Date styles

When using the format() function, the second argument can be any of the following date styles, or an object with a date property (ex: format(new Date(), { date: 'long' }))

StyleExample
fullen
de
zh
longen
de
zh
mediumen
de
zh
shorten
de
zh

Time styles

To use a time style format you must provide an object as the second argument of the format() function with a time property. You can also use the time property with the date property.

StyleExample
fullen
de
zh
longen
de
zh
mediumen
de
zh
shorten
de
zh

Format tokens

If you already know the format you need to display — Tempo’s formatting tokens allow for any arbitrary format in a way you are already familiar with (tokens are similar to day.js). These tokens automatically leverage the user’s locale or the one specified in the format function.

TokenExampleDescription
YY242 digit year
YYYY20244 digit year
M7The month 1-12
MM07The month 01-12
MMMJulShort name Jan-Dec
MMMMJulyFull name January - December
D12The day of the month 1-31
DD12The day of the month 01-31
dFSingle digit day "T"
dddFriShort day name
ddddFridayFull day name Wednesday
H1, 13Minimum hour digits, 24 hour, 0-23
HH01, 132 hour digits, 24 hour, 00-23
h1, 12Minimum hour digits, 12 hour clock, 1-12
hh01, 122 hour digits, 12 hour clock, 01-12
m2, 33The minute 0-59
mm02, 33The minute 00-59
s7, 17The second 0-59
ss07, 17The second 00-59
apmam/pm
APMAM/PM
Z+08:00, +05:30, -13:45The timezone offset from GMT ([+-]HH:mm)
ZZ+0800, +0530, -1345The timezone offset from GMT ([+-]HHmm)

Format options

The format() function can accept an object of options as its argument to provide more control over the output.

interface FormatOptions {
  /**
   * An ISO 8601 date string or a Date object.
   */
date: string | Date,
  /**
   * The format can be either format styles or format tokens.
   */
format: string | { date?: string, time?: string },
  /**
   * The locale to use when formatting.
   */
locale?: string,
  /**
   * Converts the given date option to the timezone provided.
   * For example, if the provided date option is 2021-01-01T00:00:00Z
   * and the tz option is America/New_York and the format option is
   * YYYY-MM-DD HH:mm:ss, the output will be 2020-12-31 19:00:00
   */
tz?: string,
  /**
   * When true, the month and weekday names will be in the
   * genitive case for locales where it is applicable.
   */
genitive?: boolean,
  /**
   * A function that filters the parts of the formatted date.
   * The function is called with each part of the formatted date
   * and should return true to include the part in the output.
   */
partFilter?: (part: Part) => boolean
}

Timezone

The tz option allows you to format the provided date from the “perspective” of any given timezone.

Part filter

The partFilter option allows you to filter out parts of the formatted date. The function is called with each "part" of the formatted date and should return a boolean indicating whether or not to include that part in final formatted string.

Genitive case

Some languages have a genitive case for months and weekdays. When the genitive option is set to true, the month and weekday names will be in the genitive case for locales where it is applicable.

Parse

4.43 kB
function parse(
date: string,
format: string | { date?: string, time?: string },
locale?: string
): Date
// or
function parse(
options: ParseOptions
): Date

To convert a date string into a Date object we use the parse function. This allows us to parse any output from the format function — including style formats!

A Date object in JavaScript is fundamentally a timestamp, in other words, always includes both date and time. The parse function does not need to include both date and time, but the resulting Date object will always include both. For consistent behavior, the undefined portions of the full date will use the current date at midnight local time.

Parsing options

The parse function can accept an object of options as its argument:

interface ParseOptions {
  /**
   * A string representing a date.
   */
date: string,
  /**
   * The format that should be used to parse the date.
   * This is a string composed of tokens.
   */
format: Format,
  /**
   * The locale used to parse the date.
   */
locale: string,
  /**
   * A function that can be used to filter out
   * parts of the format. This is useful when using the format styles
   * like { date: 'full', time: 'full' } and not wanting to keep
   * all the parts of the given format.
   */
partFilter?: (part: Part) => boolean,
  /**
   * The behavior to use when a date overflows a given month.
   * For example, if the date to parse is February 29, 2023 — there is
   * no 29th day of February. In this case overflow "forward" would
   * result in March 1, 2023, "backward" would result in
   * February 28, 2023 and "throw" would throw an error.
   */
dateOverflow?: "forward" | "backward" | "throw"
}

The date, format, locale options are familiar, but what is partFilter and dataOverflow?

partFilter

The partFilter option gives you fine-grained control over which pieces and parts of a date you’d like to include in the resulting Date object (remember, missing "parts" will default to the today’s date at midnight local).

dateOverflow

The dateOverflow option determines how an “out of range” date should be parsed (ex: February 30th). Options are backward (default), forward, throw.

Modify

Tempo includes a number of (tree-shakable) utility functions to assist you in your date modifying needs. These functions all accept either an ISO 8601 string or a Date object and return a new Date object (they do not change the date argument).

addDay

329 B
function addDay(
date: string | Date,
amount: number
): Date

Returns a new Date object with a positive or negative number of days applied to date argument. To subtract days, use a negative number.

addHour

328 B
function addHour(
date: string | Date,
amount: number
): Date

Returns a new Date object with a positive or negative number of hours applied to date argument. To subtract hours, use a negative number.

addMinute

327 B
function addMinute(
date: string | Date,
amount: number
): Date

Returns a new Date object with a positive or negative number of minutes applied to date argument. To subtract minutes, use a negative number.

addMonth

394 B
function addMonth(
date: string | Date,
amount: number,
dateOverflow: boolean
): Date

Returns a new Date object with a positive or negative number of months applied to date argument. To subtract months, use a negative number. Sometimes the result will "overflow" the available days of the result month. For example when adding 1 month to January 31st the resulting date would be February 31st, which does not exist. By default, the date will be set to the last day of February but you could opt for it to "overflow" into March by setting dateOverflow to true.

addSecond

326 B
function addSecond(
date: string | Date,
amount: number
): Date

Returns a new Date object with a positive or negative number of seconds applied to date argument. To subtract seconds, use a negative number.

addYear

408 B
function addYear(
date: string | Date,
amount: number,
dateOverflow: boolean
): Date

Returns a new Date object with a positive or negative number of years applied to date argument. To subtract years, use a negative number. Sometimes the result will "overflow" the available days of the result month. For example when adding 1 year to February 29, 2024 the resulting date would be February 29, 2025, which does not exist (2025 is not a leap year). By default, the date will be set to February 28, 2025 but you could opt for it to "overflow" into March by setting dateOverflow to true.

applyOffset

525 B
function applyOffset(
date: string | Date,
offset: string // +-HHmm, ex: -0800
): Date

Returns a new Date object with a timezone offset applied to date argument — this function does fundamentally change the date but can be very useful when working with timezones. Read more in the timezone section.

date

304 B
function date(
date: string | Date
): Date

Converts an ISO 8601 like string into a Date object (noop on Date objects). ISO 8601 strings do not need to be complete to be accepted, but you need at least a year and month.

dayEnd

325 B
function dayEnd(
date: string | Date
): Date

Returns a new Date object with the time set to 23:59:59.999 (local time).

dayStart

319 B
function dayStart(
date: string | Date
): Date

Returns a new Date object with the time set to 00:00:00.000 (local time).

hourEnd

323 B
function hourEnd(
date: string | Date
): Date

Returns a new Date object with the minutes part of the time set to 59:59.999 (local time).

hourStart

320 B
function hourStart(
date: string | Date
): Date

Returns a new Date object with the minutes part of the time set to 00:00.000 (local time).

minuteEnd

323 B
function minuteEnd(
date: string | Date
): Date

Returns a new Date object with the seconds part of the time set to 59.999 (local time).

minuteStart

316 B
function minuteStart(
date: string | Date
): Date

Returns a new Date object with the seconds part of the time set to 00.000 (local time).

monthEnd

330 B
function monthEnd(
date: string | Date
): Date

Returns a new Date object with the date set to the last day of the current month (does not modify the time).

monthStart

326 B
function monthStart(
date: string | Date
): Date

Returns a new Date object with the date set to the first day of the current month and the time set to 00:00:00 (local).

removeOffset

554 B
function removeOffset(
date: string | Date,
offset: string // +-HHmm, ex: -0800
): Date

Returns a new Date object with the inverse of the specified offset applied. This can be helpful to normalize time information across timezones.

tzDate

917 B
function tzDate(
date: string | Date,
tz: string // IANA timezone, ex: "America/Los_Angeles"
): Date

Converts an ISO 8601 like string into a Date object with a timezone applied. For example, tzDate('2021-01-01T00:00', 'America/Los_Angeles') will return a Date object representing 2021-01-01 00:00 in L.A.

weekEnd

379 B
function weekEnd(
date: string | Date,
startOfWeekDay: number // 0-6, 0 is Sunday
): Date

Returns a new Date object with the date set to the last day of the current week with the time set to 23:59:59 (local).

weekStart

359 B
function weekStart(
date: string | Date,
startOfWeekDay: number // 0-6, 0 is Sunday
): Date

Returns a new Date object with the date set to the first day of the current week with the time set to 00:00:00 (local).

yearEnd

338 B
function yearEnd(
date: string | Date
): Date

Returns a new Date object with the date set to the end of the year

yearStart

332 B
function yearStart(
date: string | Date
): Date

Returns a new Date object with the date set to the start of the year

Data

Tempo also includes functions to extract date information. These functions make no changes to the date object and are only used to extract useful data that is commonly needed to build applications.

ap

288 B
function ap(
amOrPm: 'am' | 'pm',
locale: string
): Date

Returns either am or pm but in any given locale.

dayOfYear

354 B
function dayOfYear(
date: Date
): number

Gets the day of the year a given date is. For example, August 1st is the 213th day of the year on non-leap years and 214th on leap years.

formatStr

1.6 kB
function formatStr(
format: { date?: string, time?: string },
locale?: string
): string

This little gem of a function returns the token format for a given format style.

fourDigitYear

79 B
function fourDigitYear(
year: string
): number

Converts a 2 digit year into a 4 digit year. This function assumes years 20 years into the future belong to the current century, and the past 80 are in the past century.

iso8601

168 B
function iso8601(
date: Date
): string

Validates that a given date passes “acceptable” levels of ISO 8601 compatibility and can be utilized within Tempo. This allows incomplete dates but must include at least the year and month. Does not require the T separator.

monthDays

345 B
function monthDays(
date: Date
): number

Returns the number of days in a given month.

nearestDay

566 B
function nearestDay(
date: Date,
searchFunction: (date: Date) => boolean,
constraint: number | "month" | "week" | "year" = 7
): Date | null

Performs a bidirectional search for the nearest date that passes a given search function. It stops searching when it finds a result or when it reaches the constraint bounds (on both sides).

offset

700 B
function offset(
date: Date,
tzA?: string,  // default: UTC, ex: America/New_York
tzB?: string // default: browser, ex: Europe/Paris
): string

Returns the offset between two (IANA) timezones on a given date. The results are ISO 8601 compatible string offsets like -0800 or +0530.

parseParts

369 B
function parseParts(
dateString: string,
parts: Part[]
): FilledPart[]

Given a date string like "2019/12/31" and the parts (like those returned from the parts function) this function returns the parts with the appropriate values extracted from the date string and added to a value property.

interface FilledPart extends Part {
  /**
   * The value of the part extracted from the date string.
   */
value: string
}

parts

1.5 kB
function parts(
format: string,
locale: string
): Part[]

Given a format and locale, this function produces an array of "parts". Similar to Intl.DateTimeFormat.formatToParts() but it accepts style formats and token formats and returns parts with granular data such as the part’s token and a regex to match for it.

interface Part {
  /**
   * Does this part require a 12 hour clock?
   */
hour12: boolean,
  /**
   * An object of partName to partValue For example:
   * { hour: '2-digit' }
   */
option: Partial<Record<Intl.DateTimeFormatPartTypes, string>>,
  /**
   * The type of part. For example: month or timeZoneName.
   */
partName: Intl.DateTimeFormatPartTypes,
  /**
   * The value of a given part. For example "2-digit", or "narrow".
   */
partValue: string,
  /**
   * A regular expression for matching the above token.
   */
pattern: RegExp,
  /**
   * The Tempo token for this part. For example: <code>MMMM</code>.
   */
token: string
}

range

3.33 kB
function range(
token: string,
locale: string,  // default: "en"
genitive?: boolean // default: false
): string[]

Returns an array of options for a given token in a given locale. For example, the token MMMM in the locale en-US would return ['January', 'February', 'March', ...].

yearDays

342 B
function yearDays(
date: Date
): number

Returns the number of days in a given year. Leap years and century years cause this to not always be 365.

Helpers

Tempo includes a number of (tree-shakable) helper functions to assist you in your date workarounds. These functions all accept either an ISO 8601 string or a Date object and return a boolean.

diffMilliseconds

322 B
function diffMilliseconds(
dateA: string | Date,
dateB: string | Date
): number

Returns the number of milliseconds difference between two date objects.

diffSeconds

365 B
function diffSeconds(
dateA: string | Date,
dateB: string | Date,
roundingMethod: "trunc" | "round" | "floor" | "ceil"
): number

Returns the number of seconds difference between two date objects. An optional third argument controls what kind of “rounding” should be used for partial seconds.

diffMinutes

366 B
function diffMinutes(
dateA: string | Date,
dateB: string | Date,
roundingMethod: "trunc" | "round" | "floor" | "ceil"
): number

Returns the number of minutes difference between two date objects. An optional third argument controls what kind of “rounding” should be used for partial minutes.

diffHours

364 B
function diffHours(
dateA: string | Date,
dateB: string | Date,
roundingMethod: "trunc" | "round" | "floor" | "ceil"
): number

Returns the number of hours difference between two date objects. An optional third argument controls what kind of “rounding” should be used for partial hours.

diffDays

365 B
function diffDays(
dateA: string | Date,
dateB: string | Date,
roundingMethod: "trunc" | "round" | "floor" | "ceil"
): number

Returns the number of days difference between two date objects. An optional third argument controls what kind of “rounding” should be used for partial days.

diffWeeks

366 B
function diffWeeks(
dateA: string | Date,
dateB: string | Date,
roundingMethod: "trunc" | "round" | "floor" | "ceil"
): number

Returns the number of weeks difference between two date objects. An optional third argument controls what kind of “rounding” should be used for partial weeks.

diffMonths

439 B
function diffMonths(
dateA: string | Date,
dateB: string | Date,
roundingMethod: "trunc" | "round" | "floor" | "ceil"
): number

Returns the number of months difference between two date objects. An optional third argument controls what kind of “rounding” should be used for partial months.

diffYears

459 B
function diffYears(
dateA: string | Date,
dateB: string | Date,
roundingMethod: "trunc" | "round" | "floor" | "ceil"
): number

Returns the number of years difference between two date objects. An optional third argument controls what kind of “rounding” should be used for partial years.

isAfter

323 B
function isAfter(
inputDate: string | Date,
dateToCompare: string | Date
): boolean

Returns true if the first date is after the second date, otherwise false.

isBefore

323 B
function isBefore(
inputDate: string | Date,
dateToCompare: string | Date
): boolean

Returns true if the first date is before the second date, otherwise false.

isEqual

323 B
function isEqual(
dateLeft: string | Date,
dateRight: string | Date
): boolean

Returns true if the first date is equal to the second date, otherwise false.

sameSecond

331 B
function sameSecond(
dateA: Date,
dateB: Date
): boolean

Checks if two dates are the same second. This function is useful for comparing dates but ignoring the milliseconds.

sameMinute

332 B
function sameMinute(
dateA: Date,
dateB: Date
): boolean

Checks if two dates are the same minute. This function is useful for comparing dates but ignoring the seconds and milliseconds.

sameHour

331 B
function sameHour(
dateA: Date,
dateB: Date
): boolean

Checks if two dates are the same hour. This function is useful for comparing dates but ignoring the minutes, seconds, and milliseconds.

sameDay

349 B
function sameDay(
dateA: Date,
dateB: Date
): boolean

Checks if two dates are the same day. This function is useful for comparing dates but ignoring the time.

sameYear

334 B
function sameYear(
dateA: Date,
dateB: Date
): boolean

Checks if two dates are the same year. This function is useful for comparing dates but ignoring the month, day, and time.

Timezones

Timezones are challenging for 2 reasons:

  • 1. They are conceptually difficult to understand.
  • 2. They involve a lot of geography, history, and politics.

Tempo provides timezone support via format(), tzDate(), offset, applyOffset, and removeOffset functions.

Key concept

A timezone is really an expression or "view" of a given absolute time. An airplane departing Amsterdam 2012-04-07 11:00:00 UTC leaves the ground at the same moment in every timezone on earth. JavaScript’s Date object is "absolute" in the same way as the airplane’s takeoff. A timezone is only a way to express that moment relative the geography and politics of a given region.

Using timezones

Creating timezone dates

The most basic timezone aware function is tzDate which allows you to create a new Date object at in a particular timezone.

Formatting timezones

The format function can accept a tz option to format a date in a specific timezone.

Calculating offsets

Tempo uses the Intl.DateTimeFormat API to extract timezone information, that makes working with timezones as simple as possible. The offset() function calculates the amount of offset between any two timezones (given in +-HHmm).

Removing offsets

To display the time of a Date object in a specific timezone you only need to remove the relative offset. Since Tempo operates with native Date objects the resulting Date object is one whose internal methods (like getHours()) will return the time "at" the desired timezone.

Applying offsets

If you are creating a car rental booking app you want the pickup time to always be relative to the local time of the pickup location. The applyOffset function is used to apply a given offset to a Date object to determine the absolute time in a different timezone.

Support us

Tempo is made with love by the FormKit team — the creators of open source projects such as:

  • FormKit - The open-source form framework for Vue.
  • AutoAnimate - Add motion to your apps with a single line of code.
  • ArrowJS - Reactivity without the Framework.

If you find our projects useful and would like to support their ongoing development, please consider sponsoring us on GitHub!