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

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:

<effectiveTime xsi:type=’SXPR_TS’><!– memorial day –>

<comp xsi:type=’SXPR_TS’>

<comp xsi:type=’PIVL_TS’>

<phase>

<low value=’19870525’/>

<high value=’19870601′ inclusive=’false’/>

</phase>

<period value=’1′ unit=’a’/>

</comp>

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

<phase>

<low value=’19870105’/>

<high value=’19870106′ inclusive=’false’/>

</phase>

<period value=’1′ unit=’wk’/>

</comp>

</comp>

<!– labor day –>

<comp xsi:type=’SXPR_TS’ operator=’P’>

<comp xsi:type=’PIVL_TS’>

<phase>

<low value=’19870901’/>

<high value=’19870908′ inclusive=’false’/>

</phase>

<period value=’1′ unit=’a’/>

</comp>

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

<phase>

<low value=’19870105’/>

<high value=’19870106′ inclusive=’false’/>

</phase>

<period value=’1′ unit=’wk’/>

</comp>

</comp>

</effectiveTime>

<effectiveTime xsi:type=’PIVL_TS’ alignment=’DW’ operator=’A’>

<!– every other Tuesday –>

<phase>

<low value=’20001202′ inclusive=’true’/>

<high value=’20001203′ inclusive=’false’/>

</phase>

<period value=’2′ unit=’wk’/>

</effectiveTime>

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

<!– from 2002 and 2003 –>

<low value=’20020101′ inclusive=’true’/>

<high value=’20040101′ inclusive=’false’/>

</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:

<effectiveTime xsi:type=’SXPR_TS’><comp xsi:type=’SXPR_TS’>

<!– memorial day –>

<comp xsi:type=’SXPR_TS’>

<comp xsi:type=’PIVL_TS’>

<phase>

<low value=’19870525’/>

<high value=’19870601′ inclusive=’false’/>

</phase>

<period value=’1′ unit=’a’/>

</comp>

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

<phase>

<low value=’19870105’/>

<high value=’19870106′ inclusive=’false’/>

</phase>

<period value=’1′ unit=’wk’/>

</comp>

</comp>

<!– labor day –>

<comp xsi:type=’SXPR_TS’ operator=’P’>

<comp xsi:type=’PIVL_TS’>

<phase>

<low value=’19870901’/>

<high value=’19870908′ inclusive=’false’/>

</phase>

<period value=’1′ unit=’a’/>

</comp>

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

<phase>

<low value=’19870105’/>

<high value=’19870106′ inclusive=’false’/>

</phase>

<period value=’1′ unit=’wk’/>

</comp>

</comp>

</comp>

<comp xsi:type=’PIVL_TS’ alignment=’DW’ operator=’A’>

<!– every other Tuesday –>

<phase>

<low value=’20001202′ inclusive=’true’/>

<high value=’20001203′ inclusive=’false’/>

</phase>

<period value=’2′ unit=’wk’/>

</comp>

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

<!– from 2002 and 2003 –>

<low value=’20020101′ inclusive=’true’/>

<high value=’20040101′ inclusive=’false’/>

</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?

9 Comments

  1. Thomas Beale says:

    Normally something like QSET would be considered a ‘computational set’, i.e. defined by its interface (has, intersects, etc), while in normal mathematics, a ‘set’ is defined extensionally… did I miss something here?

  2. Grahame Grieve says:

    well, I more meant that most people think of a set in computational terms as an enumeration of discrete items (per the way it is defined in UML 2, and equally in the generic libraries for java and .Net etc). a GTS is not like them – it’s a set in a mathematical sense.

  3. Victor Chai says:

    I think some of the complexity of GTS such as IVL, PIVL, interaction and union etc is necessary to support some of the complicated use cases, however the demonstrated example might be very hypothetical – “every other Tuesday in the season from the (US holidays) Memorial Day to Labor Day in the years 2002 and 2003”. The information is entered into system by users in the first place. Take the same example, lets ask 1) Does any clinician enter timing information using holidays from UI? 2) Even if so, how they will enter these kind of information into system? free form text or discrete data items? With more sensible and real use case requirement, GTS can be designed in a simpler way.

    Secondly,the current design of GTS makes it extremely difficult for the construction of the GTS on sending side and parsing on receiving end, it is hard to come up a generic technical component to handle the construction and parsing, even it is possible, the wire format is extremely bulky. In the current design, the system needs to interpret the actual timing is “Saturday” given that “Dec 02 2000” is Saturday on the calendar and ignore the actual date since the period is “wk”. If we compare GTS with Unix cron job syntax which is very concise and easy to understand and implement, there is a long way to go.

    Lastly, the most awkward with GTS is the way how it represents “day of the week” timing, even though ISO21090 says that the actual date in ‘phase’ element is irrelevant since the calendar alignment is day of the week, still it is very confusing. If we expand the code set for calendar alignment, for example we can create a new alignment code “DW5″ to represent alignment to Thursday then the ‘phase’ element is no longer needed. Overall in this way the XML is more concise and clearer, and simpler to implement. Using this approach, we can also easily implement ‘first week of the sept” – the intersection of “MY9” (9th month of the year) and “WM1” (first week of the month).

    <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>

    For future consideration of simplification of GTS.

  4. Kevin says:

    A simplification to LIST<IVL> is pretty helpful for things like effectiveTime/activityTime when you need to document non-periodic episodes. I am not sure, however, it is a legal “cast” from a QSET. It also presupposes that the IVLs do not intersect, I.e. there is no TS which belong to >1 IVL. That is, however, a pretty common use case. (e.g. documenting when someone had pain, was treated with a cycle of chemotherapy, was intubated, etc.).

  5. Pablo Pazos says:

    Hi Grahame, I’m was reviewing the CDA XSD and the elements of type GTS in the CDA R2 model are SXCM_TS in the XSD. SXCM_TS extends TS

    I can’t find QSET in the XSD.

    Also, IVL_TS, PIVL_TS, EIVL_TS, and others extend SXCM_TS.

    It seems that the SXCM_TS of the XSD is playing the same role as the QSET you mentioned on this post. Is this correct?

    It is a little confusing to not having a 1 to 1 correspondence between the specs and the XSD.

  6. Lloyd McKenzie says:

    CDA is based on HL7 data types release 1. QSET is part of data types release 2 (the ISO 21090 data types). So the models are somewhat different. SXCM is semi-similar to QSET, but not identical. (GTS changed somewhat as part of the release 2 process to make it “more implementable”, though that’s a long way from “highly implementable” :>

  7. Clayton says:

    Consider the example of taking medicine “three times per day”. Assume the patient takes medicine at the following times:

    Monday 10am, 4pm
    Tuesday 12:30am, 9am, 3pm and 10pm

    This schedule violates a precise translation of the instructions, but I’ll wager no one would describe this patient as non-conformant since 12:30am Tuesday is materially Monday. The opposite would be true of a patient who takes the medicine at 9am, 10am and 11am. While strictly compliant with the words, this plan is not what we understand to be conformant.

    In many cases, forcing users to translate instructions into a QSET introduces artificial precision. Their words are precise, but are actually buried in a network of contextual understanding. This network would be unbelievably hard to represent in a QSET — if we were even 100% sure of a computable definition.

    I’m not sure there’s an “obvious” solution, but I would be willing (at least as a devil’s advocate) to make the case that HL7v3 needs dedicated types for instructions like these. The types would exist solely to indicate the presence of contextual understanding. They would tend towards “special purpose” since the contextual understanding changes with the context. They would also be implementer-friendly because they would capture narrow, common patterns of human use.

    These structures would not be strictly computable, but the community could develop computable “guardrails” that would flag cases where conformance is unlikely.

Leave a Reply

Your email address will not be published. Required fields are marked *

question razz sad evil exclaim smile redface biggrin surprised eek confused cool lol mad twisted rolleyes wink idea arrow neutral cry mrgreen

*

%d bloggers like this: