Skip to content

Momentesque

Updated peter.dexter@formbird.com - 2026-08-10

"Momentesque" is a ruleset include script which provides a number of functions to replace those exploited in the past from Moment.js, and is envisaged to help replace usage of Moment within ruleset scripts over time.

It also provides means to convert between legacy Date objects and newer Temporal objects.

Be aware, this replaces only the most often used functions from Moment, not its entire function catalog, so in rare cases it would be necessary to recode entirely, using Temporal date functionality, or the functions here.

If a function that was used at large scale is omitted here, please make request to peter.dexter@formbird.com to include a replacement function.

Please note, even with these provided replacement functions, it is still necessary to recode old Moment function calls.

To use

At top of ruleset, add the #include directive for Momentesque

#include "Momentesque"

This provides the following object to use

ft3.momentesque


Functions


isValid

Checks whether a value is a valid Date, number, or ISO 8601 date/date-time string.

NB: Due to the variability of interpretation of string dates by the various browser engines, it is not possible to determine if a particular "friendly" format (eg "June 12, 2022, 3:03pm") will be "valid" in every browser instance.

Parameters

Name Type Description
value Date \| number \| string The value to validate

Returns: boolean

Accepted string formats: strictly ISO 8601 only — 2023-01-31, 2023-01-31T14:30:00, 2023-01-31T14:30:00Z, 2023-01-31T14:30:00+02:00.

ft3.momentesque.isValid("2023-06-15");           // true
ft3.momentesque.isValid("2023-06-15T14:30:00Z"); // true
ft3.momentesque.isValid("June 15, 2023");        // false — non-ISO formats no longer accepted
ft3.momentesque.isValid("01/02/2023");           // false
ft3.momentesque.isValid(new Date());             // true
ft3.momentesque.isValid(NaN);                    // false

parseWithFormat

Parses a date/time string using an explicit Moment-style format string.

Replaces moment's constructor function used with format string, eg

var x = moment("23 Sep 1966", "D MMM YYYY")

Parameters

Name Type Description
str string The date/time string to parse
format string A format string made of the tokens below

Returns: Date (always — including when the format includes Z, in which case the string is parsed as UTC before constructing the Date).

ft3.momentesque.parseWithFormat("14 Aug 1999, 7:04pm", "DD MMM YYYY, h:mma");
ft3.momentesque.parseWithFormat("August 14, 2023, 1:03PM", "MMMM D, YYYY, h:mmA");
ft3.momentesque.parseWithFormat("2026-08-06T01:03:05Z", "YYYY-MM-DDTHH:mm:ssZ"); // -> Date, parsed as UTC
ft3.momentesque.parseWithFormat("14 Aug 2023, 3:33:12.500pm", "DD MMM YYYY, h:mm:ss.SSSa");
ft3.momentesque.parseWithFormat("15 June 2023", "DD MMMM YYYY");    // no time tokens -> defaults to midnight
ft3.momentesque.parseWithFormat("12:00pm", "h:mma");                // no date tokens -> defaults to today's date

Throws an Error if str doesn't match format, or if a month name/abbreviation isn't recognized.

Missing date components (year/month/day) default to today's date; missing time components default to midnight (00:00:00.000).

See Format Tokens Reference for supported tokens.


startOf

Returns a new date/time value set to the start of the given period (year, month, day, hour, or minute).

Parameters

Name Type Description
value Date \| Temporal.* The date/time to truncate
periodName 'year' \| 'month' \| 'day' \| 'hour' \| 'minute' Period to truncate to
timezoneId string (optional) Only used for Temporal.ZonedDateTime/Temporal.Instant input; defaults to local timezone

Returns: Same type as value (Date in, Date out; Temporal.X in, Temporal.X out — except Temporal.Instant input returns a Temporal.Instant).

ft3.momentesque.startOf(new Date(), "day");
ft3.momentesque.startOf(Temporal.Now.zonedDateTimeISO(), "month");
ft3.momentesque.startOf(Temporal.Now.instant(), "hour", "Australia/Sydney");
ft3.momentesque.startOf(Temporal.PlainMonthDay.from("06-15"), "day");

Throws an Error if value is not a Date or recognized Temporal type.


add

Adds (or subtracts, with a negative num) a period to a legacy Date object. Month/year additions are day-clamped (e.g. adding 1 month to Jan 31 gives Feb 28, not Mar 3).

Parameters

Name Type Description
value Date The date to add to (not mutated — a new Date is returned)
num number Amount to add
periodName 'second' \| 'minute' \| 'hour' \| 'day' \| 'week' \| 'month' \| 'year' \| 'millisecond' Unit of num

Returns: Date

ft3.momentesque.add(new Date(), 5, "minute");
ft3.momentesque.add(new Date("2023-01-31"), 1, "month"); // -> Feb 28, 2023

Throws an Error if value is not a Date.

Only supports legacy Date objects — there is no Temporal equivalent in this include. Use native Temporal .add()/.subtract() methods directly for Temporal types.


subtract

Convenience wrapper around add with num negated.

ft3.momentesque.subtract(new Date(), 3, "day");

format

Formats a Date or Temporal object into a string using Moment-style format tokens.

Parameters

Name Type Description
dt Date \| Temporal.PlainDate \| Temporal.PlainDateTime \| Temporal.PlainTime \| Temporal.ZonedDateTime \| Temporal.Instant Value to format
tokenStr string Format string made of tokens below

Returns: string

ft3.momentesque.format(new Date(), "YYYY-MM-DD HH:mm:ss");
ft3.momentesque.format(Temporal.Now.zonedDateTimeISO(), "dddd, MMMM D, YYYY h:mm a");

Type handling: - Date is converted via local timezone to a Temporal.PlainDateTime — both date and time-of-day are preserved. - Temporal.Instant is converted to Temporal.ZonedDateTime in UTC. - Any other value must be a Temporal.PlainDateTime, Temporal.PlainDate, Temporal.PlainTime, or Temporal.ZonedDateTime — otherwise a TypeError is thrown.

See Format Tokens Reference for supported tokens (this function supports a wider set than parseWithFormat, including dddd/ddd weekday names and A/a for AM/PM).


isSame

Checks whether two date/time values represent the exact same value (via Temporal's .equals()).

Parameters

Name Type
dt1, dt2 Date \| Temporal.*

Returns: boolean

ft3.momentesque.isSame(dateA, dateB);

Date arguments are converted to Temporal.ZonedDateTime (local timezone) before comparison.

Temporal.ZonedDateTime.equals() requires matching timezone and calendar, not just the same instant — comparing two ZonedDateTimes in different timezones representing the same moment will return false.


isBefore

Checks whether dt1 is chronologically before dt2.

Parameters

Name Type Description
dt1 Date \| Temporal.*
dt2 Date \| Temporal.* (optional) Defaults to new Date() (now) if omitted

Returns: boolean

ft3.momentesque.isBefore(pastDate, futureDate); // true
ft3.momentesque.isBefore(pastDate);             // true, compared against now

Throws a TypeError if dt1 and dt2 resolve to different Temporal constructors after Date conversion.


isSameOrBefore

Convenience wrapper: isSame(dt1, dt2) || isBefore(dt1, dt2).

ft3.momentesque.isSameOrBefore(dateA, dateB);

isAfter

Checks whether dt1 is chronologically after dt2. Implemented as isBefore(dt2, dt1).

ft3.momentesque.isAfter(futureDate, pastDate); // true

isSameOrAfter

Convenience wrapper: isSame(dt1, dt2) || !isBefore(dt2, dt1).

ft3.momentesque.isSameOrAfter(dateA, dateB);

temporalToDate

Converts any Temporal object to a legacy Date, filling in sensible defaults for types missing date/timezone information.

Parameters

Name Type Description
value Temporal.* Any Temporal type except Duration
tz string (optional) Timezone to anchor to; defaults to local timezone

Returns: Date

Anchoring defaults for types missing information:

Type Missing Anchored to
PlainTime date Today's date (in tz)
PlainYearMonth day 1st of the month
PlainMonthDay year Current year
ft3.momentesque.temporalToDate(Temporal.Now.plainTimeISO());
ft3.momentesque.temporalToDate(Temporal.PlainYearMonth.from("2023-06"));
ft3.momentesque.temporalToDate(Temporal.PlainMonthDay.from("06-15"), "America/New_York");

Throws a TypeError for unrecognized types (e.g. Temporal.Duration, which has no meaningful Date equivalent).


diff

Computes the difference between two legacy Date objects in a given unit — mirrors Moment.js's .diff().

Parameters

Name Type Description
dt1 Date Minuend (later/reference date)
dt2 Date Subtrahend
unitName string (optional) Any Temporal duration unit, singular or plural (e.g. "day" or "days"). Defaults to "milliseconds"
fractional boolean (optional) If falsy, result is floored to an integer (matches Moment's default). If truthy, returns a fractional value

Returns: number

ft3.momentesque.diff(dateA, dateB, "days");          // integer days
ft3.momentesque.diff(dateA, dateB, "days", true);    // fractional days
ft3.momentesque.diff(dateA, dateB);                  // milliseconds

Both arguments are converted internally to Temporal.Instant, and the result uses .total() with relativeTo set to dt2's date, so calendar units (months/years) resolve correctly.

Throws a TypeError if dt1 is not a Date, or if dt1/dt2 are of different constructors.


Format Tokens Reference

Used by parseWithFormat and format. Longest tokens are always matched first (e.g. YYYY before YY, MMMM before MMM/MM/M).

Token Meaning parseWithFormat format
YYYY 4-digit year ✅ ✅
YY 2-digit year ✅ ✅
MMMM Full month name ✅ ✅
MMM 3-letter month abbreviation ✅ ✅
MM 2-digit month ✅ ✅
M Non-padded month ✅ ✅
DD 2-digit day ✅ ✅
D Non-padded day ✅ ✅
dddd Full weekday name — ✅
ddd 3-letter weekday abbreviation — ✅
HH 2-digit hour (24h) ✅ ✅
H Non-padded hour (24h) ✅ ✅
hh 2-digit hour (12h) ✅ ✅
h Non-padded hour (12h) ✅ ✅
mm 2-digit minute ✅ ✅
m Non-padded minute ✅ ✅
ss 2-digit second ✅ ✅
s Non-padded second ✅ ✅
SSS 3-digit millisecond ✅ ✅
a am/pm (lowercase) ✅ ✅
A AM/PM (uppercase) ✅ ✅
Z UTC marker (parsed as UTC before constructing the returned Date) ✅ —

Any character in the format string not matching a token is treated as a literal (spaces, commas, colons, etc.) and escaped automatically for regex safety in parseWithFormat.

Examples

#include "Momentesque",

// Momentesque — Sample Usage

// ---------------------------------------------------------------------------
// isValid
// ---------------------------------------------------------------------------

const isValid1 = ft3.momentesque.isValid("2023-06-15");                // true  — ISO date
const isValid2 = ft3.momentesque.isValid("2023-06-15T14:30:00Z");      // true  — ISO date-time, UTC
const isValid3 = ft3.momentesque.isValid("2023-06-15T14:30:00+10:00"); // true  — ISO date-time, offset
const isValid4 = ft3.momentesque.isValid(new Date());                  // true  — valid Date object
const isValid5 = ft3.momentesque.isValid(1723000000000);               // true  — valid epoch ms
const isValid6 = ft3.momentesque.isValid("June 15, 2023");             // false — non-ISO string, not accepted
const isValid7 = ft3.momentesque.isValid("not a date");                // false — garbage string
const isValid8 = ft3.momentesque.isValid(NaN);                         // false — not a Date/number/string


// ---------------------------------------------------------------------------
// parseWithFormat
// ---------------------------------------------------------------------------

const parsed1 = ft3.momentesque.parseWithFormat("14 Aug 1999, 7:04pm", "DD MMM YYYY, h:mma");
// -> Date, 1999-08-14 19:04:00 local time

const parsed2 = ft3.momentesque.parseWithFormat("August 14, 2023, 1:03PM", "MMMM D, YYYY, h:mmA");
// -> Date, 2023-08-14 13:03:00 local time

const parsed3 = ft3.momentesque.parseWithFormat("2026-08-06T01:03:05Z", "YYYY-MM-DDTHH:mm:ssZ");
// -> Temporal.Instant, 2026-08-06T01:03:05Z (explicitly UTC)

const parsed4 = ft3.momentesque.parseWithFormat("14 Aug 2023, 3:33:12.500pm", "DD MMM YYYY, h:mm:ss.SSSa");
// -> Date, 2023-08-14 15:33:12.500 local time

const parsed5 = ft3.momentesque.parseWithFormat("15 June 2023", "DD MMMM YYYY");
// -> Date, 2023-06-15 00:00:00 local time (no time tokens -> defaults to midnight)

const parsed6 = ft3.momentesque.parseWithFormat("12:00pm", "h:mma");
// -> Date, today's date at 12:00:00 local time (no date tokens -> defaults to today)

let parsed7 = null;
try {
    parsed7 = ft3.momentesque.parseWithFormat("2023/06/15", "YYYY-MM-DD"); // mismatched separators
} catch (err) {
    const parseErrorMessage = err.message; // "String "2023/06/15" does not match format "YYYY-MM-DD""
}


// ---------------------------------------------------------------------------
// startOf
// ---------------------------------------------------------------------------

const startOfDay = ft3.momentesque.startOf(new Date(), "day");
// -> Date, today at 00:00:00 local time

const startOfMonth = ft3.momentesque.startOf(new Date(), "month");
// -> Date, 1st of the current month at 00:00:00

const startOfMonthZdt = ft3.momentesque.startOf(Temporal.Now.zonedDateTimeISO(), "month");
// -> Temporal.ZonedDateTime, 1st of the current month, midnight, same timezone

const startOfHourInstant = ft3.momentesque.startOf(Temporal.Now.instant(), "hour", "Australia/Sydney");
// -> Temporal.Instant, current hour truncated, computed via Sydney's local clock

const startOfYearPlainDate = ft3.momentesque.startOf(Temporal.PlainDate.from("2023-06-15"), "year");
// -> Temporal.PlainDate, 2023-01-01

const startOfDayMonthDay = ft3.momentesque.startOf(Temporal.PlainMonthDay.from("06-15"), "day");
// -> Temporal.PlainMonthDay, 06-15 (already day-precision, unchanged)


// ---------------------------------------------------------------------------
// add / subtract
// ---------------------------------------------------------------------------

const addedMinutes = ft3.momentesque.add(new Date(), 5, "minute");
// -> Date, 5 minutes from now

const addedMonthClamped = ft3.momentesque.add(new Date("2023-01-31"), 1, "month");
// -> Date, 2023-02-28 (clamped — Feb has no 31st)

const addedYear = ft3.momentesque.add(new Date("2024-01-31"), 1, "year");
// -> Date, 2025-01-31 (not a leap year, but Jan always has 31 days so no clamping needed here)

const subtractedDays = ft3.momentesque.subtract(new Date(), 3, "day");
// -> Date, 3 days ago

const subtractedDayClamped = ft3.momentesque.subtract(new Date("2023-03-01"), 1, "day");
// -> Date, 2023-02-28


// ---------------------------------------------------------------------------
// format
// ---------------------------------------------------------------------------

const formattedDateTime = ft3.momentesque.format(new Date(), "YYYY-MM-DD HH:mm:ss");
// -> e.g. "2026-08-10 16:42:07"

const formattedLongDate = ft3.momentesque.format(new Date(), "dddd, MMMM D, YYYY");
// -> e.g. "Monday, August 10, 2026"

const formattedTime = ft3.momentesque.format(Temporal.Now.zonedDateTimeISO(), "h:mm a");
// -> e.g. "4:42 pm"

const formattedPlainDate = ft3.momentesque.format(Temporal.PlainDate.from("2023-06-15"), "DD/MM/YYYY");
// -> "15/06/2023"

const formattedInstantWithLiteral = ft3.momentesque.format(Temporal.Now.instant(), "YYYY-MM-DD HH:mm:ss [UTC]");
// -> e.g. "2026-08-10 06:42:07 [UTC]" (note: literal "UTC" text isn't
//    bracket-escaped by this implementation — see caveats)


// ---------------------------------------------------------------------------
// isSame / isBefore / isSameOrBefore / isAfter / isSameOrAfter
// ---------------------------------------------------------------------------

const dateA = new Date("2023-06-15T10:00:00");
const dateB = new Date("2023-06-15T10:00:00");
const dateC = new Date("2023-06-20T10:00:00");

const isSameAB = ft3.momentesque.isSame(dateA, dateB);          // true  — identical instants
const isSameAC = ft3.momentesque.isSame(dateA, dateC);          // false

const isBeforeAC = ft3.momentesque.isBefore(dateA, dateC);      // true
const isBeforeCA = ft3.momentesque.isBefore(dateC, dateA);      // false
const isBeforeNow = ft3.momentesque.isBefore(new Date("2020-01-01")); // true — compared against "now" (dt2 omitted)

const isSameOrBeforeAB = ft3.momentesque.isSameOrBefore(dateA, dateB); // true — same counts
const isSameOrBeforeAC = ft3.momentesque.isSameOrBefore(dateA, dateC); // true

const isAfterCA = ft3.momentesque.isAfter(dateC, dateA);        // true
const isAfterAC = ft3.momentesque.isAfter(dateA, dateC);        // false

const isSameOrAfterAB = ft3.momentesque.isSameOrAfter(dateA, dateB); // true — same counts
const isSameOrAfterCA = ft3.momentesque.isSameOrAfter(dateC, dateA); // true


// ---------------------------------------------------------------------------
// temporalToDate
// ---------------------------------------------------------------------------

const dateFromInstant = ft3.momentesque.temporalToDate(Temporal.Now.instant());
// -> Date, current instant

const dateFromZdt = ft3.momentesque.temporalToDate(Temporal.Now.zonedDateTimeISO());
// -> Date, current instant (same as above, via ZonedDateTime)

const dateFromPlainDate = ft3.momentesque.temporalToDate(Temporal.PlainDate.from("2023-06-15"));
// -> Date, 2023-06-15 midnight in local timezone

const dateFromPlainDateTime = ft3.momentesque.temporalToDate(Temporal.PlainDateTime.from("2023-06-15T14:30:00"));
// -> Date, 2023-06-15 14:30:00 in local timezone

const dateFromPlainTime = ft3.momentesque.temporalToDate(Temporal.Now.plainTimeISO());
// -> Date, today's date with the given time (PlainTime has no date of its own)

const dateFromYearMonth = ft3.momentesque.temporalToDate(Temporal.PlainYearMonth.from("2023-06"));
// -> Date, 2023-06-01 (anchored to the 1st, since PlainYearMonth has no day)

const dateFromMonthDay = ft3.momentesque.temporalToDate(Temporal.PlainMonthDay.from("06-15"));
// -> Date, <current year>-06-15 (anchored to this year, since PlainMonthDay has no year)

const dateFromPlainDateNY = ft3.momentesque.temporalToDate(Temporal.PlainDate.from("2023-06-15"), "America/New_York");
// -> Date, 2023-06-15 midnight, anchored to New York time specifically


// ---------------------------------------------------------------------------
// diff
// ---------------------------------------------------------------------------

const start = new Date("2023-06-10T06:00:00");
const end = new Date("2023-06-15T18:00:00");

const diffDaysInt = ft3.momentesque.diff(end, start, "days");
// -> 5 (floored/truncated integer days)

const diffDaysFractional = ft3.momentesque.diff(end, start, "days", true);
// -> 5.5 (fractional days)

const diffHours = ft3.momentesque.diff(end, start, "hours");
// -> 132

const diffMillis = ft3.momentesque.diff(end, start);
// -> milliseconds between the two dates (default unit)

const diffMonths = ft3.momentesque.diff(new Date("2023-07-15"), new Date("2023-06-15"), "months");
// -> 1 (calendar-aware — correctly resolves to exactly 1 month)