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
- parseWithFormat
- startOf
- add
- subtract
- format
- isSame
- isBefore
- isSameOrBefore
- isAfter
- isSameOrAfter
- temporalToDate
- diff
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)