datomic 2025-10-13

How to query a composite tuple containing a date (`:db.type/instant`) using d/index-range, such that the returned index range is inclusive of that date, e.g. if I store #inst "2025-10-13", I want to pass a tuple-end value to d/index-range that will include all values up to & including that value? Do I need to increment the date and filter out any values that might be 1ms after midnight? Is there a better way to do it?

⭐ 1

what does "date" mean with an instant?

by date I mean that I convert a LocalDate to java.util.Date because Datomic only supports storing :db.type/instant

... that's not a good idea

localdate is just a Y-M-D; it has to both add precision that doesn't exist and project it into the unix epoch chronology, both of which are easy to do incorrectly and incrementally accumulate errors

storing accounting entries which have a posting date (not datetime, currently), but they could have time associated with them how should I be storing dates and datetimes, if not using instant?

something that doesn't add extra precision or a unix epoch chronology

should I separate out year / month / day / hour / minute / seconds / ms as Datomic attrs?

things that work fine: string in ISO, Epoch days, a long (20251019), a tuple of these, etc

anything that lexically sorts the same way and has a straightforward round-tripping encoding

wouldn't that string be much costlier to store than an Instant?

probably, but it's also human readable; all of these have tradeoffs

the most compact is Epoch Day (for LocalDate), but it's not very readable

anyway, to your original question: index-range termination when you want the termination condition to be "inclusive"

you can either bump the end value by exactly one quantum, or you can leave it nil and use take-while

👍 1

I mentioned LocalDate, but I convert all dates to UTC before storage. Is :db.type/instant still a bad idea? I don't see why it's a bad idea if I want to support posting dates with datetime or just date (zero time) in future.

How will you tell the difference in the future?

Keep in mind that a Date or Instant is a long counting milliseconds since unix epoch, meant to represent a moment in time and be converted into locally-reckoned values as perceived in the past according to complex rules. If your time is not that (which LocalDate and LocalDateTime are not--they are ymd hms values that don't represent a moment on the unix epoch timeline), then encoding them as instants is fraught with peril. If you have a sidechannel where you keep the "real" type of the time (e.g. on the attribute), and completely control all encoding, decoding and display of these times in and out of java.util.Date perfectly forever, then you can get away with it. If you mess up, you get at best confused users and at worst irrecoverably corrupt data

however I have never seen it done successfully, and I have cleaned up multiple messes related to this problem in my career, in both datomic and sql databases

👍 1

so my advice is Just Don't

if it's not a moment in time, don't use a type with those semantics or store it that way at rest

👏 1

Why don't heterogeneous :db/tupleTypes https://docs.datomic.com/schema/schema-reference.html#tuples :db.type/short? I want to store [entity-ref some-boolean year month day hour minute seconds] but now I have to use Long for most components, which is costly to store.

what is :db.type/short? I don't think that exists even outside tuples

clojure doesn't store numbers at any precision less than long

and they are typically boxed

datomic stores data with https://github.com/Datomic/fressian, which has a variable-length integer encoding

👍 1

You can bitpack a long, too

xml schema has a bunch of https://www.w3.org/TR/xmlschema11-2/#built-in-primitive-datatypes which at some point in my career I wanted to represent in datomic and javascript in a single attribute in a precision-preserving way (to map to another xml format 1:1 instead of across multiple attributes) with temporal sorting (there was a defined way to sort datetimes with mixed precisions). Turns out you can do this in 52 bits if you make some compromises. https://gist.github.com/favila/d635c5aeb3a391f424d6 Note the abuse of the unix epoch.

How will you tell the difference between something that was originally a date only and something that was a date+time? (or is this not a thing in your schema?)

What is the underlying storage for :db.type/boolean? Is it a bit, or a Long that stores 1 or 0? The docs don't specify the cost.

In jvm, memory cost of primitive bool is typically sizeof int; but in clojure it's going to almost always be boxed

But what is it Datomic, though? Is there an optimized storage format for boolean, esp. as part of a heterogeneous tuple?