Age Calculator in Clojure: Calculate Age from Date of Birth
12 min read

Age Calculator in Clojure: Calculate Age from Date of Birth

Clojure is a JVM language, so an age calculator in Clojure can use the modern Java date and time API directly. The most useful classes are java.time.LocalDate for a date of birth and target date, and java.time.Period for a calendar difference expressed in years, months, and days. This gives Clojure a concise way to calculate completed age without treating a year as a fixed number of days.

Clojure programming language logo for age calculator
Clojure programming language logo from Wikimedia Commons; the source file is identified as public domain.

Table of Contents

Quick answer: age calculator in Clojure

A simple Clojure age calculator can import java.time.LocalDate and java.time.Period. Create the DOB with (LocalDate/of 1990 5 17), obtain today with (LocalDate/now), and calculate the calendar period with (Period/between dob today). The completed age in years is (.getYears period).

(import (java.time LocalDate Period))\n\n(def dob (LocalDate/of 1990 5 17))\n(def today (LocalDate/now))\n(def age (Period/between dob today))\n\n(println "Age:" (.getYears age))

LocalDate represents a date without a time zone, which matches the usual meaning of a birthday. Java documents it as an immutable date object in the ISO calendar system, and Period represents a date-based amount in years, months, and days. Clojure can call these Java APIs directly because it is hosted on the JVM.

Why Clojure works well for age calculations

Clojure has direct Java interoperability, so date calculations do not require a Clojure-specific date model when the JVM date-time API already provides the needed calendar operations. The Clojure documentation demonstrates importing java.time.LocalDate and calling methods such as .plusDays. The same interop mechanism works with Period, DateTimeFormatter, ZoneId, and related classes.

For an age calculator, this approach is useful because the date-of-birth calculation is primarily a calendar problem. LocalDate intentionally has no time-of-day or time-zone component, while Period is designed for date-based quantities such as 25 years, 4 months, and 12 days.

Clojure date classes for an age calculator

Java typeClojure useAge-calculator role
LocalDateImported Java classStore DOB and target calendar dates
PeriodPeriod/betweenGet years, months, and days between dates
DateTimeFormatterFormatting/parsingRead and display ISO-style DOB values
YearLeap-year checksTest whether a year is a leap year
ZoneIdUsed with zoned date-time APIsConvert timestamps to the intended local date

The first two types are enough for a basic calculator. The other classes become useful when the application accepts text input, needs explicit leap-year logic, or receives timestamps that must be converted into a calendar date for a particular time zone.

Create a date of birth in Clojure

Use LocalDate/of when the year, month, and day are already separate values. Months use the normal Java range of 1 through 12.

(import (java.time LocalDate))\n\n(def dob (LocalDate/of 1990 5 17))\n\n(println (.getYear dob))\n(println (.getMonthValue dob))\n(println (.getDayOfMonth dob))

An invalid calendar date, such as February 30, causes the Java date API to reject the value instead of silently storing an unrelated date. That behavior is useful when a DOB comes from a form and should be validated before the age calculation continues.

Get today’s date in Clojure

(def today (LocalDate/now))\n\n(println today)

LocalDate/now uses the system clock and the default time zone to obtain the current local date. For most ordinary birthday calculators that is convenient. For tests, historical calculations, or a service serving users in multiple regions, passing an explicit target date is more deterministic.

Calculate completed age in years

The simplest Clojure implementation delegates the calendar difference to Period/between and reads its years component.

(defn completed-age [dob target]\n  (.getYears (Period/between dob target)))\n\n(completed-age\n  (LocalDate/of 1990 5 17)\n  (LocalDate/of 2026 9 22))

The result is the number of completed calendar years represented by the period. This avoids the common error of calculating age by dividing elapsed days by 365 or 365.25. The Java API defines Period/between as a years-months-days calculation between the start date, inclusive, and end date, exclusive.

Calculate age on a fixed date

A fixed target date is especially useful when an age calculation must be reproducible. It is also the easiest form to test because the answer does not depend on the machine clock.

(def dob (LocalDate/of 2000 9 22))\n(def target (LocalDate/of 2026 9 21))\n\n(println (completed-age dob target))

On September 21, 2026 the completed age for a September 22, 2000 DOB is 25. On September 22, 2026 it becomes 26. The birthday boundary is handled by the calendar-period calculation rather than an approximate day count.

Calculate years, months, and days

If the application needs more than completed years, keep the complete Period. Its years, months, and days fields can be read independently.

(defn age-period [dob target]\n  (Period/between dob target))\n\n(let [p (age-period\n           (LocalDate/of 1990 5 17)\n           (LocalDate/of 2026 9 22))]\n  {:years (.getYears p)\n   :months (.getMonths p)\n   :days (.getDays p)})

This is one of the strongest reasons to use Period rather than a duration in seconds. Months and years are calendar units with variable lengths. Java’s API documents Period as a date-based amount with years, months, and days, while Duration is time-based.

Calculate elapsed days and weeks

Calendar age and elapsed-day age are different outputs. If you need the number of days between two dates, use ChronoUnit/DAYS with LocalDate.

(import (java.time LocalDate) (java.time.temporal ChronoUnit))\n\n(def dob (LocalDate/of 1990 5 17))\n(def target (LocalDate/of 2026 9 22))\n\n(def days (ChronoUnit/DAYS between dob target))\n(def weeks (quot days 7))\n\n(println "Days:" days)\n(println "Complete weeks:" weeks)

Use this for elapsed-day or elapsed-week displays, not as a replacement for completed calendar age. A person’s age in completed years and the number of elapsed days since birth answer different questions.

Validate a date of birth

LocalDate/of validates the calendar date when the object is created. You can wrap construction in a try/catch when input arrives as separate numeric fields.

(import (java.time LocalDate DateTimeException))\n\n(defn valid-date? [year month day]\n  (try\n    (LocalDate/of year month day)\n    true\n    (catch DateTimeException _\n      false)))\n\n(valid-date? 2000 2 29)\n(valid-date? 2023 2 29)

Also validate the application rule that the DOB cannot be after the target date. For user-facing calculators, rejecting future DOB values before displaying an age avoids confusing negative results.

Leap years and February 29

Leap years are handled by the ISO calendar rules used by LocalDate. Java also provides Year/isLeap and IsoChronology/isLeapYear for explicit checks. A February 29 DOB is therefore a valid date only in a leap year.

(import (java.time Year))\n\n(.isLeap (Year/of 2024))\n;; true\n\n(.isLeap (Year/of 2025))\n;; false

The arithmetic is only part of the February 29 question. If your application needs to identify a birthday in a non-leap year, define whether February 28 or March 1 should be treated as the birthday. That is a business rule, so it should be documented instead of being hidden inside the age function.

Parse and format DOB strings

For web forms and APIs, ISO-style YYYY-MM-DD input maps naturally to LocalDate/parse. The default ISO local-date formatter can parse values such as 1990-05-17.

(import (java.time LocalDate))\n\n(def dob (LocalDate/parse "1990-05-17"))\n\n(println dob)\n(println (.format dob java.time.format.DateTimeFormatter/ISO_LOCAL_DATE))

For a different display format, use DateTimeFormatter/ofPattern. Keep machine-readable input in a stable format and format dates separately for presentation.

Rich Hickey, creator of the Clojure programming language
Rich Hickey, creator of Clojure, photographed by Tapestry Dude via Flickr. Wikimedia Commons identifies the image as CC BY-SA 2.0.

Build a reusable Clojure age calculator

A reusable function should accept both the DOB and target date. That keeps the core calculation pure and makes it straightforward to test.

(ns age-calculator.core\n  (:import [java.time LocalDate Period]))\n\n(defn age-period [dob target]\n  (Period/between dob target))\n\n(defn completed-age [dob target]\n  (.getYears (age-period dob target)))\n\n(defn age-today [dob]\n  (completed-age dob (LocalDate/now)))

This structure separates the deterministic calendar calculation from the system clock. A command-line program can call age-today, while tests can call completed-age with fixed dates.

Time zones and LocalDate

LocalDate deliberately has no time zone. That is normally desirable for a birthday because the DOB is a calendar date, not an instant. Problems can arise when an application starts with a timestamp, such as an ISO instant from a database or API.

When a timestamp must become a birthday or current local date, first convert it using the intended ZoneId, then extract the LocalDate. The zone should be chosen according to the application’s semantics rather than whatever time zone happens to be configured on the server.

Period versus Duration

RequirementUseReason
Completed agePeriodCalendar years and birthday boundaries
Years, months, daysPeriodNative date-based units
Elapsed calendar daysChronoUnit.DAYSDirect date difference
Exact seconds/nanosecondsDuration or another time-based typeElapsed time rather than calendar age

Java’s documentation distinguishes Period from Duration: a period is date-based and supports years, months, and days, while a duration is time-based. That distinction is important when daylight-saving changes or variable month lengths are involved.

Test the Clojure age calculator

TestExpected behavior
Birthday todayCompleted age includes the new birthday
Day before birthdayCompleted age remains one year lower
February 29 DOBLeap-year validity is respected
Invalid DOBDate construction or validation rejects it
Future DOBApplication rejects or explicitly handles it
Fixed target dateSame inputs always produce the same result
Month-end datesCalendar-period result handles variable month lengths
(deftest age-tests\n  (is (= 25\n         (completed-age\n           (LocalDate/of 2000 9 22)\n           (LocalDate/of 2026 9 21))))\n  (is (= 26\n         (completed-age\n           (LocalDate/of 2000 9 22)\n           (LocalDate/of 2026 9 22)))))

Tests should also cover year boundaries, leap years, dates at the end of February, and parsing failures. Passing an explicit target date makes the core function independent of the system clock.

Common mistakes in Clojure age calculations

  • Dividing days by 365: this is not equivalent to completed calendar age.
  • Using Duration for a birthday: a duration measures elapsed time, not years and months on a calendar.
  • Ignoring the target date: calculations based only on the current clock are harder to test.
  • Mixing timestamps with dates: convert an instant to the intended local date before applying birthday rules.
  • Leaving February 29 unspecified: define the non-leap-year birthday policy when the application needs one.
  • Parsing user input without validation: let LocalDate reject invalid calendar dates rather than accepting malformed values.

Compare Clojure with other language implementations

LanguageTypical date APIAge-calculation pattern
Clojurejava.time.LocalDate and PeriodJava interop plus concise Clojure functions
JavaLocalDate and PeriodDirect calendar-period calculation
Pythondatetime.dateDate arithmetic with explicit birthday logic
Scalajava.time.LocalDate and PeriodJVM date API through Scala syntax
RustDate/time cratesTyped date representations and calendar operations
Gotime.TimeBuilt-in time and date operations

Our related guides cover age calculators in Java, Python, Scala, Rust, Go, Haskell, Elixir, and Lua.

Complete Clojure example

(ns age-calculator.core\n  (:import [java.time LocalDate Period]\n           [java.time.format DateTimeFormatter]))\n\n(defn parse-dob [text]\n  (LocalDate/parse text DateTimeFormatter/ISO_LOCAL_DATE))\n\n(defn age-period [dob target]\n  (when (.isAfter dob target)\n    (throw (ex-info "DOB cannot be after target date"\n                    {:dob dob :target target})))\n  (Period/between dob target))\n\n(defn completed-age [dob target]\n  (.getYears (age-period dob target)))\n\n(defn age-today [dob]\n  (completed-age dob (LocalDate/now)))\n\n(def dob (parse-dob "1990-05-17"))\n(def target (LocalDate/of 2026 9 22))\n(def period (age-period dob target))\n\n(println "Age:" (.getYears period))\n(println "Years:" (.getYears period))\n(println "Months:" (.getMonths period))\n(println "Days:" (.getDays period))

This example keeps the core age calculation deterministic, validates that the DOB is not after the target, and uses the ISO local-date formatter for machine-readable input. Replacing the fixed target with (LocalDate/now) gives a current-age function for a typical calculator.

FAQs

Can Clojure calculate age without a separate date library?

Yes. Because Clojure runs on the JVM and can call Java classes directly, java.time.LocalDate and Period provide the core functionality for a date-only age calculator.

What is the simplest Clojure age calculation?

Create two LocalDate values, call Period/between, and read .getYears for completed age. Keep the target date explicit when reproducibility matters.

How does Clojure handle leap years?

The underlying Java ISO calendar implementation handles valid leap-year dates. You can also use Year/isLeap when an application needs an explicit leap-year check.

What is the difference between Period and Duration?

Period is date-based and represents years, months, and days. Duration is time-based and represents elapsed time. A DOB age calculation normally needs the calendar semantics of Period.

Does LocalDate include a time zone?

No. LocalDate represents a calendar date without a time or time zone. If your input is an instant or timestamp, convert it into the intended time zone before extracting the date.

Official references and image credits

Clojure’s official documentation confirms that the language is hosted on the JVM and can consume Java libraries directly. The official Clojure guide also demonstrates Java date/time interoperability, including LocalDate.

Final takeaway

A Clojure age calculator can stay small while still handling real calendar rules correctly. Use LocalDate for the DOB and target, Period/between for completed years or years-months-days, and ChronoUnit/DAYS when an elapsed-day figure is actually required. Keep the target date injectable for testing, validate future or malformed DOB values, and define your February 29 policy when your application needs one.