Getting Started¶
Installation¶
Via Nix flakes¶
# flake.nix
inputs.cl-date-kit = {
url = "github:nerima-lisp/cl-date-kit/v0.2.0";
inputs.nixpkgs.follows = "nixpkgs";
};
Pin a release tag, not the default branch: this is an org-wide policy so that a sibling repository's history never breaks a consumer's build.
Via ASDF directly¶
Clone this repository, then add its absolute path to ASDF's central registry before loading the system:
Alternatively, configure CL_SOURCE_REGISTRY to find the checkout.
cl-date-kit has no ASDF dependencies (:depends-on ()); the only runtime
requirement beyond SBCL is a readable IANA time zone database, which is
already present on essentially every Linux and macOS install at
/usr/share/zoneinfo. See Compatibility for how
that lookup works and what TZDIR overrides.
Naive calendar values¶
(asdf:load-system "cl-date-kit")
(in-package :cl-date-kit)
(let ((date (make-local-date 2024 2 29)))
(format-local-date (local-date-plus-years date 1)))
;; => "2025-02-28" -- clamped, since 2025 is not a leap year
(period-between (make-local-date 2020 1 31) (make-local-date 2021 3 1))
;; => #S(PERIOD :YEARS 1 :MONTHS 1 :DAYS 1)
Real-world timestamps with a time zone¶
(let* ((ny (find-time-zone "America/New_York"))
(zdt (zoned-date-time-of-local (local-date-time-of 2024 6 15 12 0 0) ny)))
(format-zoned-date-time zdt))
;; => "2024-06-15T12:00:00-04:00[America/New_York]"
Daylight-saving gaps and overlaps¶
2024-03-10T02:30:00 never happened in America/New_York -- clocks jumped
straight from 02:00 to 03:00. RESOLVE-LOCAL-DATE-TIME (which
ZONED-DATE-TIME-OF-LOCAL calls) lets you choose how to handle that:
(resolve-local-date-time (local-date-time-of 2024 3 10 2 30 0) ny
:disambiguation :strict)
;; => signals NONEXISTENT-LOCAL-TIME
(resolve-local-date-time (local-date-time-of 2024 3 10 2 30 0) ny)
;; => #<ZONE-OFFSET -04:00> (:COMPATIBLE, the default: the post-gap offset)
2024-11-03T01:30:00 happens twice that same year, when clocks fall back
from 02:00 EDT to 01:00 EST:
(possible-offsets-for-local-date-time (local-date-time-of 2024 11 3 1 30 0) ny)
;; => (#<ZONE-OFFSET -04:00> #<ZONE-OFFSET -05:00>) -- earlier, then later
Elapsed time vs. calendar time¶
DURATION is exact elapsed time; PERIOD is calendar-based. Adding a day
across a spring-forward transition is 23 real hours, not 24 -- which is
exactly why ZONED-DATE-TIME has separate -PLUS-DURATION and
-PLUS-PERIOD operations instead of one:
(let* ((before (zoned-date-time-of-local (local-date-time-of 2024 3 9 12 0 0) ny))
(after (zoned-date-time-plus-period before (period-of-days 1))))
(values (format-zoned-date-time after)
(duration-to-seconds (duration-between before after))))
;; => "2024-03-10T12:00:00-04:00[America/New_York]"
;; 82800 (23 hours, not 86400)
See Core concepts for the full type layering and API reference for every function.