CDR implementation of partial DV_TEMPORALs

Hi all,

Have any CDRs (EHRbase, EHRserver, Better, etc) implemented persistence of partial DV_DATE, DV_TIME or DV_DATE_TIME? For example a partial DV_DATE would be just a year, or a year and a month without the day.

If so, how did you handle the practical persistence in terms of types in database implementations, programming languages and query engines? Not to mention all the complications relating to the magnitude (what is it for a DV_DATE_TIME 2026? 2026-01-01?) and all the functions of Iso8601_date et al?

Explanations and/or code examples would be very welcome :star_struck:

I’m sure someone in the community has some knowledge about this? :blush:

Also, if I and my CDR vendor DIPS (@bna) are wondering about this, maybe the Date Time Package of the specs should be more specific about how to do this? @SEC / @SEC-experts thoughts?

I am not sure I understand your question entirely, but for what I get is that you are looking for implementation nuances around comparing dates when persisted instances are partial.

From my perspective:

  • databases (Postgres, etc.) expect fully qualified timestamps/dates columns, unless stored as text.
  • programming languages also typically assume complete temporal values.
    So the problem that you point out is that a partial temporal is not a concrete instant.

In our implementation we store it as text (json/xml), and depending on the use case use, it means a sort of range (when comparing values during a database query), or a plain narrative string (in forms, view), and in some situation the nearest date instance (e.g. 2026-01-01). But you might be right, this is not solid (enough), some assumptions are being taken…

How do you think the spec should be more specific? What sort of information would be meaningful to add to that specification section, without diving in particular implementations aspects?

Sorry for being slow :frowning:

EhrBase and Better both support partial dateTimes like ‘1994’ or ‘1988-03’ - we used an example in the IHE Plugathon testing that checks this. Not sure about Cadasto @sebastian.iancu or Atomik ? @pablo

I don’t know about the internal representations or how comparisons work.

Ian

I’m also wondering if you’re looking for specific implementations of for a general set of rules at the spec level like “how this should work in openEHR”? @siljelb

Just a small comment: if we use Date or DateTime types at the database level, then the values should be valid full date and full date time. The data type implies the full data is provided, so partial dates won’t be accepted (in general). When storing strings you lose all the “date” functions and you might need to implement them yourself, which is basically like implementing your own data type at the database level. The next option is to have alternative fields: normal Date for full dates and strings or ranges for partial dates (as Sebastian mentioned a partial date is in fact a range). An alternative to string storage is using integers, but again you need to implement logic to interpret the int number, validate it, and have specific date functions over the int. There is no elegant solution, everything is a hack because openEHR specified this differently than how 99.99% of technologies actually work.

Oh, I forgot one option: having a normal date/datetime stored and an extra field for the precision, so you can store a full valid date, but when querying and comparing, you’ll process that based on the precision, which will tell you something like “use just the year and month from this date”, so 1991-05-07 should be interpreted as 1991-05, which can be interpreted as 1991-05-01..1991-05-31 (a range of dates).

In ehrbase Partial date/time values (e.g. "2023", "1985-06") are stored in the DB as two columns: the original ISO 8601 string preserving the actual precision, and a pre-computed long magnitude representing seconds since 0001-01-01T00:00:00Z — as defined by the openEHR spec, where DV_DATE and DV_DATE_TIME inherit from DV_QUANTIFIED via DV_TEMPORAL and DV_ABSOLUTE_QUANTITY, and ordering is defined as order by magnitude.

The magnitude is computed using a floor strategy: any missing fields are filled with their minimum values — month defaults to 1, day to 1, time components to 00:00:00, and timezone to UTC. AQL comparison operators (<, >, =, between) then map directly to integer comparisons on the magnitude column, with the implication that equality on mixed-precision values is semantically approximate — "2023" and "2023-01-01" produce identical magnitudes despite having different precision.

Thank you all for your responses, and particularly to @stefanspiska for the detailed explanations on your two columns as well as how you handle the magnitude. Using a floor strategy makes total sense.

It’s really both. Regarding the specs, like when working with definitions in archetypes, it’s a good rule of thumb that if modelers/implementers are unsure about how to use an archetype or a part of the spec, it may be because it’s too vague :blush:

Maybe a fleshed out Data Types Information Model - Partial Date/Times would be sufficient? For example outlining how to compute the magnitude for a partial?

I have a few follow-up questions that I hope you all can answer as well:

  1. Do you also use the accuracy (DV_DURATION) from DV_TEMPORAL to specify the level of accuracy in the persisted datetime? For example 0001 for "2023" when the specific year is meant, or 0001-01-01 for "2023-01-01" when specifically the first of january is meant? (is this what you meant in your second post, @pablo?)
  2. DV_DATE_TIME inherits Iso8601_date_time, where functions year(), month(), day() etc are mandatory. These are specified to return 0 “if not present” (Foundation Types - Iso8601_date_time Class). Is this intended to be 0 (number zero) or � (NULL)? If the former, how do you differentiate between hours/minutes/seconds “not present” vs explicitly 0 (zero)?
  3. When handling partial dates, times or datetimes in programming languages, do you use custom classes or some other tricks to avoid the issue that @sebastian.iancu points out here?

As I understand it, accuracy measures a different property: certainty

With partial dates you are dealing with certainty implicitly, so for me it’s a matter of precision more than certainty.

If you check the temporal types (Data Types Information Model), the accuracy is DV_DURATION, so it can be something like P30D, so if you want to represent 2015-05-XX I think you can’t do it with the accuracy. From your example: 0001 isn’t a valid duration expression, seems more like a mask, see below.

Just as an example, the concept of precision on a date expression, is like a data mask, so you can say precision=month, which means YYYY-MM-XX (XX is missing data), and you can see it as a 1111-11-00 mask, so you can store anything in the data that you don’t know, to keep the datatype to be valid in the underlying technology (programming language, database, query language, exchange format) like 2026-04-01, but when consumed externally it should be interpreted as value && mask which is effectively 2026-04.

The problem is the spec expects partial dates to be supported by the underlying technology, it isn’t the case and won’t be anytime soon, so at the implementation level you need to add the precision concept even if that’s not in the spec explicitly, but it allows implements the concept of partial dates. The only types that have precision are DV_QUANTITY and DV_PROPORTION.

A small note about temporal types and magnitude: by the spec the magnitude is a method (calculated) not part of the state of the temporal types, so it’s not required to store it. Though we store it too to make comparisons and calculations on dates and times.

Of course :woman_facepalming: thanks for correcting that! :smile: