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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cosyte/dates

v0.1.4

Published

Date and time utilities for the @cosyte/* healthcare parsers: precision-preserving conversion and validation that never guesses a timezone.

Readme

@cosyte/dates

Work with the dates the @cosyte/* parsers give you, without losing what the wire actually said.

npm version CI License: MIT Node

Date and time utilities for the @cosyte/* healthcare parsers: precision-preserving conversion and validation that never guesses a timezone.

Why this exists

Healthcare parsers hand back dates that are deliberately not JavaScript Date objects. An HL7 v2 |1970| is a year, |19880507| is a calendar day, and a value with no offset is the sender's local time by the standard's own words. The parsers preserve exactly that and refuse to invent the rest, which is why they carry no date library at all.

That fidelity leaves a gap: a developer still has to validate, compare, and convert those values, and the obvious tools all model an absolute instant. Reach for new Date('1988') and you have silently acquired a month, a day, a midnight, and a timezone that nobody sent you. This package is the opt-in companion that closes the gap without any of that, so the parsers stay zero-dependency and nobody has their date library chosen for them.

Status

The 0.1 line. The public API is settled and safe to depend on. The exported functions, the shape of DateParts, the issue codes, and the error classes are what this package intends to keep. While the version is below 1.0.0, a breaking change to any of them ships in a new minor version, never a patch, and the changelog says what broke and what to do instead.

Not covered on the 0.1 line, and named so the boundary is not a surprise:

  • No parsing of vendor date text. This package consumes parts that a @cosyte/* parser already produced. Format strings and vendor token grammars live in the parsers' profile systems, not here.
  • No two-digit years, ever. year must be four digits. There is no century window, no pivot, and no configuration that adds one.
  • No arithmetic, comparison, or formatting helpers. Convert with toTemporal() and use Temporal for the rest.
  • No locale-aware or human-readable output. toISO() renders ISO 8601 and nothing else.
  • Years 1000 to 9999 only, in the proleptic Gregorian calendar. No other calendar system, and no leap seconds.

Install

pnpm add @cosyte/dates

Node.js 22 or later, tested on 22, 24, and 26. ESM only: this package ships no CommonJS build, so require() will not load it.

It carries exactly one runtime dependency, temporal-polyfill, because Temporal.PlainDate is the only widely available type that can hold a calendar date with no time of day and no zone. Temporal is re-exported from this package, so a consumer works with the same objects rather than installing a second copy.

Usage

import { precisionOf, toISO, toInstant, toTemporal, validateParts } from "@cosyte/dates";

// A date of birth that arrived as a year and nothing more. It stays a year.
const birthYear = { year: 1988 };
console.log(toISO(birthYear), "| precision:", precisionOf(birthYear));

// A calendar date. No midnight is invented, and no zone is attached.
const admitted = { year: 1988, month: 5, day: 7 };
console.log(toISO(admitted), "| Temporal:", toTemporal(admitted).constructor.name);

// A timestamp whose sender stated its offset. The offset is kept exactly.
const collected = { year: 1988, month: 5, day: 7, hour: 13, minute: 45, offsetMinutes: 330 };
console.log(toISO(collected), "->", toInstant(collected).toString());

// The same timestamp with an unknown offset. Nothing guesses one.
const unknownZone = { year: 1988, month: 5, day: 7, hour: 13, minute: 45 };
try {
  toInstant(unknownZone);
} catch (error) {
  console.log(error.name);
}

// A date that does not exist is refused, and says which rule it broke.
const result = validateParts({ year: 2025, month: 2, day: 29 });
console.log(result.valid ? "valid" : result.issues[0].message);
1988 | precision: year
1988-05-07 | Temporal: PlainDate
1988-05-07T13:45+05:30 -> 1988-05-07T08:15:00Z
MissingZoneError
day 29 does not exist: February 2025 has 28 days, because 2025 is not a leap year in the proleptic Gregorian calendar

That output is identical under every host TZ, and test/readme.test.ts runs this exact block against the built package and compares it character for character.

More runnable programs, one for each job this package does, are in examples/, and the full guide is at docs.cosyte.com/dates.

PHI and safety

A date of birth is PHI, and a wrong conversion is the quiet kind of defect: it returns a plausible date rather than an error, and a consumer that has already written it into a record does not learn of it from a later fix. What this package does about that:

  • It writes nothing, anywhere. No logging, no telemetry, no files, no network. Nothing is written to stdout or stderr, on any code path including every error path. test/package-contract.test.ts proves it by running every public entry point in a child process and asserting both streams are empty and that nothing was written to the working directory.
  • It retains nothing. Every function is pure and holds no state between calls. A value passed in is not cached, memoized, or referenced after return.
  • It refuses rather than guesses. No century window, no host timezone fallback, no midnight for a date with no time, no picking one side of a daylight-saving transition, no rounding a sub-second value to make it fit. Each of those raises a named error instead.
  • Diagnostics quote one component, never the whole value. A validation message names the component at fault and the value of THAT component (month must be an integer in 1 to 12 ...; received the number 13). It never assembles the full date into an error string.

What the consumer still owns: whether to log an error message that may contain one component of a patient's date of birth, how the parts got there, and where the converted value goes next. This package is a pure function in the middle.

API

| export | what it does | | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | validateParts(value) | Checks a value against the contract. Never throws. Returns every fault, not just the first. | | isValidParts(value) | The same check as a type guard. | | assertValidParts(value) | Returns the recognized components, or throws DatePartsError. | | precisionOf(value) | The least significant present component: 'year' through 'fraction'. | | toISO(value) | ISO 8601 at the value's own precision. 1988, 1988-05, 1988-05-07T13:45+05:30. | | toTemporal(value) | The narrowest Temporal type that carries everything the value states: PlainYearMonth, PlainDate, PlainDateTime, or ZonedDateTime when the value carries an offset. | | toZonedDateTime(value, options?) | The value placed in a stated zone. Needs a time of day and a zone. | | toInstant(value, options?) | The absolute instant, same requirements. | | Temporal | The Temporal implementation this package converts into. |

Errors: DatePartsError (carries the same issues the validator reports), MissingZoneError, PrecisionError, AmbiguousLocalTimeError.

The caveats that bite:

  • A value's OWN offsetMinutes always wins. options apply only when the offset is unknown, so passing a timeZone never re-zones data the sender already qualified.
  • toTemporal() throws for a year-only value, because Temporal has no year-only type and this package will not invent a month to reach one.
  • An absent offsetMinutes means UNKNOWN. It does not mean UTC, and it does not mean the host zone.

Compatibility

The input shape is the spec-native parts value the @cosyte/* parsers produce: month is 1 to 12, not JavaScript's 0 to 11, and components run from year downward with no gaps. Handled: year, month, day, hour, minute, second, sub-second down to one nanosecond, and a signed offset in minutes.

fraction must be a whole number of nanoseconds, which is nine decimal places of a second and Temporal's own floor. A value carrying finer detail is REFUSED with finer-than-nanosecond rather than rounded to fit, because a silent round is the class of change this package exists to not make.

Known NOT handled, deliberately: two-digit years, leap seconds, calendars other than the proleptic Gregorian, years outside 1000 to 9999, sub-second detail finer than one nanosecond, an offset on a value that states no time of day, and any zone abbreviation such as CST (they are ambiguous across regions, so an IANA identifier or a numeric offset is required).

Support

Questions and bug reports go in GitHub issues. Use synthetic values only: a date of birth or an admission time from a real patient is PHI, so reduce a report to made-up parts that reproduce it. For help with an integration, see our integration services.

Security

Report a vulnerability privately, never in a public issue, and with no real patient data in it. Our security policy says how. A conversion that silently moves a value, rather than refusing, is worth reporting that way.

Contributing

Issues and pull requests are welcome at github.com/cosyte/dates. A contribution has to clear the same gate CI runs on every push; AGENTS.md lists the commands. New behavior needs a test, and anything touching conversion needs one that pins the exact output rather than a shape.

License

MIT, copyright Cosyte LLC. See LICENSE. The standards this package names belong to their owners; see TRADEMARKS.md.