LifeSpan
Know Your Time
People Google "how many days have I been alive" millions of times a month. Every result is a 2009-era tool with ads, low contrast, and zero personality. LifeSpan is the answer those searches deserve — fast, accurate, shareable, and genuinely useful beyond a single number.
Features
Precise Age
Years, months, days, hours — all computed live with no rounding.
Life Progress
A progress bar benchmarked to 80 years showing how far you've come.
Milestones
Ages 18–70 with past/upcoming/future status and days until each.
Remaining Timeline
A year-by-year grid of your life — every block is one year.
Birthday Countdown
Live ticking countdown to your next birthday, leap-day safe.
Shareable URLs
Every birth date gets a permanent URL with a dynamic OG image.
Tech stack
Next.js 15
App Router, RSC, edge-ready
Tailwind CSS
Utility-first, no runtime
next-themes
Dark/light mode, SSR-safe
TypeScript
All date logic, zero deps
Vercel
Edge functions, OG images
API Reference
All calculation logic lives in lib/ as pure TypeScript functions — no framework dependencies, fully importable in any Node.js or edge environment.
lib/agecalculateAge
calculateAge(birthDate: string | Date, referenceDate?: string | Date): AgeResult
Returns exact age broken down into years, months, days, and total months.
Example
import { calculateAge } from "@/lib/age";
const result = calculateAge("1990-05-14");
if (result.isValid) {
console.log(result.years); // e.g. 34
console.log(result.months); // e.g. 7
console.log(result.days); // e.g. 3
console.log(result.totalMonths); // e.g. 415
}Success response
{
isValid: true,
birthDate: "1990-05-14",
referenceDate: "2025-01-17",
years: 34,
months: 7,
days: 3,
totalMonths: 415
}Error response
{
isValid: false,
error: "FUTURE_DATE" | "INVALID_DATE",
message: "Birth date cannot be in the future."
}calculateDaysAlive
calculateDaysAlive(birthDate: string | Date, referenceDate?: string | Date): DaysAliveResult
Returns the total number of days elapsed since birth.
Example
import { calculateDaysAlive } from "@/lib/age";
const result = calculateDaysAlive("1990-05-14");
if (result.isValid) {
console.log(result.daysAlive); // e.g. 12666
}Success response
{
isValid: true,
birthDate: "1990-05-14",
referenceDate: "2025-01-17",
daysAlive: 12666
}calculateWeeksAlive
calculateWeeksAlive(birthDate: string | Date, referenceDate?: string | Date): WeeksAliveResult
Returns total complete weeks alive and leftover days.
Example
import { calculateWeeksAlive } from "@/lib/age";
const result = calculateWeeksAlive("1990-05-14");
if (result.isValid) {
console.log(result.weeksAlive); // e.g. 1809
console.log(result.remainingDays); // e.g. 3
}Success response
{
isValid: true,
birthDate: "1990-05-14",
referenceDate: "2025-01-17",
weeksAlive: 1809,
remainingDays: 3
}calculateHoursAlive
calculateHoursAlive(birthDate: string | Date, referenceDate?: string | Date): HoursAliveResult
Returns total hours alive (days × 24, no partial hours).
Example
import { calculateHoursAlive } from "@/lib/age";
const result = calculateHoursAlive("1990-05-14");
if (result.isValid) {
console.log(result.hoursAlive); // e.g. 303984
}Success response
{
isValid: true,
birthDate: "1990-05-14",
referenceDate: "2025-01-17",
hoursAlive: 303984
}lib/milestonescalculateMilestones
calculateMilestones(birthDate: string | Date, referenceDate?: string | Date): MilestoneTimelineResult
Returns milestone dates for ages 18, 21, 25, 30, 40, 50, 60, 70 — each tagged as Passed, Upcoming, or Future.
Example
import { calculateMilestones } from "@/lib/milestones";
const result = calculateMilestones("1990-05-14");
if (result.isValid) {
result.milestones.forEach((m) => {
console.log(m.age, m.dateReached, m.status);
// 18 "2008-05-14" "Passed"
// 25 "2015-05-14" "Passed"
// 35 "2025-05-14" "Upcoming"
// 40 "2030-05-14" "Future"
});
}Success response
{
isValid: true,
birthDate: "1990-05-14",
referenceDate: "2025-01-17",
milestones: [
{ age: 18, dateReached: "2008-05-14", status: "Passed" },
{ age: 21, dateReached: "2011-05-14", status: "Passed" },
{ age: 25, dateReached: "2015-05-14", status: "Passed" },
{ age: 30, dateReached: "2020-05-14", status: "Passed" },
{ age: 40, dateReached: "2030-05-14", status: "Upcoming" },
...
]
}lib/factscalculateFunFacts
calculateFunFacts(birthDate: string | Date, referenceDate?: string | Date): FunFactsResult
Returns biological estimates — heartbeats, breaths, sleep hours — and the weekday you were born on.
Example
import { calculateFunFacts } from "@/lib/facts";
const result = calculateFunFacts("1990-05-14");
if (result.isValid) {
console.log(result.estimatedHeartbeats); // e.g. 1,274,788,800
console.log(result.estimatedBreaths); // e.g. 291,149,568
console.log(result.estimatedSleepHours); // e.g. 101,328
console.log(result.birthWeekday); // e.g. "Monday"
}Success response
{
isValid: true,
birthDate: "1990-05-14",
referenceDate: "2025-01-17",
estimatedHeartbeats: 1274788800,
estimatedBreaths: 291149568,
estimatedSleepHours: 101328,
birthWeekday: "Monday",
assumptions: {
heartRateBpm: 70,
breathsPerMinute: 16,
sleepHoursPerDay: 8
}
}Error handling pattern
Every function returns a discriminated union — always check isValid before accessing result fields. Errors carry a typed error code ("INVALID_DATE" or "FUTURE_DATE") plus a human-readable message.
const result = calculateAge(userInput);
if (!result.isValid) {
// result.error → "INVALID_DATE" | "FUTURE_DATE"
// result.message → human-readable string
showError(result.message);
return;
}
// Safe to access result.years, result.months, etc.Shareable URL pattern
Any valid ISO date maps to a permanent page with a dynamic OG image generated at the CDN edge.
// Page route
https://lifespan.app/age/1990-05-14
// Dynamic OG image (auto-generated)
https://lifespan.app/age/1990-05-14/opengraph-imageYour data stays yours
LifeSpan stores nothing. All calculations happen entirely in your browser. Your birth date is never sent to any server — it only appears in the URL if you choose to share it.