{"id":28655972,"url":"https://github.com/gersak/timing","last_synced_at":"2025-12-12T01:06:32.874Z","repository":{"id":8986130,"uuid":"10733289","full_name":"gersak/timing","owner":"gersak","description":"Time computation library with CRON scheduling capability","archived":false,"fork":false,"pushed_at":"2025-06-01T16:00:28.000Z","size":1334,"stargazers_count":24,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-06T02:03:15.544Z","etag":null,"topics":["calendar","clojure","clojurescript","date","datetime","interval","scheduler","time","timestamp"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"akveo/ng2-admin","license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gersak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2013-06-17T08:39:51.000Z","updated_at":"2025-06-05T12:07:02.000Z","dependencies_parsed_at":"2025-03-20T05:37:09.185Z","dependency_job_id":"0a2dfffd-b75c-4d04-94c0-e5355fa10e76","html_url":"https://github.com/gersak/timing","commit_stats":null,"previous_names":["gersak/vura"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gersak/timing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gersak%2Ftiming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gersak%2Ftiming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gersak%2Ftiming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gersak%2Ftiming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gersak","download_url":"https://codeload.github.com/gersak/timing/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gersak%2Ftiming/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259606977,"owners_count":22883562,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calendar","clojure","clojurescript","date","datetime","interval","scheduler","time","timestamp"],"created_at":"2025-06-13T08:09:25.591Z","updated_at":"2025-10-14T17:06:50.538Z","avatar_url":"https://github.com/gersak.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Timing ⏰\n\n[![Clojars Project](https://img.shields.io/clojars/v/dev.gersak/timing.svg)](https://clojars.org/dev.gersak/timing)\n\n**A time library that thinks in numbers and embraces functional programming.**\n\nTiming offers a different approach to time computation by working in the numeric domain first. \nIf you enjoy functional programming, sequences, and immutable data, you might find Timing's \napproach refreshing.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width=\"460\" height=\"300\" src=\"resources/images/infinityclock.jpg\" style=\"border-radius:20px;\"\u003e\n\u003c/p\u003e\n\n# [API](https://cljdoc.org/d/dev.gersak/timing.core/0.6.4/doc/readme)\n\n\n## 🤔 Why Try Timing?\n\n### **Zero Dependencies, Simple Design**\n- Pure Clojure/ClojureScript with no external dependencies\n- Cross-platform compatibility between JVM and JavaScript\n- Immutable operations that play nicely with functional code\n- Support for multiple calendar systems (Gregorian, Julian, Hebrew, Islamic)\n- Holiday awareness for ~200 countries\n\n### **Numbers-First Philosophy**\nInstead of working with date objects, Timing encourages you to:\n```clojure\n;; Work with time as numbers (milliseconds since epoch)\n(def now (time-\u003evalue (date 2024 6 15 14 30 0)))  ; =\u003e 1718461800000\n(def later (+ now (days 7) (hours 3)))            ; Simple arithmetic!\n(value-\u003etime later)                               ; Back to Date when needed\n; =\u003e #inst \"2024-06-22T17:30:00.000-00:00\"\n```\n\n### **Sequence-Friendly Design**\nTiming was built to work well with Clojure's sequence operations:\n```clojure\n;; Generate quarterly dates for 2024\n(-\u003e\u003e (range 0 12 3)\n     (map #(add-months (time-\u003evalue (date 2024 1 1)) %))\n     (map value-\u003etime))\n; =\u003e (#inst \"2024-01-01T00:00:00.000-00:00\"\n;     #inst \"2024-04-01T00:00:00.000-00:00\" \n;     #inst \"2024-07-01T00:00:00.000-00:00\"\n;     #inst \"2024-10-01T00:00:00.000-00:00\")\n\n;; Business days using familiar sequence functions\n(def q1 (time-\u003evalue (date 2024 1 15)))\n(-\u003e\u003e (business-days-in-range (start-of-quarter q1) (end-of-quarter q1))\n     (take 5)\n     (map value-\u003etime))\n; =\u003e (#inst \"2024-01-01T23:00:00.000-00:00\"\n;     #inst \"2024-01-02T23:00:00.000-00:00\"\n;     #inst \"2024-01-03T23:00:00.000-00:00\"\n;     #inst \"2024-01-04T23:00:00.000-00:00\"\n;     #inst \"2024-01-07T23:00:00.000-00:00\")\n```\n\n### **Flexible Period Arithmetic**\nHandle edge cases naturally with smart period functions:\n```clojure\n;; Fixed-length periods (traditional)\n(+ today (days 30) (hours 8))\n\n;; Variable-length periods (handles month/year complexities)\n(-\u003e today\n    (add-months 3)    ; Handles month lengths properly\n    (add-years 2)     ; Handles leap years automatically  \n    (+ (days 15)))    ; Mix with fixed periods seamlessly\n```\n\n## ⚡ Quick Start\n\n### Installation\n```clojure\n;; deps.edn\n{:deps {dev.gersak/timing {:mvn/version \"0.7.0\"}}}\n\n;; Leiningen  \n[dev.gersak/timing \"0.7.0\"]\n```\n\n### Basic Usage\n```clojure\n(require '[timing.core :as t])\n\n;; Create dates\n(def birthday (t/date 1990 5 15))\n(def now (t/date))\n\n;; Convert to numeric domain for computation\n(def age-ms (- (t/time-\u003evalue now) (t/time-\u003evalue birthday)))\n(def age-days (/ age-ms t/day))\n\n;; Time arithmetic  \n(def next-week (+ (t/time-\u003evalue now) (t/days 7)))\n(def next-month (t/add-months (t/time-\u003evalue now) 1))\n\n;; Convert back to dates\n(t/value-\u003etime next-week)\n; =\u003e #inst \"2025-06-08T13:56:08.098-00:00\"\n(t/value-\u003etime next-month)\n; =\u003e #inst \"2025-07-01T13:56:08.098-00:00\"\n```\n\n## 🎯 Core Features\n\n### **1. Precision Time Units**\n```clojure\n;; All time units as precise numbers\nt/millisecond  ; =\u003e 1\nt/second      ; =\u003e 1000\nt/minute      ; =\u003e 60000\nt/hour        ; =\u003e 3600000\nt/day         ; =\u003e 86400000\nt/week        ; =\u003e 604800000\n\n;; Helper functions\n(t/days 7)      ; =\u003e 604800000\n(t/hours 3)     ; =\u003e 10800000  \n(t/minutes 45)  ; =\u003e 2700000\n```\n\n### **2. Smart Period Arithmetic**\n```clojure\n;; Variable-length periods with edge case handling\n(t/add-months (t/time-\u003evalue (t/date 2024 1 31)) 1)  \n; =\u003e Converts Jan 31 -\u003e Feb 28, 2024 (handles month-end properly)\n\n(t/add-months (t/time-\u003evalue (t/date 2023 1 31)) 1)  \n; =\u003e Converts Jan 31 -\u003e Feb 27, 2023 (non-leap year handling)\n\n(t/add-years (t/time-\u003evalue (t/date 2024 2 29)) 1)   \n; =\u003e Feb 29 -\u003e Feb 27, 2025 (Feb 29 doesn't exist in 2025)\n\n;; Chain operations naturally\n(-\u003e (t/time-\u003evalue (t/date 2024 1 15))\n    (t/add-months 6)\n    (t/add-years 2)  \n    (+ (t/days 10))\n    (+ (t/hours 8))\n    t/value-\u003etime)\n; =\u003e #inst \"2026-07-25T06:00:00.000-00:00\"\n```\n\n### **3. Flexible Rounding \u0026 Alignment**\n```clojure\n;; Round to any precision\n(t/round-number 182.8137 0.25 :up)      ; =\u003e 183.0\n(t/round-number 182.8137 0.25 :down)    ; =\u003e 182.75\n(t/round-number 182.8137 0.25 :ceil)    ; =\u003e 183.0\n(t/round-number 182.8137 0.25 :floor)   ; =\u003e 182.75\n\n;; Align to time boundaries\n(def test-value (t/time-\u003evalue (t/date 2024 6 15 14 30 45)))\n(t/value-\u003etime (t/midnight test-value))           ; Round to start of day\n; =\u003e #inst \"2024-06-14T22:00:00.000-00:00\"\n(t/value-\u003etime (t/round-number test-value t/hour :floor))  ; Round to start of hour\n; =\u003e #inst \"2024-06-15T12:00:00.000-00:00\"\n```\n\n### **4. Rich Time Context**\n```clojure\n(t/day-time-context (t/time-\u003evalue (t/date 2024 6 15)))\n; =\u003e {:leap-year? true,\n;     :day 6,\n;     :hour 0,\n;     :week 24,\n;     :weekend? true,\n;     :days-in-month 30,\n;     :first-day-in-month? false,\n;     :second 0,\n;     :days-in-year 366,\n;     :value 1718409600000,\n;     :month 6,\n;     :year 2024,\n;     :millisecond 0,\n;     :holiday? false,\n;     :last-day-in-month? false,\n;     :day-in-month 15,\n;     :minute 0}\n```\n\n### **5. Calendar Frame Generation**\n```clojure\n;; Get all days in a month (helpful for UI calendars)\n(take 3 (t/calendar-frame (t/time-\u003evalue (t/date 2024 6 1)) :month))\n; =\u003e ({:day 6, :week 22, :first-day-in-month? true, :value 1717200000000, \n;      :month 6, :year 2024, :last-day-in-month? false, :weekend true, :day-in-month 1}\n;     {:day 7, :week 22, :first-day-in-month? false, :value 1717286400000,\n;      :month 6, :year 2024, :last-day-in-month? false, :weekend true, :day-in-month 2}\n;     {:day 1, :week 23, :first-day-in-month? false, :value 1717372800000,\n;      :month 6, :year 2024, :last-day-in-month? false, :weekend false, :day-in-month 3})\n\n;; Also available: :year and :week views\n```\n\n## 🛠️ Advanced Features\n\n### **Temporal Adjusters**\n```clojure\n(require '[timing.adjusters :as adj])\n\n;; Navigate to specific days\n(def today (t/time-\u003evalue (t/date 2024 6 15)))  ; Saturday\n\n(adj/next-day-of-week today 1)        ; Next Monday\n; =\u003e 1718496000000 (converts to #inst \"2024-06-16T22:00:00.000-00:00\")\n\n(adj/first-day-of-month-on-day-of-week today 5)  ; First Friday of month\n; =\u003e 1717716000000 (converts to #inst \"2024-06-06T22:00:00.000-00:00\")\n\n(adj/last-day-of-month-on-day-of-week today 5)   ; Last Friday of month\n; =\u003e 1719525600000 (converts to #inst \"2024-06-27T22:00:00.000-00:00\")\n\n(adj/nth-day-of-month-on-day-of-week today 2 3)  ; 3rd Tuesday of month\n; =\u003e 1718582400000 (converts to #inst \"2024-06-17T22:00:00.000-00:00\")\n\n;; Period boundaries\n(adj/start-of-week today)             ; Start of current week\n(adj/end-of-month today)              ; End of current month  \n(adj/start-of-quarter today)          ; Start of current quarter\n(adj/end-of-year today)               ; End of current year\n\n;; Business day operations\n(adj/next-business-day today)         ; Skip weekends\n(adj/add-business-days today 5)       ; Add 5 business days\n; =\u003e 1718928000000 (converts to #inst \"2024-06-20T22:00:00.000-00:00\")\n\n(take 3 (map t/value-\u003etime (adj/business-days-in-range \n                           (adj/start-of-month today) \n                           (adj/end-of-month today))))\n; =\u003e (#inst \"2024-06-02T22:00:00.000-00:00\"\n;     #inst \"2024-06-03T22:00:00.000-00:00\"\n;     #inst \"2024-06-04T22:00:00.000-00:00\")\n```\n\n### **Calendar Printing**\n```clojure\n(require '[timing.util :as util])\n\n(util/print-calendar 2024 6)\n; Prints:\n;                 June 2024\n; +---+---+---+---+---+---+---+\n; |Mon|Tue|Wed|Thu|Fri|Sat|Sun|\n; +---+---+---+---+---+---+---+\n; |   |   |   |   |   | 1 | 2 |\n; | 3 | 4 | 5 | 6 | 7 | 8 | 9 |\n; |10 |11 |12 |13 |14 |15 |16 |\n; |17 |18 |19 |20 |21 |22 |23 |\n; |24 |25 |26 |27 |28 |29 |30 |\n; +---+---+---+---+---+---+---+\n\n;; Customizable options available\n(util/print-calendar 2024 6 {:first-day-of-week 7    ; Sunday first\n                              :show-week-numbers true ; Show week numbers\n                              :day-width 4})          ; Wider cells\n\n;; Print entire year\n(util/print-year-calendar 2024)\n```\n\n### **Timezone \u0026 Configuration**\n```clojure\n;; Dynamic timezone context\n(t/with-time-configuration {:timezone \"America/New_York\"}\n  (select-keys (t/day-time-context (t/time-\u003evalue (t/date 2024 6 15))) \n               [:year :month :day-in-month :hour]))\n; =\u003e {:year 2024, :month 6, :day-in-month 15, :hour 0}\n\n;; Convert between timezones\n(def my-time (t/time-\u003evalue (t/date 2024 6 15 12 0 0)))\n(def london-time (t/teleport my-time \"Europe/London\"))\n(t/value-\u003etime london-time)\n; =\u003e #inst \"2024-06-15T09:00:00.000-00:00\" (adjusted for timezone)\n\n;; Custom weekend days and holidays\n(t/with-time-configuration {:weekend-days #{5 6}      ; Fri/Sat weekend\n                            :holiday? my-holiday-fn}   ; Custom holiday logic\n  (t/weekend? (t/time-\u003evalue (t/date 2024 6 14))))    ; Friday\n; =\u003e true\n```\n\n### **Multiple Calendar Systems**\n```clojure\n;; Switch calendar systems dynamically\n(let [now (t/time-\u003evalue (t/date 2024 6 15))]\n  (println \"Gregorian:\" \n           (select-keys (t/day-time-context now) [:year :month :day-in-month]))\n  (println \"Hebrew:\" \n           (t/with-time-configuration {:calendar :hebrew}\n             (select-keys (t/day-time-context now) [:year :month :day-in-month])))\n  (println \"Islamic:\" \n           (t/with-time-configuration {:calendar :islamic}\n             (select-keys (t/day-time-context now) [:year :month :day-in-month]))))\n\n; Prints:\n; Gregorian: {:year 2024, :month 6, :day-in-month 15}\n; Hebrew: {:year 5784, :month 3, :day-in-month 9}\n; Islamic: {:year 1445, :month 12, :day-in-month 8}\n\n;; Available calendars: :gregorian, :julian, :hebrew, :islamic\n```\n\n### **Holiday Integration**\n```clojure\n(require '[timing.holiday :as holiday])\n;; Note: For full holiday support, also require:\n;; (require '[timing.holiday.all]) ; Add all holiday implementations\n\n;; Check holidays by country\n(holiday/? :us (t/time-\u003evalue (t/date 2024 7 4)))     ; =\u003e holiday map\n(holiday/? :us (t/time-\u003evalue (t/date 2024 12 25)))   ; =\u003e holiday map\n(holiday/? :us (t/time-\u003evalue (t/date 2024 1 1)))     ; =\u003e holiday map\n\n;; Get holiday name (important: use holiday/name function!)\n(def july4-holiday (holiday/? :us (t/time-\u003evalue (t/date 2024 7 4))))\n(holiday/name :en july4-holiday)  ; =\u003e \"Independence Day\"\n\n(def christmas-holiday (holiday/? :us (t/time-\u003evalue (t/date 2024 12 25))))\n(holiday/name :en christmas-holiday)  ; =\u003e \"Christmas Day\"\n\n;; Supports ~200 countries\n```\n\n### **Cron Scheduling**\n```clojure\n(require '[timing.cron :as cron])\n\n;; Parse and work with cron expressions\n(def next-noon (cron/next-timestamp (t/time-\u003evalue (t/date 2024 6 15)) \"0 0 12 * * ?\"))\n(t/value-\u003etime next-noon)\n; =\u003e #inst \"2024-06-15T10:00:00.000-00:00\" (next occurrence of daily noon)\n\n(cron/valid-timestamp? (t/time-\u003evalue (t/date 2024 6 15 12 0 0)) \"0 0 12 * * ?\")\n; =\u003e true (matches cron pattern)\n\n;; Generate future execution times\n(def start-time (t/time-\u003evalue (t/date 2024 6 15)))\n(take 3 (map t/value-\u003etime (cron/future-timestamps start-time \"0 0 9 * * MON\")))\n; =\u003e (#inst \"2024-06-17T07:00:00.000-00:00\"    ; Next Monday 9 AM\n;     #inst \"2024-06-24T07:00:00.000-00:00\"    ; Following Monday\n;     #inst \"2024-07-01T07:00:00.000-00:00\")   ; And the one after\n```\n\n## 💡 Real-World Examples\n\n### **Business Date Calculations**\n```clojure\n;; Add 30 business days to today\n(def deadline \n  (adj/add-business-days (t/time-\u003evalue (t/date 2024 6 15)) 30))\n(t/value-\u003etime deadline)\n; =\u003e #inst \"2024-07-25T22:00:00.000-00:00\"\n\n;; Find all month-end Fridays in 2024\n(def month-end-fridays\n  (-\u003e\u003e (range 1 13)\n       (map #(t/time-\u003evalue (t/date 2024 % 1)))\n       (map #(adj/last-day-of-month-on-day-of-week % 5))\n       (map t/value-\u003etime)))\n(take 3 month-end-fridays)\n; =\u003e (#inst \"2024-01-25T23:00:00.000-00:00\"\n;     #inst \"2024-02-22T23:00:00.000-00:00\"\n;     #inst \"2024-03-28T23:00:00.000-00:00\")\n\n;; Calculate working days between two dates\n(def working-days\n  (count (adj/business-days-in-range \n          (t/time-\u003evalue (t/date 2024 6 1))\n          (t/time-\u003evalue (t/date 2024 6 30)))))\n; =\u003e 20 (working days in June 2024)\n```\n\n### **Recurring Event Generation**\n```clojure\n;; Every 2nd Tuesday for next 6 months\n(def bi-weekly-meetings\n  (-\u003e\u003e (adj/every-nth-day-of-week today 2 2)  ; Every 2nd Tuesday\n       (take-while #(\u003c % (t/add-months today 6)))\n       (take 12)\n       (map t/value-\u003etime)))\n\n;; Quarterly board meetings (last Friday of quarter)\n(def quarterly-meetings\n  (-\u003e\u003e [3 6 9 12]  ; End of quarters\n       (map #(t/time-\u003evalue (t/date 2024 % 1)))\n       (map adj/end-of-month)\n       (map #(adj/last-day-of-month-on-day-of-week % 5))\n       (map t/value-\u003etime)))\n```\n\n### **Financial Calculations**\n```clojure\n;; Monthly payment dates (15th of each month)\n(def payment-dates-2024\n  (-\u003e\u003e (range 1 13)\n       (map #(t/time-\u003evalue (t/date 2024 % 15)))\n       (map #(if (adj/weekend? %) \n               (adj/previous-business-day %)  ; Move to Friday if weekend\n               %))\n       (map t/value-\u003etime)))\n\n;; Quarter-end reporting dates\n(def quarter-ends\n  (-\u003e\u003e (range 2024 2027)\n       (mapcat #(map (fn [q] (adj/end-of-quarter \n                             (t/time-\u003evalue (t/date % (* q 3) 1)))) \n                     [1 2 3 4]))\n       (map t/value-\u003etime)))\n```\n\n## 🔧 Architecture\n\n### **Modular Design**\n```\ntiming/\n├── core/        # Core time computation (timing.core)\n├── timezones/   # IANA timezone database (timing.timezones)  \n├── holidays/    # Country-specific holidays (timing.holiday)\n├── cron/        # Cron scheduling (timing.cron)\n└── util/        # Utility functions (timing.util, timing.adjusters)\n```\n\n### **Design Philosophy**\n\n1. **Numeric Domain First** - Computation in milliseconds, objects for display\n2. **Immutable Values** - All operations return new values\n3. **Functional Composition** - Everything chains naturally with threading macros\n4. **Zero Dependencies** - Pure Clojure/ClojureScript\n5. **Cross-Platform** - Identical behavior on JVM and JavaScript\n\n### **Performance Characteristics**\n- **Efficient** - Numeric arithmetic on primitive longs\n- **Memory Friendly** - Minimal object allocation during computation\n- **Lazy-Friendly** - Works well with lazy sequences\n- **Composable** - Easy to combine with other functional operations\n\n## 🤝 When to Choose Timing\n\nTiming might be a good fit if you:\n- Enjoy functional programming patterns\n- Prefer working with sequences and transformations\n- Want to avoid external dependencies\n- Like the numeric domain approach to time\n- Need cross-platform Clojure/ClojureScript compatibility\n- Appreciate immutable, composable operations\n\nOther excellent time libraries like `clj-time` and Java 8 Time API excel in different areas:\n- **clj-time**: Rich object model, extensive parsing/formatting\n- **Java 8 Time API**: Comprehensive feature set, strong typing\n- **js-joda**: JavaScript port of JSR-310 with excellent browser support\n\nEach approach has its strengths, and the best choice depends on your specific needs and preferences.\n\n## 🎨 Usage Patterns\n\n### **Functional Pipeline Style**\n```clojure\n(def employees [{:hire-date (t/date 2023 1 15)}\n                {:hire-date (t/date 2023 3 20)}\n                {:hire-date (t/date 2023 6 10)}])\n\n(-\u003e\u003e employees\n     (map :hire-date)\n     (map t/time-\u003evalue)  \n     (map #(t/add-years % 1))         ; One year anniversary\n     (map #(adj/next-day-of-week % 5)) ; Move to Friday\n     (map t/value-\u003etime)              ; Back to dates\n     (take 3))\n; =\u003e (#inst \"2024-01-18T23:00:00.000-00:00\"\n;     #inst \"2024-03-21T23:00:00.000-00:00\"\n;     #inst \"2024-06-13T22:00:00.000-00:00\")\n```\n\n### **Threading Macro Style**  \n```clojure\n(-\u003e (t/date 2024 1 1)\n    t/time-\u003evalue\n    (t/add-months 6)\n    (adj/start-of-quarter)\n    (adj/next-business-day)\n    t/value-\u003etime)\n; =\u003e #inst \"2024-07-01T22:00:00.000-00:00\"\n```\n\n### **Sequence Generation**\n```clojure\n;; Generate all Mondays in 2024\n(take-while #(\u003c % (t/time-\u003evalue (t/date 2025 1 1)))\n            (adj/every-nth-day-of-week (t/time-\u003evalue (t/date 2024 1 1)) 1 1))\n\n;; All business days in a month\n(def today (t/time-\u003evalue (t/date 2024 6 15)))\n(adj/business-days-in-range (adj/start-of-month today) (adj/end-of-month today))\n```\n\n## ⚡ Tips for Best Results\n\n1. **Stay in Numeric Domain** - Minimize conversions to/from Date objects\n2. **Embrace Lazy Sequences** - Let Clojure's laziness work for you\n3. **Batch Operations** - Process collections functionally\n4. **Cache Computations** - Store frequently used values\n\n```clojure\n;; Efficient: Stay numeric  \n(map #(+ % (t/days 1)) timestamps)\n\n;; Less efficient: Convert back and forth\n(map #(t/value-\u003etime (+ (t/time-\u003evalue %) (t/days 1))) dates)\n```\n\n## 🚀 Getting Started\n\n1. **Add Timing to your project**\n2. **Start with basic date arithmetic**\n3. **Explore calendar frames for UI components**\n4. **Add temporal adjusters for complex logic**\n5. **Use holidays and timezones as needed**\n\n```clojure\n;; Your first Timing program\n(require '[timing.core :as t])\n(require '[timing.adjusters :as adj])\n\n(def today (t/time-\u003evalue (t/date)))\n(def next-friday\n  (-\u003e today\n      (t/midnight)\n      (adj/next-day-of-week 5)\n      (+ (t/hours 17))))  ; 5 PM\n\n(println \"Next Friday at 5 PM:\" (t/value-\u003etime next-friday))\n```\n\n## 🔧 Important Notes\n\n### **Timezone-Aware Date Display**\nDue to timezone handling, dates may display with timezone offsets. This is normal and expected behavior:\n```clojure\n(t/value-\u003etime (t/time-\u003evalue (t/date 2024 6 15)))\n; =\u003e #inst \"2024-06-14T22:00:00.000-00:00\" (with timezone offset)\n```\n\n### **Holiday Name Extraction**\nHoliday functions return holiday objects that need to be processed with `holiday/name`:\n```clojure\n;; Don't expect direct string results\n(holiday/? :us (t/time-\u003evalue (t/date 2024 7 4)))\n; =\u003e {:name #function, ...}\n\n;; Use holiday/name to get readable names\n(def holiday-obj (holiday/? :us (t/time-\u003evalue (t/date 2024 7 4))))\n(holiday/name :en holiday-obj)  ; =\u003e \"Independence Day\"\n```\n\n### **Rounding Behavior**\nThe `round-number` function behavior varies by strategy:\n```clojure\n(t/round-number 182.8137 0.25 :up)    ; =\u003e 183.0\n(t/round-number 182.8137 0.25 :down)  ; =\u003e 182.75\n(t/round-number 182.8137 0.25 :ceil)  ; =\u003e 183.0\n(t/round-number 182.8137 0.25 :floor) ; =\u003e 182.75\n```\n\n## 📜 License\n\nCopyright © 2018 Robert Gersak\n\nReleased under the MIT license.\n\n---\n\n*Timing: A friendly approach to time computation in Clojure.*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgersak%2Ftiming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgersak%2Ftiming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgersak%2Ftiming/lists"}