Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

J#27099: Adding additional time interval examples #63

Merged
merged 2 commits into from
Aug 13, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
317 changes: 317 additions & 0 deletions spec/15-h-timeintervalcalculations.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ ____
Durations above seconds are calendar durations that are not comparable with definite quantity UCUM duration units
____

In addition to duration and difference calculations, CQL provides timing phrases to allow flexible and precise natural language comparisons of date and time values and intervals. The available timing phrases in CQL fall into four categories:

* `same as`
* `before` and `after`
* `within`
* `during` and `includes`

The following sections provide detailed examples of calculating duration and difference, as well as interpreting timing phrases of CQL.

[[calculating-duration-in-years]]
== Calculating Duration in Years

Expand Down Expand Up @@ -494,3 +503,311 @@ Date 2: 2016-10-31 02:45:55 (-5:00) +
Date 2 (normalized): 2016-10-31 03:45:55 +
Difference (minutes) = Duration (minutes) between 2016-10-31 04:30:00.0 and 2016-10-31 03:45:00.0 +
Difference (minutes) = *-45 minutes*

[[interpreting-timing-phrases]]
== Interpreting Timing Phrases

[[same-as]]
=== Same as
To directly compare two date/time values, you can use the standard equality operators:

[source,cql]
----
@2020-07-30 = @2020-07-30
@2020-07-30 != @020-07-31
----

However, CQL also supports a `same as` timing phrase to support precision-based comparison of date/time values:

[source,cql]
----
@2020-07-30 same as @2020-07-30
----

When used without a precision specifier as in the above example, the `same as` timing phrase is the same as equality. Precision specifiers can be used to compare date/time values to a specific precision:

[source,cql]
----
@2020-07-30 same month as @2020-07-31
----

This returns true because the comparison only proceeds to the `month` precision.

[[relative-comparison]]
=== Relative comparison

To determine whether a date/time value is before or after another, CQL supports relative comparisons. As with equality, the standard relative comparison operators can be used:

[source,cql]
----
@2020-07-30 < @2020-07-31
@2020-07-31 <= @2020-07-31
----

These comparisons both return true because the date July 30th, 2020 is _before_ July 31st, 2020, and July 31st is _on or before_ July 31st. As with direct comparison, CQL supports the `before` and `after` keywords:

[source,cql]
----
@2020-07-30 before @2020-07-31 // equivalent to @2020-07-30 < @2020-07-31
@2020-07-31 on or before @2020-07-31 // equivalent to @2020-02-31 <= @2020-07-31
----

When no precision specifier is provided, these phrases are equivalent to the standard relative comparison operators. To compare to a particular precision:

[source,cql]
----
@2020-07-30 before month of @2020-07-31
----

This comparison returns false, because although July 30th is _before_ July 31st, the comparison only proceeds to the _month_ and the months are the same.

[[direct-comparison-with-offsets]]
=== Direct comparison with offsets

Timing phrases for comparison can also include an _offset_, which allows a _duration_ to be considered as part of the comparison. For example:

[source,cql]
----
@2020-07-01T09:30:00.0 1 hour before @2020-07-01T10:30:00.0
----

This returns true because 9:30AM on July 1st is exactly 1 hour before 10:30AM on July 1st. Note that this usage is a _direct_ comparison, not a relative comparison, so:

[source,cql]
----
@2020-07-01T08:30:00.0 1 hour before @2020-07-01T10:30:00.0
----

This returns false because 8:30AM on July 1st is more than 1 hour before 10:30AM on July 1st. To support relative comparison with offsets, include the `or more` or `or less` keywords:

[source,cql]
----
@2020-07-01T08:30:00.0 1 hour or more before @2020-07-01T10:30:00.0
----

The result of this comparison is true.

[[relative-comparison-with-offsets]]
=== Relative comparison with offsets

When using `or less`, the comparison is evaluated by considering an interval:

[source,cql]
----
@2020-07-01T09:30:00.0 1 hour or less on or before @2020-07-01T10:30:00.0
----

The above comparison returns true because 9:30AM on July 1st, 2020 is 1 hour or less on or before 10:30AM on July 1st, 2020. This is equivalent to asking

[source,cql]
----
@2020-07-01T09:30:00.0 >= (@2020-07-01T10:30:00.0 - 1 hour)
and @2020-07-01T09:30:00.0 <= @2020-07-01T10:30:00.0
----

Some further examples using this timing phrase:

[source,cql]
----
@2020-07-01T09:29:59.999 1 hour or less on or before @2020-07-01T10:30:00.0
----

This example returns false because 9:29:59.999AM on July 1st, 2020 is just barely more than 1 hour before 10:30AM on July 1st, 2020.

[source,cql]
----
@2020-07-01T09:29:59.999 1 hour or less on or before hour of @2020-07-01T10:30:00.0
----

However, the above example returns true because the comparison only proceeds to the hour, and the hour, 9, is 1 hour or less before the hour, 10.

[source,cql]
----
@2020-07-01T08:31:00.0 1 hour or less on or before @2020-07-01T10:30:00.0
----

The above example returns false because 8:31AM is more than 1 hour before 10:30AM (even though it is 1 hour and 59 minutes before).

To illustrate this point another way:

[source,cql]
----
hours between @2020-07-01T08:31:00.0 and @2020-07-01T10:30:00.0 <= 1 // true
----

The above example returns true because the duration in hours between 8:31AM and 10:30 AM is less than or equal to 1 (even though there is 1 hour and 59 minutes between the two times, it's still less than or equal to 1 and the duration calculation is only looking for full hours).

[source,cql]
----
difference in hours between @2020-07-01T08:31:00.0 and @2020-07-01T10:30:00.0 <= 1 // false
----

The above example returns false, because in using the `difference` calculation, we are indicating that we are concerned with the number of boundaries crossed, rather than the number of full hours, and two hour boundaries have been crossed between 8:31AM and 10:30AM.

Looking at another example, this time using `after` and to the `day`:

[source,cql]
----
@2020-07-12T10:00:00.0 1 day after day of @2020-07-11T10:00:00.0 // true
----

The above example returns true because the comparison only proceeds to the day, and July 12th, 2020 is exactly 1 day after July 11th 2020.

[source,cql]
----
@2020-07-11T23:59:59.999 1 day after day of @2020-07-11T10:00:00.0 // false
----

The above example returns false, again because the comparison only proceeds to the day, and July 11th, 2020 is the same day as July 11th, 2020, not the day after.

[source,cql]
----
@2020-07-13T00:00:00.0 1 day after day of @2020-07-11T10:00:00.0 // false
----

And finally, the above example returns false because July 13th is _more than_ 1 day after July 11th.

Looking at another example, this time using `or less before` and `weeks`:

[source,cql]
----
@2019-09-23 42 weeks or less before @2020-07-13 // true
----

The above example returns true because September 23rd, 2019 is 42 weeks or less before July 13th, 2020.

[source,cql]
----
@2019-09-23T09:00:00.0 42 weeks or less before @2020-07-13T10:00:00.0 // false
----

Adding time into the comparison, the above example returns false, because 9:00AM on September 23rd is more than 42 weeks before July 13th 2020. Only 1 hour more, but still more.

[source,cql]
----
@2019-09-23T09:00:00.0 42 weeks or less before day of @2020-07-13T10:00:00.0 // true
----

However, the above example returns true, because the comparison only proceeds to the day, September 23rd 2019 is 42 weeks before July 13th 2020, again because the time components are not considered in the comparison.

[source,cql]
----
@2019-09-22T11:00:00.0 42 weeks or less before day of @2020-07-13T10:00:00.0 // false
----

And finally, the above example returns false, because the comparison proceeds to the day, and September 22nd 2019 is 1 day more than 42 weeks before July 13th 2020.

=== Interval Example Summary

|===
|Scenario: OneHourOrLessOnOrBefore |Calculation Type |CQL Timing Phrase |Result |Comment

.3+|Start: @2020-07-01T08:31:00.0
End: @2020-07-01T10:30:00.0
| Interval
| @2020-07-01T08-08:31:00.0 1 hour *or less* on or before @2020-07-01T10:30:00.0 | `false` | Keyword `or less` is used
Interval calculation is used, and the time difference is 1 hour and 59 minutes

|Duration |hours *between* @2020-07-01T08:31:00.0 and @2020-07-01T10:30:00.0 <= 1 | `true` | Keyword `between` is used
Duration calculation is used and the hours are rounded down to the closest hour

|Difference |*difference* in hours between @2020-07-01T08:31:00.0 and @2020-07-01T10:30:00.0 <= 1 | `false` | Keyword `difference` is used
Difference calculation is used

.3+|Start: @2020-07-01T09:29:59.999
End: @2020-07-01T10:30:00.0
|Interval
|@2020-07-01T09:29:59.999 1 hour *or less* on or before @2020-07-01T10:30:00.0 |`false` |Result of calculation is 1 hour and 1 millisecond

|Duration |hours *between* @2020-07-01T09:29:59.999 and @2020-07-01T10:30:00.0 <= 1 |`true` |Keyword `between` is used

|Difference |*difference* in hours between @2020-07-01T09:29:59.999 and @2020-07-01T10:30:00.0 <= 1 |`true` |Keyword `difference` is used

|===

|===
|Scenario: OneDayAfterDayOf |Calculation Type |Timing Phrase |Result |Comment

|Start: @2020-07-12T10:00:00.0
End: @2020-07-11T10:00:00.0
|Comparison
|@2020-07-12T10:00:00.0 1 day after day of @2020-07-11T10:00:00.0 |`true` |True since calculation = 24 hours = 1 day. The keyword `day of` sets precision to `day`

|Start: @2020-07-12T08:00:00.0
End: @2020-07-11T10:00:00.0
|Comparison
|@2020-07-12T08:00:00.0 1 day after day of @2020-07-11T10:00:00.0 |`true` |True since the calculation < 24 hours but crossing one day = 1 day. The keyword `day of` sets precision to `day`

|Start: @2020-07-12T23:59:59.999
End: @2020-07-11T10:00:00.0
|Comparison
|@2020-07-12T23:59:59.999 1 day after day of @2020-07-11T10:00:00.0 |`true` |True since the calculation > 24 hours and crossing one day = 1 day. The keyword `day of` sets precision to `day`

|Start: @2020-07-11T23:59:59.999
End: @2020-07-11T10:00:00.0
|Comparison
|@2020-07-011T23:59:59.999 1 day after day of @2020-07-11T10:00:00.0 |`false` |False since the dates are the same day, calculation = 0 days

|Start: @2020-07-13T00:00:00.0
End: @2020-07-11T10:00:00.0
|Comparison
|@2020-07-13T00:00:00.0 1 day after day of @2020-07-11T10:00:00.0 |`false` |False since 7/11 to 7/13 = 2 days

|===

|===
|Scenario: FortyTwoWeeksOrLessBefore |Calculation Type |Timing Phrase |Result |Comment

|Start: @2019-09-23
End: @2020-07-13
|Interval
|@2019-09-23 42 weeks or less before @2020-07-13 |`true` |True since calculation = 42 weeks. Data only has date, but no time, so the precision is `day`

.2+|Start: @2019-09-23T09:00:00.0
End: @2020-07-13T10:00:00.0
|Interval
|@2019-09-23T09:00:00.0 42 weeks or less before @2020-07-13T10:00:00.0 |`false` |False since time is added to data, time precision is used and calculation = 42 weeks and 1 hour fails

|Interval
|@2019-09-23T09:00:00.0 42 weeks or less before *day of* @2020-07-13T10:00:00.0 |`true` |True since `day of` is setting the precision to `day`, 42 weeks 1 hour = 24 weeks 0 days

|Start: @2019-09-22T11:00:00.0
End: @2020-07-13T10:00:00.0
|Interval
|@2019-09-22T11:00:00.0 42 weeks or less before day of @2020-07-13T10:00:00.0 |`false` |False since calculation = 42 weeks and 1 day

|===

|===
|CQL Syntax |CQL Calculation Type

|A starts (ends) *before* start (end) of B
A starts (ends) *after* start (end) of B
A starts (ends) 3 days (weeks, hours) *or more* before start (end) of B
|Comparison

|A starts (ends) 3 days (weeks, hours) *or less* before start (end) of B
A starts (ends) *within* 3 days (weeks, hours) of start (end) of B
|Interval

|A starts (ends) 3 days (weeks, hours) *before* start (end) of B
A starts (ends) 3 days (weeks, hours) *after* start (end) of B
days (weeks, hours) *between* A and B
*duration in* days (weeks, hours) between A and B
|Duration

|*difference* in days (weeks, hours) *between* A and B
|Difference

|===

[[during-and-includes]]
=== During and includes

[source,cql]
----
@2020-01-01T00:00:00.0 during Interval[@2020-01-01T00:00:00.0, @2020-01T10:30:00.00]
@2020-01-01T10:30:00.0 during Interval[@2020-01-01T00:00:00.0, @2020-01T10:30:00.00]
----

These examples return true because during is defined inclusively.