Java Allergies and Revisiting java.time
Tags: clojure, java
Published: 2024-02-17 16:34:26

---
title: Java Allergies and Revisiting java.time
published: true
date: 2024-02-09 18:08:27 UTC
tags: Clojure, Java
canonical_url: https://camdez.com/blog/2024/02/09/java-allergies-and-revisiting-java.time/
---

_Wherein I argue that Clojurians should be a touch more embracing of Java interop and that `java.time` is actually good._

<aside>
<h2>Table of Contents</h2>
<nav id="TableOfContents">
<ol>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#fun-times-with-javatime">Fun Times with <code>java.time</code></a></li>
<li><a href="#but-im-allergic-to-java">But I’m Allergic to Java</a></li>
<li><a href="#to-library-or-not-to-library">To Library Or Not To Library?</a></li>
<li><a href="#postscript">Postscript</a></li>
</ol>
</nav>
</aside>
## Introduction

I enjoyed [Josh Glover’s post](https://jmglov.net/blog/2024-02-09-counting-blog-posts.html) today in which he described how he queried his [quickblog](https://github.com/borkdude/quickblog) data with a dash of [Babash…ka](https://github.com/babashka/babashka) (_sorry, couldn’t resist_) to determine the number of posts he made during a certain date range—“in 50 simple steps”.

I appreciate this narrative, almost _lab notebook_, style of writing. It’s helpful to see how others work through problems, what tools they use to explore, and what alternatives they consider. It’s also humanizing, reminding us that elegant works we encounter likely didn’t emerge spontaneously, [Athena-like](https://en.wikipedia.org/wiki/Athena#Birth), from the heads of their creators, but took time<sup id="fnref:1"><a href="#fn:1" role="doc-noteref">1</a></sup> and, perhaps, toil. _Ex nihilo nihil fit_—the mill demands its grist.

It takes extra work to write as he did, tracking your own twists and turns, so, _thank you_ to Josh for the post. (He has a good sense of humor and makes cool stuff, so check out [his blog](https://jmglov.net/blog/)!)

I enjoyed seeing how simple the [quickblog](https://github.com/borkdude/quickblog) data model was along the way (universal Clojure plot spoiler: _it was maps_).

![Scooby Doo Reveal meme of Clojure taking the mask off data to reveal a Clojure map beneath.](https://camdez.com/images/blog/java-allergies-and-revisiting-java-time/clj-data-reveal.jpg)

## Fun Times with `java.time`

I’d like to borrow a few of Josh’s steps<sup id="fnref:2"><a href="#fn:2" role="doc-noteref">2</a></sup> as a jumping off point to address date / time handling in Clojure<sup id="fnref:3"><a href="#fn:3" role="doc-noteref">3</a></sup>:

> 1. Realise that :posts is a map of filename to post, but no matter!
> 
> 2. Sigh as you come to terms with the fact that you’re going to need to do some date parsing and you never remember how to use java.time and argh!
> 
> 3. Smile as you remember about tick, which is a library from our good friends at JUXT that provides a nicer API for this sort of stuff

I’ve had several discussions with peers who felt the exact same way about `java.time`. Hell, _I_ felt that way for while.

But while I certainly agree that the [JUXT folks](https://www.juxt.pro) are great friends to the community (💙), and that they’ve created something nice with [tick](https://github.com/juxt/tick), I’d also argue that **`java.time` is pretty good** , and it deserves another look if you wrote it off early on.

I can’t vouch for everything in `java.time`—I don’t _know_everything in `java.time`—but it was written (comparatively) recently, after the lessons of `java.util.Date` (bad) and[Joda-Time](https://www.joda.org/joda-time/) (much better) had been absorbed, and _they basically got it right_.

I suspect most of us have dealt with a date parsing scenario like Josh encountered and found ourselves thinking: _“just turn this damn string into a date—how hard could it be?”_ Maybe we even came from a language where a single function could parse `"2022-08-26"` and`"2022-08-26T10:30"` and `"2022-08-26T10:30:00.000-05:00"`, and it all seemed so easy. And now we have this fussy, baroque `java.time` thing to contend with.

But the thing about those easy interfaces? They’re _all_ bad, broken, or incorrect<sup id="fnref:4"><a href="#fn:4" role="doc-noteref">4</a></sup>. And, if you haven’t already, you _will_ eventually get burned by them.

It’s actually trivial to demonstrate: what does `"2022-08-26"`represent? Is it August 26th, 2022 in your computer’s time zone? In GMT? Is it that date _regardless_ of your timezone (meaning it’s not a single, unified point in time<sup id="fnref:5"><a href="#fn:5" role="doc-noteref">5</a></sup>)? These are all foundational concepts in our world; we need all of these concepts to have programs that do practical things, and since the semantics are not differentiated in the content of the string, they cannot possibly be handled correctly by a single (undifferentiated) parser.

Human representation of time is _inherently_ complex (read [Falsehoods programmers believe about time](https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time) if you need convincing), so you really only have two options:

1. **The broken way** — “Being small and ~~simple~~ [easy] is more important than being correct.” (perjoratively, “the Unix way”, à la[The Unix Haters Handbook, pg. xx](https://archive.org/details/TheUnixHatersHandbook/page/n21/mode/2up))
2. **The fussy way** — “The design must be correct in all observable aspects. Incorrectness is simply not allowed.” (generously, “the Lisp way”, à la [rpg](https://dreamsongs.com/RiseOfWorseIsBetter.html)).

A correct implementation, by necessity, reflects the inherent complexity of the problem domain<sup id="fnref:6"><a href="#fn:6" role="doc-noteref">6</a></sup>. And I think `java.time`actually does a good job of that. [The core classes in`java.time`](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) neatly represent the underlying concepts in the domain (_and are immutable!_)—it’s just that the domain is big, so there’s a lot for us to get our heads around.

But once we do:

- A date, irrespective of time or timezone is just:   
`(LocalDate/parse "2022-08-26")`
- If that includes a time, it’s:   
`(LocalDateTime/parse "2022-08-26T10:30")`
- And a single point in time is:   
`(Instant/parse "2022-08-26T10:30:00.000-05:00")`

There’s really no fluff or inconsistency here—we just need to remember the class names. It wouldn’t be any easier if these were three _function_ names instead.

## But I’m Allergic to Java

Many Clojurians seem grumpy about seeing Java _stuff_ in their Clojure code. I get it. I, too, came from the pure Lisp lands of kebabs and unified case (though preferably not `*THE-SHOUTY-KIND*`). And didn’t we do all of this to get away from that Java stuff?

Calling everything via functions looks nice, and has its benefits for porting (_e.g._ CLJS), and, hey, wouldn’t it be _dreamy_ if one day we had a non-hosted, portable language with a spec and maybe it even bootstrapped itself, and compiled natively and… _[trails off]_

But in the meantime…

- JVM interop is a freaking _superpower_. The presence of “Java stuff” in our language means we get to tap into _thousands of person-years_ of engineering effort that our small community couldn’t have come up with on its own. _It’s not a bug—it’s a feature!_ (But really this time.)
- You’ll need to learn _some_ Java stuff if you want to get good at Clojure. You need to be able to read stacktraces effectively, use records and protocols, and wield core Java libraries.
- We shouldn’t shun _interop_ in the same way that non-Lispers shouldn’t shun _s-expressions_. Think what that bias has cost them. If minor syntax details are your biggest concern, you need to work on harder problems!
- [Clojure 1.12](https://clojure.org/news/2024/02/08/1-12-alpha6) is around the corner and you’re going to be able to do this:   
`(map LocalDate/parse ["2022-08-26" ,,,])`   
Instead of this:   
`(map (fn [v] (LocalDate/parse v)) ["2022-08-26" ,,,])`   
Rejoice!

## To Library Or Not To Library?

So we want to filter a sequence of maps based on their `:date`values—should we pull in a library? (You know what I’m going to say if you read all of the above, so I won’t belabor it…)

JUXT actually provides [a nice guide to when you might want to use tick](https://github.com/juxt/tick?tab=readme-ov-file#should-you-use-tick-for-date-time-work) that makes thoughtful points you should read.

But for the basic cases, my fellow Clojurians, I really encourage you to just learn `java.time`. Because of the inherent complexity of time, it’s basically _this set of classes_ or _that set of functions_—and the former is standard, built-in, battle-tested, and known.

_Give it another chance._

It may not look like much, but it’s got it where it counts.

## Postscript

**Bonus tip** : for super basic cases, you can just use`read-instant-date` (which pairs great with [`#inst`’s convenient capabilities](https://engineering.collbox.co/post/clj-inst-reader-literal-shortcuts/)!):

```
(.before (clojure.instant/read-instant-date "2022-08-26") #inst "2022-09-01")
;; =>
true

```

It’s naive, without a proper understanding of timezones and such, but it handles the basics fine.

* * *

_Thank you for reading, and thanks again to Josh for [the great post](https://jmglov.net/blog/2024-02-09-counting-blog-posts.html)._

* * *

1. Hopefully of the _hammock_ variety. [↩︎](#fnref:1)

2. Slightly reformatted because _computers_. [↩︎](#fnref:2)

3. It is my sincere hope that the author won’t mind my use of his post as a foundation to discuss this topic with the community; it’s certainly not intended as a critique of his post and / or code. [↩︎](#fnref:3)

4. Just to be clear (and fair), I’m not including [tick](https://github.com/juxt/tick) in my “bad” list—it’s actually a thin, sane layer over `java.time` (on the JVM). [↩︎](#fnref:4)

5. If you want to have a bad time (ugh, sorry), try explaining to a_user_ that a date basically _isn’t a thing_ and that their globally-distributed team possibly _should_ see two different dates on the same invoice depending on their location. [↩︎](#fnref:5)

6. [So many pigeons, so few holes.](https://en.wikipedia.org/wiki/Pigeonhole_principle) [↩︎](#fnref:6)