NPM Package v1.0.0Zero DependenciesTypeScript First

Dates without timezone surprises.

DaySafe is a lightweight, zero-dependency TypeScript library for safe, predictable calendar date operations. Pure civil arithmetic, strict validation, and branded types.

$npm install daysafe
hero-demo.ts
Live daysafe 1.0.0
import { addDays, daysBetween } from "daysafe";
// Interactive inputs:
const baseDate = ;
const days = ;
const nextDate = addDays(baseDate, days);
const diff = daysBetween(baseDate, nextDate);
Console OutputNo Timezone Skew
console.log(nextDate):"2026-11-09"
console.log(diff):7
The Architectural Trap

JavaScript dates are not always calendar dates.

The native JavaScript Date object was designed for millisecond timestamps. When applications use timestamps to store dates of birth, invoices, or hotel reservations, timezone boundaries inevitably distort the data.

1Off-By-One Day Timezone Bug

Parsing ISO date strings shifts backward by 1 full day in Western timezones.

Standard JS DateUnsafe
// In New York (America/New_York, UTC-5):
const d = new Date("2026-11-02");
console.log(d.getDate()); // 1 (November 1st!)
DaySafe SolutionPredictable
// With DaySafe in any timezone:
const d = parseDate("2026-11-02");
console.log(d); // "2026-11-02"

2DST Transition Distortions

Subtracting timestamps and dividing by 86,400,000 yields fractional days on 23h/25h days.

Standard JS DateUnsafe
// Spring DST transition (23-hour day):
const diff = (t2 - t1) / (1000 * 60 * 60 * 24);
console.log(diff); // 1.9583 days (breaks billing!)
DaySafe SolutionPredictable
// Exact civil calendar day arithmetic:
const diff = daysBetween("2026-03-07", "2026-03-09");
console.log(diff); // 2 (exact integer days)

3Silent Rollover on Invalid Dates

JavaScript Date silently normalizes impossible days like Feb 30 into March 2.

Standard JS DateUnsafe
// Invalid calendar day silently accepted:
new Date(2026, 1, 30);
// Becomes March 2nd without warning!
DaySafe SolutionPredictable
// Strict Gregorian calendar validation:
parseDate("2026-02-30");
// Returns null (rejected immediately)

4Legacy Two-Digit Year Quirks

Date.UTC(25, 0, 1) silently treats year 25 as 1925 instead of civil year 0025.

Standard JS DateUnsafe
// Legacy quirk for years 0-99:
new Date(Date.UTC(25, 0, 1)).getUTCFullYear();
// 1925 (Unexpected century mapping!)
DaySafe SolutionPredictable
// Uniform handling across years 0001 - 9999:
fromDateParts({ year: 25, month: 1, day: 1 });
// "0025-01-01" (pure civil year)
The DaySafe Model

Treat dates like dates.

DaySafe eliminates timestamp conversion entirely. By modeling dates strictly as Gregorian calendar values, your application gains 100% predictable arithmetic and airtight type safety.

Strict YYYY-MM-DD Strings

Canonical ISO 8601 calendar date representation without time or timezone offset ambiguity.

Branded DateOnly Type

Compile-time nominal typing prevents arbitrary unvalidated strings from passing into critical calculations.

Integer Civil Calendar Math

Based on Howard Hinnant's proven calendar algorithms for pure integer day-number arithmetic.

Predictable Month Clamping

Adding 1 month to January 31 safely resolves to February 28 (or 29 in leap years), never overflowing into March.

Timezone Independence

Calculations occur on civil calendar units. Running in Tokyo, London, or San Francisco yields identical answers.

UTC-Safe Locale Formatting

Native Intl.DateTimeFormat locked to UTC calendar day so localized strings never shift day boundaries.

Verified Capabilities

Engineered for bulletproof calendar math.

Every feature is verified in the source code, backed by strict unit tests, and guaranteed zero runtime dependencies.

Timezone-Independent

Operations execute on calendar dates without local machine timezone offsets or UTC conversions ever shifting the date.

Strict Date Validation

Validates YYYY-MM-DD for Gregorian years 0001 to 9999. Rejects timestamps, non-strings, year 0000, and impossible days like Feb 30.

Date-Only Branded Types

Nominal DateOnly branded strings prevent unvalidated strings from being passed into calculations without validation.

Accurate Leap-Year Support

Full Gregorian century rules: 2024 is a leap year, 1900 is not, and 2000 is a leap year. Accurate February 29 handling.

Predictable Month Math

Safe month-end clamping prevents dates from silently spilling into the following month (e.g. Jan 31 + 1 mo = Feb 28).

DST-Independent Differences

Guaranteed exact signed integer day differences without timestamp division fractional errors on 23-hour or 25-hour DST days.

Locale-Aware Formatting

Leverages native Intl.DateTimeFormat locked to UTC, ensuring localized format strings never cross day boundaries.

Zero Runtime Dependencies

Exactly zero dependencies in production. Lightweight, audit-friendly, and minimal footprint in your application bundle.

Dual ESM & CommonJS

Pre-bundled with tsup into modern ECMAScript Modules (.mjs) and CommonJS (.js) with comprehensive .d.ts and .d.mts declarations.

Interactive Preview

Try DaySafe in real-time.

Switch operations and tweak parameters below to see actual in-browser calculations and generated code.

Open Full 3-Panel Playground
Tip: Enter 2026-02-30 into parseDate to watch DaySafe strictly reject non-existent calendar days without throwing an unhandled exception.
Computed Result:
"2026-02-28"
Generated Code:
import { addMonths } from "daysafe"; const result = addMonths("2026-01-31", 1); // "2026-02-28"
All 23 functions supported with custom input fieldsLaunch Full Playground →
Developer Ergonomics

Designed for TypeScript purists.

No heavy class instances, no monkey-patching prototypes, no global state. Just pure functions, branded types, and instant autocompletion.

Install and import in seconds.

DaySafe distributes dual ESM and CommonJS modules with pre-generated declaration files. Bundlers like Vite, Next.js, Rollup, and esbuild tree-shake everything with zero extra configuration.

✓

Pure Functional Design

All operations accept inputs and return new values without mutating dates.

✓

Zero Runtime Dependencies

No external packages, no risk of supply-chain vulnerabilities.

✓

Complete Autocomplete & TSDoc

Full descriptions, parameters, throws, and code examples visible right in your IDE.

developer-experience.ts
400 font-semibold">import { addDays, isValidDate, parseDate } 400 font-semibold">from 400 font-semibold">class="text-emerald-400">"daysafe";
// 1. Strict parsing with narrowing
400 font-semibold">const userInput: unknown = 400 font-semibold">class="text-emerald-400">"2026-11-02";
400 font-semibold">if (isValidDate(userInput)) {
// userInput is automatically typed as DateOnly!
400 font-semibold">const nextWeek = addDays(userInput, 7);
console.log(nextWeek); 400 font-semibold">class=400 font-semibold">class="text-emerald-400">"text-slate-500 italic">// 400 font-semibold">class="text-emerald-400">"2026-11-09"
}
// 2. Direct string usage is also allowed:
400 font-semibold">const end = addDays(400 font-semibold">class="text-emerald-400">"2026-11-02", 30);
console.log(end); 400 font-semibold">class=400 font-semibold">class="text-emerald-400">"text-slate-500 italic">// 400 font-semibold">class="text-emerald-400">"2026-12-02"

Built for real-world software.

From regulated enterprise fintech to consumer booking platforms, calendar dates are mission-critical.

Browse Complete Code Recipes

Booking & Hospitality

Calculate exact night counts between check-in and check-out dates without DST transitions causing 1.95 fractional day errors.

Date of Birth & Age Verification

Strictly validate birthdays, verify 18+ legal age thresholds, and safely handle leap-day (Feb 29) birthdays without silent rollover.

Billing & Invoicing

Compute exact Net-30 payment due dates, roll weekend deadlines to Monday, and print timezone-immune invoice dates.

Subscription Scheduling

Add monthly billing cycles with end-of-month clamping (Jan 31 rolls to Feb 28, not March 3).

Examination Platforms

Ensure exam schedules, application windows, and submission deadlines fall on identical calendar dates worldwide.

Database DATE Serialization

Bind directly to PostgreSQL, MySQL, and SQLite DATE columns without the JavaScript Date timestamp timezone offset shifts.

Calendar UI Components

Generate 42-cell month grids with startOfMonth, endOfMonth, and getDayOfWeek in pure civil time.

Architecture & Design

How DaySafe processes dates.

By bypassing JavaScript timestamp conversions entirely, DaySafe ensures your calendar calculations remain deterministic, auditable, and timezone-free.

1. Input Layer

DateInput

DateOnly | string

Accepts either branded DateOnly values or standard ISO 8601 calendar strings ('YYYY-MM-DD').

2. Validation Engine

Strict Validation

ECMAScript Spec

Validates year range (0001 - 9999), month bounds (1-12), and leap-year rules. Rejects timestamps and non-dates.

3. Calendar Arithmetic

Civil Day Calculations

Howard Hinnant Algorithms

Transforms calendar components into civil day integers for exact math, free of timezone and DST interference.

4. Output Layer

DateOnly Result

Branded String

Returns immutable, nominal DateOnly string or formatted output with native Intl locked to UTC.

Ready for Production

Make calendar dates predictable.

Eliminate timezone offsets, DST clock distortion, and silent invalid date rollovers. Start using DaySafe today with zero runtime dependencies.

npm install daysafe