
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
strict ISO 8601 timestamps, date stamps, and durations with runtime validation
A glossary of intuitive, universally unambiguous time, date, and duration domain literals.
declare a universally unambiguous serialization format for dates, times, and datetimes via strict ISO 8601 types with runtime validation.
the core insight: time types are either stamps or floats
etymology:
Stamp — matches PostgreSQL timestamp semantics; a time "stamped" into historyFloat — a time that floats, not anchored; could land on many different instantsusecases:
const since: IsoDateStamp = '2024-12-15')const birthday: IsoDateStamp = asIsoDateStamp(new Date()))if (!isIsoDateStamp(since)) throw new Error('wrong format'))stamps (absolute instants)
IsoTimeStamp = yyyy-MM-ddTHH:mm:ssZ (e.g., 2024-06-15T14:30:00Z)IsoDateStamp = yyyy-MM-dd (e.g., 2024-06-15)IsoMonthStamp = yyyy-MM (e.g., 2024-06)IsoYearStamp = yyyy (e.g., 2024)floats (detached patterns)
IsoTimeFloat = HH:mm:ss (e.g., 14:30:00)IsoHourFloat = HH (e.g., 14)IsoMinuteFloat = mm (e.g., 30)IsoMonthFloat = MM (e.g., 06)IsoDayFloat = dd (e.g., 15)IsoWeekdayFloat = i (e.g., 1 for Monday)ranges (time spans)
IsoTimeStampRange = { since: IsoTimeStamp; until: IsoTimeStamp }IsoDateStampRange = { since: IsoDateStamp; until: IsoDateStamp }durations (time quantities)
IsoDurationWords = iso 8601 string format with compile-time validation (e.g., 'P1Y', 'PT30S', 'P1DT2H')IsoDurationShape = structured object format (e.g., { years: 1 }, { seconds: 30 }, { days: 1, hours: 2 })IsoDuration = union of both formats — use whichever is more convenientnpm install iso-time
import { asIsoTimeStamp, asIsoDateStamp } from 'iso-time';
const timestamp = asIsoTimeStamp(new Date()); // '2024-06-15T14:30:00Z'
const datestamp = asIsoDateStamp(new Date()); // '2024-06-15'
import { isIsoTimeStamp, isIsoDateStamp } from 'iso-time';
isIsoTimeStamp('2024-06-15T14:30:00Z'); // true
isIsoTimeStamp('2024-06-15'); // false
isIsoDateStamp('2024-06-15'); // true
isIsoDateStamp('06/15/2024'); // false
import type { IsoTimeStamp, IsoDateStamp } from 'iso-time';
const occurredAt: IsoTimeStamp = asIsoTimeStamp(new Date());
const birthday: IsoDateStamp = asIsoDateStamp('1990-01-15');
import { asIsoHourFloat, asIsoMinuteFloat, asIsoWeekdayFloat } from 'iso-time';
const hour = asIsoHourFloat(new Date()); // '14'
const minute = asIsoMinuteFloat(new Date()); // '30'
const weekday = asIsoWeekdayFloat(new Date()); // '1' (Monday)
import { asIsoTimeStampRange, asIsoDateStampRange } from 'iso-time';
const standup = asIsoTimeStampRange({
since: new Date('2024-06-15T10:00:00Z'),
until: new Date('2024-06-15T11:00:00Z'),
});
const vacation = asIsoDateStampRange({
since: new Date('2024-07-01'),
until: new Date('2024-07-14'),
});
durations can be expressed in two formats:
'P1Y', 'PT30S', 'P1DT2H30M'){ years: 1 }, { seconds: 30 })note
IsoDurationShape is easier to work with programmatically, so all operations return it by default.IsoDurationWords can be convenient for inputs but is rarely convenient for manipulation.import type { IsoDuration, IsoDurationWords, IsoDurationShape } from 'iso-time';
// string format — concise, readable, compile-time validated
const cacheTtl: IsoDurationWords = 'PT5M'; // 5 minutes
const invoicePeriod: IsoDurationWords = 'P1M'; // 1 month
const trialPeriod: IsoDurationWords = 'P14D'; // 14 days
const sessionTimeout: IsoDurationWords = 'PT30M'; // 30 minutes
// object format — easy to manipulate programmatically
const timeout: IsoDurationShape = { seconds: 30 };
const workday: IsoDurationShape = { hours: 8 };
const sprint: IsoDurationShape = { weeks: 2 };
// union type — accepts either format
const flexible: IsoDuration = 'P1D'; // string ✓
const alsoFlexible: IsoDuration = { days: 1 }; // object ✓
IsoDurationWords catches invalid iso 8601 strings at compile time:
import type { IsoDurationWords } from 'iso-time';
// ✅ valid — compiles
const good1: IsoDurationWords = 'P1Y';
const good2: IsoDurationWords = 'PT30S';
const good3: IsoDurationWords = 'P1DT2H30M';
// ❌ invalid — compile error
const bad1: IsoDurationWords = '1 day'; // not iso format
const bad2: IsoDurationWords = 'P'; // lacks components
const bad3: IsoDurationWords = 'T30S'; // lacks P prefix
const bad4: IsoDurationWords = { days: 1 }; // wrong type (object)
import { toMilliseconds } from 'iso-time';
// both formats work
toMilliseconds({ hours: 1 }); // 3600000
toMilliseconds('PT1H'); // 3600000
toMilliseconds({ days: 1 }); // 86400000
toMilliseconds('P1D'); // 86400000
import { addDuration, subDuration, getDuration } from 'iso-time';
const later = addDuration(asIsoTimeStamp(new Date()), { hours: 2 });
const earlier = subDuration(asIsoTimeStamp(new Date()), { days: 1 });
const earlierStill = subDuration(asIsoTimeStamp(new Date()), 'P3D');
const elapsed = getDuration({
of: { range: { since: '2024-06-15T10:00:00Z', until: '2024-06-15T12:30:00Z' } },
}); // { hours: 2, minutes: 30 }
import { sumDurations } from 'iso-time';
// spread syntax
const total = sumDurations({ hours: 1 }, { minutes: 30 }, { seconds: 45 });
// { hours: 1, minutes: 30, seconds: 45 }
// array syntax
const durations = [{ hours: 2 }, 'PT30M', { minutes: 15 }];
const combined = sumDurations(durations);
// { hours: 2, minutes: 45 }
// get sum in a specific unit
const totalMinutes = sumDurations(durations, { as: 'minutes' });
// { minutes: 165 }
import { sleep } from 'iso-time';
await sleep('PT5S');
await sleep({ seconds: 5 });
await sleep({ milliseconds: 100 });
import { now, today } from 'iso-time';
const timestamp = now(); // '2024-06-15T14:30:00Z'
const datestamp = today(); // '2024-06-15'
import { startDurationStopwatch } from 'iso-time';
const stopwatch = startDurationStopwatch({ for: 'api call' }, { log: console });
await fetchData();
const { duration } = stopwatch.stop(); // logs elapsed time
import type {
IsoTimeStamp, IsoDateStamp, IsoTimeFloat,
IsoWeekdayFloat, IsoDuration
} from 'iso-time';
// when exactly the event occurs (absolute)
interface ScheduledEvent {
createdAt: IsoTimeStamp; // '2024-01-15T14:30:00Z'
occursOn: IsoDateStamp; // '2024-03-20'
}
// detached schedule pattern (float)
interface DailySchedule {
since: IsoTimeFloat; // '09:00:00' - every day at 9am
until: IsoTimeFloat; // '10:00:00'
}
// scheduled standup with date range and weekday pattern
interface ScheduledStandup {
repeats: {
since: IsoDateStamp; // '2024-01-15' - start date
until: IsoDateStamp; // '2024-12-31' - end date
};
occurs: {
days: IsoWeekdayFloat[]; // ['1', '3', '5'] - mon, wed, fri
at: IsoTimeFloat; // '09:00:00'
for: IsoDuration; // { minutes: 15 } or 'PT15M'
};
}
import type { IsoMonthStamp, IsoDayFloat, IsoMonthFloat } from 'iso-time';
// bill period (absolute)
interface Invoice {
period: IsoMonthStamp; // '2024-01' - January 2024 invoice
}
// bill schedule (float)
interface BillSchedule {
dueOn: IsoDayFloat; // '15' - due on 15th of each month
renewsOn: IsoMonthFloat; // '01' - annual renewal in January
}
type-safe rental periods with compile-time validated durations:
import type { IsoDurationWords, IsoDuration, IsoDurationShape, IsoTimeStamp } from 'iso-time';
import { addDuration, getDuration, now } from 'iso-time';
// rental options — iso strings validated at compile time
interface RentalOption {
label: string;
duration: IsoDurationWords;
pricePerDay: number;
}
const excavatorRentals: RentalOption[] = [
{ label: 'half day', duration: 'PT4H', pricePerDay: 150 },
{ label: 'full day', duration: 'P1D', pricePerDay: 250 },
{ label: 'weekend', duration: 'P2D', pricePerDay: 200 },
{ label: 'week', duration: 'P7D', pricePerDay: 175 },
// ❌ compile error — '3 days' is not valid iso 8601
// { label: 'bad', duration: '3 days', pricePerDay: 180 },
];
// rental agreement with typed durations
interface RentalAgreement {
equipment: string;
pickedUpAt: IsoTimeStamp;
duration: IsoDuration; // accepts both formats
gracePeriod: IsoDuration; // 'PT1H' — 1 hour grace
}
// compute when equipment is due back
const computeDueAt = (input: { agreement: RentalAgreement }): IsoTimeStamp => {
const dueAt = addDuration(input.agreement.pickedUpAt, input.agreement.duration);
return addDuration(dueAt, input.agreement.gracePeriod);
};
// check if rental is overdue
const computeOverdueHours = (input: {
agreement: RentalAgreement;
returnedAt: IsoTimeStamp;
}): number => {
const dueAt = computeDueAt({ agreement: input.agreement });
const overdue = getDuration({
of: { range: { since: dueAt, until: input.returnedAt } },
as: 'hours',
});
return overdue.hours;
};
// check how much to refund, minus early return fee
const computeRefundDuration = (input: {
agreement: RentalAgreement;
returnedAt: IsoTimeStamp;
}): IsoDurationShape => {
const dueAt = computeDueAt({ agreement: input.agreement });
const hoursUnused = getDuration({
of: { range: { since: input.returnedAt, until: dueAt } },
as: 'hours',
}).hours;
const hoursRefundable = hoursUnused * 0.8; // 20% early return fee
return { hours: hoursRefundable }; // object format — value is computed
};
FAQs
strict ISO 8601 timestamps, date stamps, and durations with runtime validation
The npm package iso-time receives a total of 1,453 weekly downloads. As such, iso-time popularity was classified as popular.
We found that iso-time demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.