Everything you didn’t want to know about the GTS data type

Dec 12, 2011

Of all the HL7 / ISO 21090 data types, by far the most complex is the General Timing Specification (GTS). (Aside: I usually say that CD is the most complex data type, but it’s not; it’s just that people use CD as hard as they can, where as one glance at GTS convinces almost everybody to keep things as simple as they possibly can.) GTS in Data types R2 / ISO 21090

It’s easier to consider GTS in R2, and then work backwards to explain what GTS is in data types R1 (CDA). In ISO 21090, a GTS is a QSET(TS) – a set of TS. This is a mathematical set – some type of expression that defines what times are “in the set”, rather than a computational set, which lists the values of the set. In the case of ISO 21090, the expression can be built by combining simple times and intervals of times with grouped and nested operations such as Union, Intersection, etc.

Formally, these are defined as a set of classes like this:

 

|Type|Basic Summary |QSET|Abstract base type. Anywhere QSET(T) is specified, one of the types below must be used |IVL|A simple interval from start time to end time (may be open and therefore continuing) |PIVL|A simple interval of time that repeats regularly (the interval might be assumed, like as in “3 times a day” or explicit, such as “twice a day for half an hour”) |QSI|The intersection of two other sets (a simple case: the intersection of 2 times a day for 10 minutes from 10-Aug 2011 to 10-Sept 2011) |QSU|The union of two sets of times (perhaps, 3 times a day on week days, and twice a day on weekends) |QSD|The difference between two sets of times |QSP|The periodic hull of two sets of times:  ) |QSH|The convex hull of two sets of times: |QSS|A list of times, where the time covered by the imprecision of the time is in the set (i.e. 24-April 2012 means all of that day is in the set). This is simpler to use than IVL where single days are covered |QSC|A code that identifies a set of times. The codes are only those defined by HL7 or ISO members, and include common clinical codes, as well as holidays:

  • AM  Every morning at institution specified times.
  • PM  Every afternoon at institution specified times.
  • BID  Two times a day at institution specified time
  • TID  Three times a day at institution specified time
  • QID  Four times a day at institution specified time

(note: Alert HL7 readers might wonder where EIVL is. Since this is my blog, I get to send it off to Antarctica from where I’ll retrieve it when it’s a cold day in hell)

That’s pretty confusing – so let’s clarify slightly by an example:  “every other Tuesday in the season from the (US holidays) Memorial Day to Labor Day in the years 2002 and 2003”. This is built as an expression of the intersection between 3 sets:

  • every other Tuesday;
  • the years 2002 and 2003;
  • the season between Memorial Day and Labor Day.
<example xsi:type="QSI_TS"><!-- intersection, because it is a QSI -->
 <!-- every other Tuesday -->
 <term xsi:type='PIVL_TS' alignment='DW'>
  <phase lowClosed='true' highClosed='false'>
   <low value='20001202'/>
   <high value='20001203'/>
  </phase>
  <period value='2' unit='wk'/>
 </term>

 <!-- 2002 and 2003 -->
 <term xsi:type='IVL_TS' lowClosed='true' highClosed='false'>
  <low value='20020101'/>
  <high value='20040101'/>
 </term>
 <!-- season between Memorial Day and Labor Day -->
 <!-- periodic hull between Memorial day and Labor Day -->
 <term xsi:type='QSP_TS'>
  <low xsi:type="QSI_TS">
  <!-- memorial day: intersection of last week of May and mondays -->
   <term xsi:type='PIVL_TS'>
    <phase highClosed='false'>
     <low value='19870525'/>
     <high value='19870601'/>
    </phase>
    <period value='1' unit='a'/>
   </term>
   <term xsi:type='PIVL_TS'>
    <phase highClosed='false'>
     <low value='19870105'/>
     <high value='19870106'/>
    </phase>
    <period value='1' unit='wk'/>
   </term>
  </low>
  <high xsi:type="QSI_TS">
   <!-- labor day :  intersection of first week of Sept and mondays -->
   <term xsi:type='PIVL_TS'>
    <phase highClosed='false'>
     <low value='19870901'/>
     <high value='19870908'/>
    </phase>
    <period value='1' unit='a'/>
   </term>
   <term xsi:type='PIVL_TS'>
    <phase highClosed='false'>
     <low value='19870105'/>
     <high value='19870106'/>
    </phase>
    <period value='1' unit='wk'/>
   </term>
  </high>
 </term>
</example>

For me, what this example clarifies is the following:

  • You can build any timing description you like from this.
  • There’s too much power to grapple with the general case here in a computable way
  • This is a bad way to communicate timing specifications between people

That’s a particularly nasty combination for me – from a computable view point, it’s too powerful. From a human view point – which is where we fall back if we can’t get to compute these things – it’s very clunky.

In practice, every use of the GTS data type that I’ve seen, people either use IVL, PIVL, or a bounded PIVL: a PIVL intersected with a IVL where the IVL serves as start and end dates. Anything else gives nearly everybody fits, and I’ve never seen it used (will be happy to hear real experience otherwise in comments).

R1 vs R2

GTS appears rather different in R1 than R2. Firstly, we completely rewrote how we describe GTS, so that it’s comprehensible (I know what GTS means in R1, but only because I wrote data types R2, and then wrote my R1 -> R2 implementation). We did also make some changes in the features of GTS too, by adding

  • QSC – a coded GTS
  • QSS – a list of times

Other than that, there’s no semantic change.

GTS in R1

I’m not even going to explain how GTS is specified in R1, it just hurts my head. The XML is kind of goofy (though it works). But it has one rather sad side effect that not many people realise. Let’s take that same example as above:

 

</comp>

</effectiveTime>

 

Rather than having QSX, there’s a single type SXPR_TS, and it has an operator on instead. That’s just syntactical sugar – it’s the same structure. The comp just maps to the various named attributes which are operands on the operations. And some of the attributes are renamed. So that’s not so bad.

But the real difference is the way the base operands are constructed – by tacking effectiveTime elements after each other with operators on them. This is legal, even when the cardinality on the effectiveTime attribute is 0..1, because that’s the cardinality of the GTS, not the cardinality of the effectiveTime element that builds the GTS. That’s a subtlety that not many people are aware of. Additionally, the GTS would mean that same thing if there was only one effective time with 3 components like this:

 

</comp>

< comp xsi:type=’IVL_TS’ operator=’A’>

</comp>

</effectiveTime>

Requirements for GTS

This doesn’t really stand as a full explanation of the GTS data type. I can’t be bothered providing one of those. The question I’m interested in is, what are we trying to do here? What are the real world requirements we need here? Here’s my list:

  • We need to support common things for medications – bid, etc, and simple bounded periodic intervals
  • Whatever we do has to be human readable as a fall back for when computing it is impossible
  • We need to be able to make repeating appointments in a form that calendars can process

Anything else?