Coverage report: /development/source/library/org/datagraph/spocq-shard/src/algebra/operators/temporal-operators.lisp
| Kind | Covered | All | % |
| expression | 809 | 1674 | 48.3 |
| branch | 39 | 72 | 54.2 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; -*- Mode: lisp; Syntax: ansi-common-lisp; Base: 10; Package: org.datagraph.spocq.implementation; -*-
3
(in-package :org.datagraph.spocq.implementation)
5
(:documentation "This file defines the standard SPARQL operators for dateTime values and
6
the extended XPath temporal operators for the 'org.datagraph.spocq' RDF engine."
9
"Copyright 2010 [james anderson](mailto:james.anderson@setf.de) All Rights Reserved.")
12
"The file defines the operators for SPOCQ from the 'SPARQL Query Language for RDF'[1] recommendation
13
and also the additional temporal operators from the 'XPath and XQuery Functions and Operators 3.0'[2] recommendation.
14
The definition pattern is the same as for the core algebra operators.
15
The construction, combination and promotion rules are those specified for XPath[3].
17
The datatypes are (with respective '-' / camel-case transformations)
30
There are four operator classes:
31
- constructors, which also perform casts
36
This implementation prescribes ZULU as the implicit timezone component of the dynamic context[4].
37
As that value is a static constant and, as such pervasive, all data is canonicalized to zulu time
40
The implementation evolved from one in which all instances included canonicalization to zulu time
41
as part of instantiation, into one in which the fragments were canonicalized, into one in which
42
nothing extra which nothing is done until either the location value or a fragment is required,
43
at which point that is produced according to
44
lexical form -> fragments -> location value(s)
45
that is, they retain as much as they can as late as they can.
46
given values and the zone and do not perform canonicalization inorder to generate the location value
47
until it is required, at which point the zone is applied.
49
nb. temporal predicates are defined here as well as accessors and combinations.
50
all predicates require the types to match.
51
nb. no order is defined for Gregorians
52
The only operations defined on xs:gYearMonth, xs:gYear, xs:gMonthDay, xs:gMonth and xs:gDay values
53
are equality comparison and component extraction. For other types, further operations are provided,
54
including order comparisons, arithmetic, formatted display, and timezone adjustment.[5]
55
2020-05-02: This reference falls back to starting instance, which does allow comparisons
58
[1]: http://www.w3.org/TR/rdf-sparql-query/#OperatorMapping.
59
[2]: http://www.w3.org/TR/xpath-functions-30/
60
[3]: http://www.w3.org/TR/xpath-functions-30/#casting
61
[4]: http://www.w3.org/TR/xpath-30/#id-xp-evaluation-context-components
62
[5]: http://www.w3.org/TR/xpath-functions-30/#date-time-types
68
(defmacro spocq.a:|now| ()
70
The function NOW returns an XSD dateTime value for the current query execution.
71
All calls to this function in any one query execution must return the same
73
The moment is that recorded when the query process was created."
77
"Return the 'current' time for the evaluation of a given query.
78
This is the universal time point hen the query initialized."
79
(universal-time-date-time (if *task* (task-start-time *task*) (get-universal-time))))
81
(defmacro spocq.a:|then| ()
84
;;; originally for use with an universal time. currently applies to the current revision
85
;;; the universal time case should use universal-time-date
86
(defun spocq.e:then ()
87
(repository-revision-date-time *repository*))
89
(defun spocq.e::unix-now ()
91
(multiple-value-bind (sec microsec) (sb-ext:get-time-of-day)
92
(+ (* sec 1000000) microsec))
93
#-sbcl #.(error "no get-time-of-day"))
94
(defconstant unix-timestamp-units-per-second 1000000)
96
;;; abstract operators for temporals, gregorians
98
(defgeneric decode-temporal (value &optional zone)
99
(:documentation "Decode the given concrete temporal instance")
101
(:method :around ((object spocq:temporal) &optional (zone nil zp-p))
102
(multiple-value-bind (fraction second minute hour day month year)
104
(unless zp-p (setf zone (spocq:temporal-zone object)))
105
(if (offset-zone-p zone)
106
(canonicalize-date-time-fragments fraction second minute hour day month year 0 zone)
107
(values fraction second minute hour day month year))))
109
(:method ((object integer) &optional zone)
110
(dydra-ndk::decode-time object (or zone spocq:+reference-zone+))))
112
(defun temporal-zone (temporal)
113
(unless (temporal-decoded-p temporal)
114
(decode-temporal temporal))
115
(spocq:temporal-zone temporal))
117
(defun decode-extended-time (value &optional zone) (decode-temporal value zone))
119
(macrolet ((def-slot-accessors (class &rest slots)
120
(let ((decoder (cons-symbol :spocq.i :decode- class))
121
(decoded-names '(fraction second minute hour day month year zone))
122
(spocq-type (or (find-symbol (symbol-name class) :spocq)
123
(error "invalid temporal class: ~s" class))))
125
,@(loop for slot in slots
126
for name = (if (consp slot) (first slot) slot)
127
for type = (if (consp slot) (getf (rest slot) :type) 'fixnum)
128
collect (let* ((spocq.i (cons-symbol :spocq.i class "-" name))
129
(spocq (or (find-symbol (symbol-name spocq.i) :spocq)
130
(error "invalid field: ~s" name))))
131
`(progn (defun ,spocq.i (,class)
133
(nth-value ,(position name decoded-names) (,decoder ,class))))
134
(defmethod decode-temporal ((temporal ,spocq-type) &optional zone)
135
(declare (ignore zone))
137
(define-compiler-macro ,spocq.i (,class)
138
(list 'or (list ',spocq ,class)
139
(list 'nth-value ,(position name decoded-names) (list ',decoder ,class))))
140
(declaim (ftype (function (,spocq-type) ,type) ,spocq.i)))))))))
141
(def-slot-accessors date minute hour day month year zone)
142
(def-slot-accessors date-time fraction second minute hour day month year zone)
143
(def-slot-accessors day-time-duration fraction second minute hour day zone)
144
(def-slot-accessors duration fraction second minute hour day month year zone)
145
(def-slot-accessors g-day day zone)
146
(def-slot-accessors g-month month zone)
147
(def-slot-accessors g-month-day month day zone)
148
(def-slot-accessors g-year year zone)
149
(def-slot-accessors g-year-month year month zone)
150
(def-slot-accessors time fraction second minute hour zone)
151
(def-slot-accessors year-month-duration month year zone))
154
(macrolet ((def-temporal-accessor ((interface-op implementation-op &optional (accessor implementation-op))
155
(parameter . classes) documentation
156
&optional (result-type t))
157
;; accept both date, date-time and duration
158
(setf documentation (format nil "( (or~{ ~a~}) ) ~a~%~a" classes result-type documentation))
159
`(progn (define-compiler-macro ,interface-op (,parameter)
160
(setf (variable-opacity (expression-variables ,parameter)) :transparent)
161
(list ',implementation-op ,parameter))
162
(defun ,interface-op (,parameter)
164
(,implementation-op ,parameter))
165
(declaim (ftype (function ((or ,@classes)) ,result-type) ,implementation-op))
166
(defgeneric ,implementation-op (,parameter)
167
(:documentation ,documentation)
168
,@(loop for class in classes
169
collect `(:method ((,parameter ,class))
170
(,(cons-symbol :spocq.i class :- accessor) ,parameter))))))
173
(def-temporal-accessor (spocq.a:|year| spocq.e:year)
174
(arg spocq:date spocq:date-time spocq:year-month-duration spocq:g-year spocq:g-year-month)
175
"The function YEAR returns the year part of arg as an integer.
176
This function corresponds to fn:year-from-dateTime.")
177
(def-temporal-accessor (spocq.a:|month| spocq.e:month)
178
(arg spocq:date spocq:date-time spocq:year-month-duration spocq:g-month spocq:g-month-day spocq:g-year-month)
179
"The function MONTH returns the month part of arg as an integer.
180
This function corresponds to fn:month-from-dateTime.")
181
(def-temporal-accessor (spocq.a:|day| spocq.e:day)
182
(arg spocq:date spocq:date-time spocq:day-time-duration spocq:g-day spocq:g-month-day)
183
"The function DAY returns the day part of arg as an integer.
184
This function corresponds to fn:day-from-dateTime and its variations.")
185
(def-temporal-accessor (spocq.a:|hours| spocq.e:hours hour)
186
(arg spocq:date-time spocq:day-time-duration spocq:time)
187
"The function HOURS returns the hours part of arg as an integer.
188
The value is as given in the lexical form of the XSD dateTime.
189
This function corresponds to fn:hours-from-dateTime.")
190
(def-temporal-accessor (spocq.a:|minutes| spocq.e:minutes minute)
191
(arg spocq:date-time spocq:day-time-duration spocq:time)
192
"The function MINUTES returns the minutes part of the lexical form of arg.
193
The value is as given in the lexical form of the XSD dateTime.
194
This function corresponds to fn:minutes-from-dateTime.")
195
#+(or) ; must be implemented differently, below
196
(def-temporal-accessor (spocq.a:|seconds| spocq.e:seconds second)
197
(arg spocq:date-time spocq:day-time-duration spocq:time)
198
"The function SECONDS returns the seconds part of the lexical form of arg.
199
This function corresponds to fn:seconds-from-dateTime."))
201
;;; certain cases are over-ridden
203
(defmethod spocq.e:year ((object spocq:day-time-duration)) 0)
204
(defmethod spocq.e:month ((object spocq:day-time-duration)) 0)
205
(defmethod spocq.e:day ((object spocq:year-month-duration)) 0)
206
(defmethod spocq.e:hours ((object spocq:year-month-duration)) 0)
207
(defmethod spocq.e:minutes ((object spocq:year-month-duration)) 0)
208
(defmethod spocq.e:seconds ((object spocq:year-month-duration)) 0)
210
;;; second must be computed
211
(define-compiler-macro spocq.a:|seconds| (arg)
212
(setf (variable-opacity (expression-variables arg)) :transparent)
213
`(spocq.e:seconds ,arg))
215
(defun spocq.a:|seconds| (arg)
216
"( (or date-time day-time-duration time) ) t
217
the function seconds returns the seconds part of the lexical form of arg.
218
this function corresponds to fn:seconds-from-datetime."
219
(org.datagraph.spocq.evaluation:seconds arg))
221
(flet ((compute-real-seconds (second fraction)
224
(+ second (float (/ fraction +timeline-units-per-second+))))))
225
(defgeneric spocq.e:seconds (arg)
226
(:documentation "( (or date-time day-time-duration time) ) t
227
the function seconds returns the seconds part of the lexical form of arg.
228
this function corresponds to fn:seconds-from-datetime.")
229
(:method ((arg spocq:date-time)) (compute-real-seconds (date-time-second arg) (date-time-fraction arg)))
230
(:method ((arg spocq:day-time-duration)) (compute-real-seconds (day-time-duration-second arg) (day-time-duration-fraction arg)))
231
(:method ((arg spocq:time)) (compute-real-seconds (time-second arg) (time-fraction arg)))))
233
(declaim (ftype (function ((or spocq:date-time spocq:day-time-duration spocq:time)) t)
234
org.datagraph.spocq.evaluation:seconds))
238
(defmacro spocq.a::|timezone| (arg)
239
"( ( xsd:dateTime ) xsd:dayTimeDuration )
240
The function TIMEZONE returns the timezone part of arg as an xsd:dayTimeDuration. Raises an error if there is no timezone.
241
This function corresponds to fn:timezone-from-dateTime except for the treatment of literals with no timezone.
242
As all values are represented as zulu times, the function constantly returns
243
'PT0S'^^xsd:dayTimeDuration.
245
(setf (variable-opacity (expression-variables arg)) :transparent)
246
`(spocq.e:time-zone ,arg))
248
(defgeneric spocq.e:time-zone (term)
249
(:method ((term spocq:temporal))
250
;; all date-time values are canonicalized to zulu time
251
(let ((zone-offset (temporal-zone term)))
253
((nil) (invalid-argument-type spocq.a::|timezone| term spocq:temporal))
255
(t (multiple-value-bind (hours minutes) (truncate zone-offset)
256
(spocq:make-day-time-duration :fraction 0 :second 0 :minute minutes :hour hours :day 0))))))
257
(:method ((term spocq:duration))
258
(invalid-argument-type spocq.a::|timezone| term spocq:temporal))
260
(invalid-argument-type spocq.a::|timezone| term spocq:temporal)))
263
(define-compiler-macro spocq.a::|tz| (arg)
264
(setf (variable-opacity (expression-variables arg)) :transparent)
267
(defun spocq.a::|tz| (arg)
268
"( ( xsd:dateTime ) simpleLiteral )
269
The function TZ returns the timezone part of arg as an xsd:dayTimeDuration.
270
Raises an error if there is no timezone.
271
This function corresponds to fn:timezone-from-dateTime except for the treatment of literals with no timezone.
272
As all times are recorded as zulu times, the function constantly returns 'Z'"
275
(defgeneric spocq.e:tz (term)
276
(:method ((term spocq:temporal))
277
(with-output-to-string (stream) (spocq:format-zone-offset stream (temporal-zone term) nil)))
278
(:method ((term spocq:duration))
279
(invalid-argument-type spocq.a::|tz| term spocq:temporal))
281
(invalid-argument-type spocq.a::|tz| term spocq:date-time)))
287
(defmacro |xsd|:|date| (expression)
288
"( ( (or xsd:string numeric) ) xsd:date )"
289
(setf (variable-opacity (expression-variables expression)) :transparent)
290
`(spocq.e:date ,expression))
292
(defgeneric spocq.e:date (object)
293
(:method ((object string))
294
(let ((result (intern-term-aspects :literal object |xsd|:|date| nil)))
297
(t (call-next-method)))))
298
(:method ((object spocq:date-time))
299
(multiple-value-bind (fraction second minute hour day month year)
300
(decode-date-time object)
301
(declare (ignore fraction second minute hour))
302
(spocq:make-date :year year :month month :day day
304
(:method ((object spocq:date))
306
(:method ((object integer))
307
(timeline-location-date (* +timeline-units-per-second+ object)))
308
(:method ((object real))
309
(timeline-location-date (truncate (* +timeline-units-per-second+ object))))
310
(:method ((object t))
311
(invalid-argument-type |xsd|:|date| object |xsd|:|date|)))
313
(macrolet ((def-xpath-date-accessor (xpath-op implementation-op)
314
`(progn (defun ,xpath-op (term)
315
"( ( xsd:date ) xsd:integer )"
316
(,implementation-op term))
317
(define-compiler-macro ,xpath-op (expression)
318
(setf (variable-opacity (expression-variables expression)) :transparent)
319
(list ',implementation-op expression)))))
320
(def-xpath-date-accessor |fn|:|day-from-date| spocq.e:day)
321
(def-xpath-date-accessor |fn|:|month-from-date| spocq.e:month)
322
(def-xpath-date-accessor |fn|:|timezone-from-date| spocq.e:time-zone)
323
(def-xpath-date-accessor |fn|:|year-from-date| spocq.e:year))
325
(defmethod spocq.e:same-term ((term1 spocq:date) (term2 spocq:date))
326
"Compare the canonicalized term values. Strict term identity would require also that the zones match."
327
(= (date-timeline-location term1) (date-timeline-location term2)))
329
(defmethod spocq.e:= ((term1 spocq:date) (term2 spocq:date))
330
"Compare the canonicalized term values."
331
(= (date-timeline-location term1) (date-timeline-location term2)))
333
(defmethod spocq.e:< ((term1 spocq:date) (term2 spocq:date))
334
"Compare the canonicalized term values."
335
(< (date-timeline-location term1) (date-timeline-location term2)))
337
(defmethod spocq.e:<= ((term1 spocq:date) (term2 spocq:date))
338
"Compare the canonicalized term values."
339
(<= (date-timeline-location term1) (date-timeline-location term2)))
341
(defmethod spocq.e:|+| ((date spocq:date) (time spocq:time))
342
"Combine a time with a date sa a linear combiation, as the time should never exceed 24 hours.
343
Adopt the time zone unless the date is alreadz offset"
344
(let ((zone (date-zone date)))
345
;; if the date is not offset, allow the time to set the zone
346
(unless (offset-zone-p zone) (setf zone (or (time-zone time) spocq:+reference-zone+)))
347
(spocq:make-date-time :fraction (time-fraction time) :second (time-second time)
348
:minute (time-minute time) :hour (time-hour time)
349
:day (date-day date) :month (date-month date)
350
:year (date-year date)
353
(spocq:make-date-time :timeline-location (+ (date-timeline-location date) (time-timeline-location time))
356
(defmethod spocq.e:|+| ((time spocq:time) (date spocq:date))
357
(spocq.e:|+| date time))
359
(defmethod spocq.e:|+| ((date spocq:date) (duration spocq:day-time-duration))
360
"Combine the 00:00:00 date with the given time and normalize.
361
This requires deconstruction in order to effect the correct day/month normalization.
362
(see http://www.w3.org/TR/xpath-functions-30/#func-add-dayTimeDuration-to-date
363
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)
364
nb. to quote 'the time components are then discarded'."
365
(multiple-value-bind (s-fraction s-second s-minute s-hour s-day s-month s-year)
367
(multiple-value-bind (d-fraction d-second d-minute d-hour d-day)
368
(decode-day-time-duration duration)
369
;; no need to pin the day, as duration does not change the year or month
370
(multiple-value-bind (fraction second minute hour day month year)
371
(normalize-date-time-fragments (+ s-fraction d-fraction) (+ s-second d-second)
372
(+ s-minute d-minute) (+ s-hour d-hour)
373
(+ s-day d-day) s-month s-year)
374
(declare (ignore fraction second minute hour))
375
(spocq:make-date :minute s-minute :hour s-hour ; discard the result time fragments and reinstate the start zone
376
:day day :month month :year year :zone (spocq:date-zone date))))))
378
(defmethod spocq.e:|+| ((date spocq:date) (duration spocq:year-month-duration))
379
"Combine the 00:00:00 date with the given months and normalize.
380
This requires deconstruction in order to effect the correct day/month normalization.
381
(see http://www.w3.org/TR/xpath-functions-30/#func-add-yearMonthDuration-to-date
382
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)"
383
(multiple-value-bind (s-fraction s-second s-minute s-hour s-day s-month s-year)
385
(declare (ignore s-fraction s-second))
386
(multiple-value-bind (d-fraction d-second d-minute d-hour d-day d-month d-year)
387
(decode-year-month-duration duration)
388
(declare (ignore d-fraction d-second d-minute d-hour d-day))
389
(let ((e-month (+ s-month d-month))
390
(e-year (+ s-year d-year)))
391
(normalize-location-month-to-year e-month e-year)
392
(let* ((end-of-month-p (= s-day (date:month-days s-month s-year)))
393
(month-days (date:month-days e-month e-year))
394
(e-day (if end-of-month-p month-days (min s-day month-days)))) ; pin the day
395
(spocq:make-date :hour s-hour :minute s-minute
396
:day e-day :month e-month :year e-year :zone (spocq:date-zone date)))))))
398
;;; + commutes, - does not
399
(defmethod spocq.e:|+| ((term1 spocq:duration) (term2 spocq:date))
400
(spocq.e:|+| term2 term1))
403
(defmethod spocq.e:|-| ((date spocq:date) (duration spocq:day-time-duration))
404
"Combine the 00:00:00 date with the given time and normalize.
405
This requires deconstruction in order to effect the correct day/month normalization.
406
(see http://www.w3.org/TR/xpath-functions-30/#func-add-dayTimeDuration-to-date
407
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)"
408
(multiple-value-bind (s-fraction s-second s-minute s-hour s-day s-month s-year)
410
(multiple-value-bind (d-fraction d-second d-minute d-hour d-day)
411
(decode-day-time-duration duration)
412
;; no need to pin the day, as duration does not change the year or month
413
(multiple-value-bind (fraction second minute hour day month year)
414
(normalize-date-time-fragments (- s-fraction d-fraction)
415
(- s-second d-second)
416
(- s-minute d-minute) (- s-hour d-hour)
419
(declare (ignore fraction second minute hour))
420
(spocq:make-date :minute s-minute :hour s-hour ; discard the result time fragments and reinstate the start zone
421
:day day :month month :year year :zone (spocq:date-zone date))))))
423
(defmethod spocq.e:|-| ((date spocq:date) (duration spocq:year-month-duration))
424
"Combine the 00:00:00 date with the given months and normalize.
425
This requires deconstruction in order to effect the correct day/month normalization.
426
(see http://www.w3.org/TR/xpath-functions-30/#func-add-yearMonthDuration-to-date
427
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)"
428
(multiple-value-bind (s-fraction s-second s-minute s-hour s-day s-month s-year)
430
(declare (ignore s-fraction s-second))
431
(multiple-value-bind (d-fraction d-second d-minute d-hour d-day d-month d-year)
432
(decode-year-month-duration duration)
433
(declare (ignore d-fraction d-second d-minute d-hour d-day))
434
(let ((e-month (- s-month d-month))
435
(e-year (- s-year d-year)))
436
(normalize-location-month-to-year e-month e-year)
437
(let* ((end-of-month-p (= s-day (date:month-days s-month s-year)))
438
(month-days (date:month-days e-month e-year))
439
(e-day (if end-of-month-p month-days (min s-day month-days)))) ; pin the day
440
(spocq:make-date :hour s-hour :minute s-minute
441
:day e-day :month e-month :year e-year :zone (spocq:date-zone date)))))))
444
(defmethod spocq.e:|-| ((term1 spocq:date) (term2 spocq:date))
445
"Combine two dates based on their timeline locations. On one hand the XPath specification
446
refers to the algorithm for adding durations, of which this should be an inverse, but
447
as the result is in the dayTimeduration value domein, it is in seconds, which means
448
that algorithms's logic for pinned days and month normalization has no relvance.
449
first in the months domain, thn in the seconds domain
450
with the intermediate step to pin the result day.
451
This requires deconstruction in order to effect the correct day/month normalization.
452
(see http://www.w3.org/TR/xpath-functions-30/#func-subtract-DateTimes
453
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)"
454
(let ((timeline-difference (- (date-timeline-location term1) (date-timeline-location term2))))
455
(spocq:make-day-time-duration :second-location timeline-difference)))
458
(defmethod predicate-argument-type-error (operator (term1 spocq:date) (term2 t) &optional (type (spocq.a:|datatype| term1)))
459
(spocq.e:argument-type-error :operator operator :expected-type type :datum term2))
461
(defmethod predicate-argument-type-error (operator (term1 t) (term2 spocq:date) &optional (type (spocq.a:|datatype| term2)))
462
(predicate-argument-type-error operator term2 term1 type))
467
(defun |fn|:|dateTime| (date time)
468
"( ( xsd:date xsd:time ) xsd:dateTime )"
469
(spocq.e::+ date time))
471
(define-compiler-macro |fn|:|dateTime| (date time)
472
(setf (variable-opacity (expression-variables date)) :transparent)
473
(setf (variable-opacity (expression-variables time)) :transparent)
474
`(spocq.e::+ ,date ,time))
476
(defmacro |xsd|:|dateTime| (expression)
477
"( ( (or xsd:string numeric) ) xsd:dateTime )"
478
(setf (variable-opacity (expression-variables expression)) :transparent)
479
`(spocq.e:date-time ,expression))
481
(defgeneric spocq.e:date-time (object)
482
(:documentation "Construct a date-time term respective the given argument.
484
- iso-8601 format string
487
- unix timestamp integer
488
- float - of which just the integer component is converted")
489
(:method ((object string))
490
(let ((result (intern-term-aspects :literal object |xsd|:|dateTime| nil)))
492
(spocq:date-time result)
493
(t (call-next-method)))))
494
(:method ((object spocq:date-time))
496
(:method ((object spocq:date))
497
(multiple-value-bind (fraction second minute hour day month year)
499
(declare (ignore hour minute second fraction))
500
(spocq:make-date-time :year year :month month :day day
501
:fraction 0 :second 0 :minute 0 :hour 0)))
502
(:method ((object integer))
503
(timeline-location-date-time (* +timeline-units-per-second+ object)))
504
(:method ((object real))
505
(timeline-location-date-time (truncate (* +timeline-units-per-second+ object))))
506
(:method ((object t))
507
(invalid-argument-type |xsd|:|dateTime| object |xsd|:|dateTime|)))
509
(macrolet ((def-xpath-date-time-accessor (xpath-op implementation-op)
510
`(progn (defun ,xpath-op (term)
511
"( ( xsd:dateTime ) xsd:integer )"
512
(,implementation-op term))
513
(define-compiler-macro ,xpath-op (expression)
514
(setf (variable-opacity (expression-variables expression)) :transparent)
515
(list ',implementation-op expression)))))
516
(def-xpath-date-time-accessor |fn|:|day-from-dateTime| spocq.e:day)
517
(def-xpath-date-time-accessor |fn|:|hours-from-dateTime| spocq.e:hours)
518
(def-xpath-date-time-accessor |fn|:|minutes-from-dateTime| spocq.e:minutes)
519
(def-xpath-date-time-accessor |fn|:|month-from-dateTime| spocq.e:month)
520
(def-xpath-date-time-accessor |fn|:|seconds-from-dateTime| spocq.e:seconds)
521
(def-xpath-date-time-accessor |fn|:|timezone-from-dateTime| spocq.e:time-zone)
522
(def-xpath-date-time-accessor |fn|:|year-from-dateTime| spocq.e:year))
525
(defmethod spocq.e:same-term ((term1 spocq:date-time) (term2 spocq:date-time))
526
"Compare the canonicalized term values. Strict term identity would require also that the zones match."
527
(= (date-time-timeline-location term1) (date-time-timeline-location term2)))
529
(defmethod spocq.e:= ((term1 spocq:date-time) (term2 spocq:date-time))
530
"Compare the canonicalized term values."
531
(= (date-time-timeline-location term1) (date-time-timeline-location term2)))
533
(defmethod spocq.e:< ((term1 spocq:date-time) (term2 spocq:date-time))
534
"Compare the canonicalized term values."
535
(< (date-time-timeline-location term1) (date-time-timeline-location term2)))
537
(defmethod spocq.e:<= ((term1 spocq:date-time) (term2 spocq:date-time))
538
"Compare the canonicalized term values."
539
(<= (date-time-timeline-location term1) (date-time-timeline-location term2)))
543
(defmethod spocq.e:|+| ((date-time spocq:date-time) (duration spocq:day-time-duration))
544
"Combine the date-time with the given time duration and normalize.
545
This requires deconstruction in order to effect the correct day/month normalization.
546
(see http://www.w3.org/TR/xpath-functions-30/#func-add-dayTimeDuration-to-dateTime
547
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)"
548
(multiple-value-bind (s-fraction s-second s-minute s-hour s-day s-month s-year)
549
(decode-date-time date-time)
550
(multiple-value-bind (d-fraction d-second d-minute d-hour d-day)
551
(decode-day-time-duration duration)
552
;; no need to pin the day, as duration does not change the year or month
553
(multiple-value-bind (fraction second minute hour day month year)
554
(normalize-date-time-fragments (+ s-fraction d-fraction) (+ s-second d-second)
555
(+ s-minute d-minute) (+ s-hour d-hour)
558
(spocq:make-date-time :fraction fraction :second second :minute minute :hour hour
559
:day day :month month :year year :zone (spocq:date-time-zone date-time))))))
561
(defmethod spocq.e:|+| ((date-time spocq:date-time) (duration spocq:year-month-duration))
562
"Combine the date-time with the given time duration and normalize.
563
This requires deconstruction in order to effect the correct day/month normalization.
564
(see http://www.w3.org/TR/xpath-functions-30/#func-add-yearMonthDuration-to-dateTime
565
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)"
566
(multiple-value-bind (s-fraction s-second s-minute s-hour s-day s-month s-year)
567
(decode-date-time date-time)
568
(multiple-value-bind (d-fraction d-second d-minute d-hour d-day d-month d-year)
569
(decode-year-month-duration duration)
570
(declare (ignore d-fraction d-second d-minute d-hour d-day)) ; a duration as no time zone offset
571
(let ((e-month (+ s-month d-month))
572
(e-year (+ s-year d-year)))
573
(normalize-location-month-to-year e-month e-year)
574
(let* ((end-of-month-p (= s-day (date:month-days s-month s-year)))
575
(month-days (date:month-days e-month e-year))
576
(e-day (if end-of-month-p month-days (min s-day month-days)))) ; pin the day
577
(spocq:make-date-time :fraction s-fraction :second s-second :minute s-minute :hour s-hour
578
:day e-day :month e-month :year e-year :zone (spocq:date-time-zone date-time)))))))
580
;;; + commutes, - does not
581
(defmethod spocq.e:|+| ((term1 spocq:duration) (term2 spocq:date-time))
582
(spocq.e:|+| term2 term1))
585
(defmethod spocq.e:|-| ((date-time spocq:date-time) (duration spocq:day-time-duration))
586
"Combine the 00:00:00 date with the given time and normalize.
587
This requires deconstruction in order to effect the correct day/month normalization.
588
(see http://www.w3.org/TR/xpath-functions-30/#func-add-dayTimeDuration-to-date
589
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)"
590
(multiple-value-bind (s-fraction s-second s-minute s-hour s-day s-month s-year)
591
(decode-date-time date-time)
592
(multiple-value-bind (d-fraction d-second d-minute d-hour d-day)
593
(decode-day-time-duration duration)
594
;; no need to pin the day, as duration does not change the year or month
595
(multiple-value-bind (fraction second minute hour day month year)
596
(normalize-date-time-fragments (- s-fraction d-fraction) (- s-second d-second)
597
(- s-minute d-minute) (- s-hour d-hour)
600
(spocq:make-date-time :fraction fraction :second second :minute minute :hour hour
601
:day day :month month :year year :zone (spocq:date-time-zone date-time))))))
603
(defmethod spocq.e:|-| ((date-time spocq:date-time) (duration spocq:year-month-duration))
604
"Combine the 00:00:00 date with the given months and normalize.
605
This requires deconstruction in order to effect the correct day/month normalization.
606
(see http://www.w3.org/TR/xpath-functions-30/#func-add-yearMonthDuration-to-date
607
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)"
608
(multiple-value-bind (s-fraction s-second s-minute s-hour s-day s-month s-year)
609
(decode-date-time date-time)
610
(multiple-value-bind (d-fragment d-second d-minute d-hour d-day d-month d-year)
611
(decode-year-month-duration duration)
612
(declare (ignore d-fragment d-second d-minute d-hour d-day))
613
(let ((e-month (- s-month d-month))
614
(e-year (- s-year d-year)))
615
(normalize-location-month-to-year e-month e-year)
616
(let* ((end-of-month-p (= s-day (date:month-days s-month s-year)))
617
(month-days (date:month-days e-month e-year))
618
(e-day (if end-of-month-p month-days (min s-day month-days)))) ; pin the day
619
(spocq:make-date-time :fraction s-fraction :second s-second :hour s-hour :minute s-minute
620
:day e-day :month e-month :year e-year :zone (spocq:date-time-zone date-time)))))))
623
(defmethod spocq.e:|-| ((term1 spocq:date-time) (term2 spocq:date-time))
624
"Combine two full date-times basedon their timeline locations. On one hand the XPath specification
625
refers to the algorithm for adding durations, of which this should be an inverse, but
626
as the result is in the dayTimeduration value domein, it is in seconds, which means
627
that algorithms's logic for pinned days and month normalization has no relvance.
628
first in the months domain, thn in the seconds domain
629
with the intermediate step to pin the result day.
630
This requires deconstruction in order to effect the correct day/month normalization.
631
(see http://www.w3.org/TR/xpath-functions-30/#func-subtract-DateTimes
632
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)"
633
(let ((timeline-difference (- (date-time-timeline-location term1) (date-time-timeline-location term2))))
634
(spocq:make-day-time-duration :second-location timeline-difference)))
638
(defmethod predicate-argument-type-error (operator (term1 spocq:date-time) (term2 t) &optional (type (spocq.a:|datatype| term1)))
639
(spocq.e:argument-type-error :operator operator :expected-type type :datum term2))
641
(defmethod predicate-argument-type-error (operator (term1 t) (term2 spocq:date-time) &optional (type (spocq.a:|datatype| term2)))
642
(predicate-argument-type-error operator term2 term1 type))
646
;;; day-time-duration
648
(defmacro |xsd|:|dayTimeDuration| (expression)
649
"( ( (or xsd:string numeric) ) xsd:dayTimeDuration )"
650
(setf (variable-opacity (expression-variables expression)) :transparent)
651
`(spocq.e:day-time-duration ,expression))
653
(defgeneric spocq.e:day-time-duration (object)
654
(:method ((object string))
655
(let ((result (intern-term-aspects :literal object |xsd|:|dayTimeDuration| nil)))
657
(spocq:day-time-duration result)
658
(t (call-next-method)))))
659
(:method ((object spocq:day-time-duration))
661
(:method ((object spocq:temporal))
662
(make-day-time-duration 0 0 0 0 0))
664
(:method ((object integer))
665
(timeline-location-day-time-duration (* +timeline-units-per-second+ object)))
666
(:method ((object real))
667
(timeline-location-day-time-duration (truncate (* +timeline-units-per-second+ object))))
668
(:method ((object t))
669
(invalid-argument-type |xsd|:|dayTimeDuration| object |xsd|:|dayTimeDuration|)))
672
(defmethod spocq.e:same-term ((term1 spocq:day-time-duration) (term2 spocq:day-time-duration))
673
"Compare the canonicalized term values. Strict term identity would require also that the zones match."
674
(= (day-time-duration-second-location term1) (day-time-duration-second-location term2)))
676
(defmethod spocq.e:< ((term1 spocq:day-time-duration) (term2 spocq:day-time-duration))
677
(< (day-time-duration-second-location term1) (day-time-duration-second-location term2)))
679
(defmethod spocq.e:<= ((term1 spocq:day-time-duration) (term2 spocq:day-time-duration))
680
(<= (day-time-duration-second-location term1) (day-time-duration-second-location term2)))
682
(defmethod spocq.e:= ((term1 spocq:day-time-duration) (term2 spocq:day-time-duration))
683
(= (day-time-duration-second-location term1) (day-time-duration-second-location term2)))
685
(defmethod spocq.e:= ((term1 spocq:day-time-duration) (term2 spocq:day-time-duration))
686
"Compare the canonicalized term values."
687
(= (day-time-duration-second-location term1) (day-time-duration-second-location term2)))
689
(defmethod spocq.e:<= ((term1 spocq:day-time-duration) (term2 spocq:day-time-duration))
690
"Compare the canonicalized term values."
691
(<= (day-time-duration-second-location term1) (day-time-duration-second-location term2)))
694
(defmethod spocq.e:+ ((term1 spocq:day-time-duration) (term2 spocq:day-time-duration))
695
(spocq:make-day-time-duration :second-location (+ (day-time-duration-second-location term1)
696
(day-time-duration-second-location term2))
699
(defmethod spocq.e:- ((term1 spocq:day-time-duration) (term2 spocq:day-time-duration))
700
(spocq:make-day-time-duration :second-location (- (day-time-duration-second-location term1)
701
(day-time-duration-second-location term2))
704
(defmethod spocq.e:* ((term1 spocq:day-time-duration) (term2 number))
705
(spocq:make-day-time-duration :second-location (round (* (day-time-duration-second-location term1)
709
(defmethod spocq.e:* ((term1 number) (term2 spocq:day-time-duration))
710
(spocq:make-day-time-duration :second-location (round (* (day-time-duration-second-location term2)
714
(defmethod spocq.e:/ ((term1 spocq:day-time-duration) (term2 number))
715
(spocq:make-day-time-duration :second-location (round (/ (day-time-duration-second-location term1)
719
(defmethod spocq.e:/ ((term1 spocq:day-time-duration) (term2 spocq:day-time-duration))
720
(/ (day-time-duration-second-location term1)
721
(day-time-duration-second-location term2)))
725
(defmethod predicate-argument-type-error (operator (term1 spocq:day-time-duration) (term2 t) &optional (type (spocq.a:|datatype| term1)))
726
(spocq.e:argument-type-error :operator operator :expected-type type :datum term2))
728
(defmethod predicate-argument-type-error (operator (term1 t) (term2 spocq:day-time-duration) &optional (type (spocq.a:|datatype| term2)))
729
(predicate-argument-type-error operator term2 term1 type))
734
(defmacro |xsd|:|duration| (expression)
735
"( ( (or xsd:string numeric) ) xsd:duration )"
736
(setf (variable-opacity (expression-variables expression)) :transparent)
737
`(spocq.e:duration ,expression))
739
(defgeneric spocq.e:duration (object)
740
(:method ((object string))
741
(let ((result (intern-term-aspects :literal object |xsd|:|duration| nil)))
743
(spocq:duration result)
744
(t (call-next-method)))))
745
(:method ((object spocq:duration))
747
(:method ((object spocq:day-time-duration))
748
(spocq:make-duration :second-location (day-time-duration-second-location object)))
749
(:method ((object spocq:year-month-duration))
750
(spocq:make-duration :month-location (year-month-duration-month-location object)))
752
(:method ((object integer))
753
(timeline-location-duration (* +timeline-units-per-second+ object)))
754
(:method ((object real))
755
(timeline-location-duration (truncate (* +timeline-units-per-second+ object))))
757
(:method ((object t))
758
(invalid-argument-type |xsd|:|duration| object |xsd|:|duration|)))
761
(macrolet ((def-xpath-duration-accessor (xpath-op implementation-op documentation)
762
`(progn (defun ,xpath-op (term)
764
(,implementation-op term))
765
(define-compiler-macro ,xpath-op (expression)
766
(setf (variable-opacity (expression-variables expression)) :transparent)
767
(list ',implementation-op expression)))))
768
(def-xpath-duration-accessor |fn|:|seconds-from-duration| spocq.e:seconds
769
"( ( xsd:duration) xsd:rational )
770
Return the normalized second fragment from a duration.
771
For a yearMonth duration the result is constantly zero.
772
Available also as the SPARQL operator SECONDS.")
773
(def-xpath-duration-accessor |fn|:|minutes-from-duration| spocq.e:minutes
774
"( ( xsd:duration) xsd:integer )
775
Return the normalized minute fragment from a duration.
776
For a yearMonth duration the result is constantly zero.
777
Available also as the SPARQL operator MINUTES.")
778
(def-xpath-duration-accessor |fn|:|hours-from-duration| spocq.e:hours
779
"( ( xsd:duration) xsd:integer )
780
Return the normalized hour fragment from a duration.
781
For a yearMonth duration the result is constantly zero.
782
Available also as the SPARQL operator HOURS.")
783
(def-xpath-duration-accessor |fn|:|days-from-duration| spocq.e:day
784
"( ( xsd:duration) xsd:integer )
785
Return the normalized day fragment from a duration.
786
For a yearMonth duration the result is constantly zero.
787
Available also as the SPARQL operator DAY.")
788
(def-xpath-duration-accessor |fn|:|months-from-duration| spocq.e:month
789
"( ( xsd:duration) xsd:integer )
790
Return the normalized month fragment from a duration.
791
For a dayTimeMonth duration the result is constantly zero.
792
Available also as the SPARQL operator MONTH.")
793
(def-xpath-duration-accessor |fn|:|years-from-duration| spocq.e:year
794
"( ( xsd:duration) xsd:integer )
795
Return the normalized year fragment from a duration.
796
For a dayTimeMonth duration the result is constantly zero.
797
Available also as the SPARQL operator YEAR."))
799
(macrolet ((def-xpath-duration-accessor (xpath-op implementation-op documentation)
800
`(progn (defun ,xpath-op (term)
802
(,implementation-op term))
803
(define-compiler-macro ,xpath-op (expression)
804
(setf (variable-opacity (expression-variables expression)) :transparent)
805
(list ',implementation-op expression)))))
806
(def-xpath-duration-accessor |fn|:|duration-in-seconds| spocq.e::duration-in-seconds
807
"( ( xsd:dayTimeDuration ) xsd:rational )
808
Return the total duration in seconds as a function of all fragments.")
809
(def-xpath-duration-accessor |fn|:|duration-in-minutes| spocq.e::duration-in-minutes
810
"( ( xsd:dayTimeDuration ) xsd:rational )
811
Return the total duration in minutes as a function of all fragments.")
812
(def-xpath-duration-accessor |fn|:|duration-in-hours| spocq.e::duration-in-hours
813
"( ( xsd:dayTimeDuration ) xsd:rational )
814
Return the total duration in hours as a function of all fragments.")
815
(def-xpath-duration-accessor |fn|:|duration-in-days| spocq.e::duration-in-days
816
"( ( xsd:dayTimeDuration ) xsd:rational )
817
Return the total duration in days as a function of all fragments.")
818
(def-xpath-duration-accessor |fn|:|duration-in-months| spocq.e::duration-in-months
819
"( ( xsd:yearMonthDuration ) xsd:rational )
820
Return the total duration in months as a function of all fragments.")
821
(def-xpath-duration-accessor |fn|:|duration-in-years| spocq.e::duration-in-years
822
"( ( xsd:yearMonthDuration ) xsd:rational )
823
Return the total duration in minutes as a function of all fragments."))
825
(defgeneric spocq.e::duration-in-seconds (object)
826
(:method ((duration spocq:day-time-duration))
827
(/ (day-time-duration-second-location duration)
828
+timeline-units-per-second+))
829
(:method ((duration spocq:year-month-duration))
831
(:method ((duration string))
832
(cond ((parse-|PnDTnHnMnS| duration nil)
833
(/ (day-time-duration-second-location (|PnDTnHnMnS| duration))
834
+timeline-units-per-second+))
835
((parse-|PnYnM| duration nil)
838
(/ (duration-second-location (|PnYnMnDTnHnMnS| duration))
839
+timeline-units-per-second+)))))
841
(defun spocq.e::duration-in-minutes (object)
842
(/ (spocq.e::duration-in-seconds object) 60))
844
(defun spocq.e::duration-in-hours (object)
845
(/ (spocq.e::duration-in-seconds object) #.(* 60 60)))
847
(defun spocq.e::duration-in-days (object)
848
(/ (spocq.e::duration-in-seconds object) #.(* 24 60 60)))
850
(defgeneric spocq.e::duration-in-months (object)
851
(:method ((duration spocq:day-time-duration))
853
(:method ((duration spocq:year-month-duration))
854
(year-month-duration-month-location duration))
855
(:method ((duration string))
856
(cond ((parse-|PnYnM| duration nil)
857
(year-month-duration-month-location (|PnYnM| duration)))
858
((parse-|PnDTnHnMnS| duration nil)
861
(duration-month-location (|PnYnMnDTnHnMnS| duration))))))
863
(defun spocq.e::duration-in-years (object)
864
(/ (spocq.e::duration-in-months object) 12))
868
;;; (spocq.a:- (spocq.a:|now|) (spocq:make-date-time :universal-time (- (get-universal-time) 3600)))
870
(defmethod spocq.e:= ((term1 spocq:duration) (term2 spocq:duration))
871
"Given any combination of durations - whether the abstract type or different concrete types,
872
compare the respective second and month locations."
873
(and (= (duration-second-location term1) (duration-second-location term2))
874
(= (duration-month-location term1) (duration-month-location term2))))
876
(defmethod spocq.e:< ((term1 spocq:duration) (term2 spocq:duration))
879
(defmethod spocq.e:<= ((term1 spocq:duration) (term2 spocq:duration))
887
(defmacro |xsd|:|gDay| (expression)
888
"( ( (or xsd:string numeric) ) xsd:gDay )"
889
(setf (variable-opacity (expression-variables expression)) :transparent)
890
`(spocq.e:g-day ,expression))
892
(defgeneric spocq.e:g-day (object)
893
(:method ((object string))
894
(let ((result (intern-term-aspects :literal object |xsd|:|gDay| nil)))
897
(t (call-next-method)))))
898
(:method ((object spocq:g-day))
900
(:method ((object spocq:date))
901
(make-g-day (date-day object) (spocq:date-zone object)))
902
(:method ((object spocq:date-time))
903
(make-g-day (date-time-day object) (spocq:date-time-zone object)))
904
(:method ((object integer))
907
(make-g-day (timeline-location-day object))))
908
(:method ((day real))
909
(make-g-day (round day)))
910
(:method ((object t))
911
(invalid-argument-type |xsd|:|gDay| object |xsd|:|gDay|)))
913
(defmethod spocq.e:= ((term1 spocq:g-day) (term2 spocq:g-day))
914
;; zone equality is stipulated in the recommendation
915
;; http://www.w3.org/TR/xpath-functions-30/#func-gDay-equal
916
;; require comparable zones and equality to the minute to allow for zone offset
917
(and (zones-comparable-p (g-day-zone term1) (g-day-zone term2))
918
(= (spocq:g-day-day term1) (spocq:g-day-day term2))
919
(= (spocq:g-day-hour term1) (spocq:g-day-hour term2))
920
(= (spocq:g-day-minute term1) (spocq:g-day-minute term2))))
922
;;; no absolute ordering operators (see note at head)
923
(defmethod spocq.e:< ((term1 spocq:g-day) (term2 spocq:g-day))
924
(< (spocq:g-day-day term1) (spocq:g-day-day term2)))
925
(defmethod spocq.e:<= ((term1 spocq:g-day) (term2 spocq:g-day))
926
(or (spocq.e:= term1 term2)
927
(spocq.e:< term1 term2)))
930
(defmacro |xsd|:|gMonth| (expression)
931
"( ( (or xsd:string numeric) ) xsd:gMonth )"
932
(setf (variable-opacity (expression-variables expression)) :transparent)
933
`(spocq.e:g-month ,expression))
935
(defgeneric spocq.e:g-month (object)
936
(:method ((object string))
937
(let ((result (intern-term-aspects :literal object |xsd|:|gMonth| nil)))
939
(spocq:g-month result)
940
(t (call-next-method)))))
941
(:method ((object spocq:g-month))
943
(:method ((object spocq:date))
944
(make-g-month (date-month object) (spocq:date-zone object)))
945
(:method ((object spocq:date-time))
946
(make-g-month (date-time-month object) (spocq:date-time-zone object)))
947
(:method ((object integer))
949
(make-g-month object)
950
(make-g-month (timeline-location-month object))))
951
(:method ((object real))
952
(make-g-month (round object)))
953
(:method ((object t))
954
(invalid-argument-type |xsd|:|gMonth| object |xsd|:|gMonth|)))
956
(defmethod spocq.e:= ((term1 spocq:g-month) (term2 spocq:g-month))
957
;; require comparable zones and equality to the minute to allow for zone offset
958
(and (zones-comparable-p (g-month-zone term1) (g-month-zone term2))
959
(= (spocq:g-month-month term1) (spocq:g-month-month term2))
960
(= (spocq:g-month-day term1) (spocq:g-month-day term2))
961
(= (spocq:g-month-hour term1) (spocq:g-month-hour term2))
962
(= (spocq:g-month-minute term1) (spocq:g-month-minute term2))))
964
;;; no absolute ordering operators (see note at head)
965
(defmethod spocq.e:< ((term1 spocq:g-month) (term2 spocq:g-month))
966
(< (spocq:g-month-month term1) (spocq:g-month-month term2)))
967
(defmethod spocq.e:<= ((term1 spocq:g-month) (term2 spocq:g-month))
968
(or (spocq.e:= term1 term2)
969
(spocq.e:< term1 term2)))
972
(defmacro |xsd|:|gMonthDay| (expression)
973
"( ( (or xsd:string numeric) ) xsd:gMonthDay )"
974
(setf (variable-opacity (expression-variables expression)) :transparent)
975
`(spocq.e:g-month-day ,expression))
977
(defgeneric spocq.e:g-month-day (object)
978
(:method ((object string))
979
(let ((result (intern-term-aspects :literal object |xsd|:|gMonthDay| nil)))
981
(spocq:g-month-day result)
982
(t (call-next-method)))))
983
(:method ((object spocq:g-month-day))
985
(:method ((object spocq:date))
986
(make-g-month-day (date-day object) (spocq:date-month object) (spocq:date-zone object)))
987
(:method ((object spocq:date-time))
988
(make-g-month-day (date-time-day object) (spocq:date-time-month object) (spocq:date-time-zone object)))
989
(:method ((object integer))
990
(or (and (<= 1 object 1231)
991
(multiple-value-bind (month day) (truncate object 100)
992
(when (and (<= 1 month 12) (<= 1 day (date:month-days month)))
993
(make-g-month-day day month))))
994
(make-g-month-day (timeline-location-day object) (timeline-location-month object))))
995
(:method ((object real))
996
(spocq.e:g-month-day (round object)))
997
(:method ((object t))
998
(invalid-argument-type |xsd|:|gMonthDay| object |xsd|:|gMonthDay|)))
1000
(defmethod spocq.e:= ((term1 spocq:g-month-day) (term2 spocq:g-month-day))
1001
;; require both or neither to be zoned
1002
;; compare down to the minute to recognize zone offset
1003
(and (zones-comparable-p (spocq:g-month-day-zone term1) (spocq:g-month-day-zone term2))
1004
(= (spocq:g-month-day-month term1) (spocq:g-month-day-month term2))
1005
(= (spocq:g-month-day-day term1) (spocq:g-month-day-day term2))
1006
(= (spocq:g-month-day-hour term1) (spocq:g-month-day-hour term2))
1007
(= (spocq:g-month-day-minute term1) (spocq:g-month-day-minute term2))))
1009
;;; no absolute ordering operators (see note at head)
1010
(defmethod spocq.e:< ((term1 spocq:g-month-day) (term2 spocq:g-month-day))
1011
(if (= (spocq:g-month-day-month term1) (spocq:g-month-day-month term2))
1012
(< (spocq:g-month-day-day term1) (spocq:g-month-day-day term2))
1013
(< (spocq:g-month-day-month term1) (spocq:g-month-day-month term2))))
1014
(defmethod spocq.e:<= ((term1 spocq:g-month-day) (term2 spocq:g-month-day))
1015
(or (spocq.e:= term1 term2)
1016
(spocq.e:< term1 term2)))
1022
(defmacro |xsd|:|gYear| (expression)
1023
"( ( (or xsd:string numeric) ) xsd:gYear )"
1024
(setf (variable-opacity (expression-variables expression)) :transparent)
1025
`(spocq.e:g-year ,expression))
1027
(defgeneric spocq.e:g-year (object)
1028
(:method ((object string))
1029
(let ((result (intern-term-aspects :literal object |xsd|:|gYear| nil)))
1031
(spocq:g-year result)
1032
(t (call-next-method)))))
1033
(:method ((object spocq:g-year))
1035
(:method ((object spocq:date))
1036
(make-g-year (date-year object) (spocq:date-zone object)))
1037
(:method ((object spocq:date-time))
1038
(make-g-year (date-time-year object) (spocq:date-time-zone object)))
1039
(:method ((object integer))
1040
(if (<= 1 object 9999)
1041
(make-g-year object)
1042
(make-g-year (timeline-location-year object))))
1043
(:method ((object real))
1044
(make-g-year (round object)))
1045
(:method ((object t))
1046
(invalid-argument-type |xsd|:|gYear| object |xsd|:|gYear|)))
1048
(defmethod spocq.e:= ((term1 spocq:g-year) (term2 spocq:g-year))
1049
(and (zones-comparable-p (g-year-zone term1) (g-year-zone term2))
1050
(= (spocq:g-year-year term1) (spocq:g-year-year term2))
1051
(= (spocq:g-year-month term1) (spocq:g-year-month term2))
1052
(= (spocq:g-year-day term1) (spocq:g-year-day term2))
1053
(= (spocq:g-year-hour term1) (spocq:g-year-hour term2))
1054
(= (spocq:g-year-minute term1) (spocq:g-year-minute term2))))
1056
;;;; no absolute ordering operators (see note at head)
1057
(defmethod spocq.e:< ((term1 spocq:g-year) (term2 spocq:g-year))
1058
(< (spocq:g-year-year term1) (spocq:g-year-year term2)))
1059
(defmethod spocq.e:<= ((term1 spocq:g-year) (term2 spocq:g-year))
1060
(or (spocq.e:= term1 term2)
1061
(spocq.e:< term1 term2)))
1066
(defmacro |xsd|:|gYearMonth| (expression)
1067
"( ( (or xsd:string numeric) ) xsd:gYearMonth )"
1068
(setf (variable-opacity (expression-variables expression)) :transparent)
1069
`(spocq.e:g-year-month ,expression))
1071
(defgeneric spocq.e:g-year-month (object)
1072
(:method ((object string))
1073
(let ((result (intern-term-aspects :literal object |xsd|:|gYearMonth| nil)))
1075
(spocq:g-year-month result)
1076
(t (call-next-method)))))
1077
(:method ((object spocq:g-year-month))
1079
(:method ((object spocq:date))
1080
(make-g-year-month (date-month object) (spocq:date-year object) (spocq:date-zone object)))
1081
(:method ((object spocq:date-time))
1082
(make-g-year-month (date-time-month object) (spocq:date-time-year object) (spocq:date-time-zone object)))
1083
(:method ((object integer))
1084
(or (and (<= 1 object 999912)
1085
(let ((year (floor object 100))
1086
(month (mod object 100)))
1087
(when (and (<= 1 month 12) (<= 1 year 9999))
1088
(make-g-year-month month year))))
1089
(make-g-year-month (timeline-location-month object) (timeline-location-year object))))
1090
(:method ((object real))
1091
(spocq.e:g-year-month (round object)))
1092
(:method ((object t))
1093
(invalid-argument-type |xsd|:|gYearMonth| object |xsd|:|gYearMonth|)))
1095
(defmethod spocq.e:= ((term1 spocq:g-year-month) (term2 spocq:g-year-month))
1096
(and (zones-comparable-p (g-year-month-zone term1) (g-year-month-zone term2))
1097
(= (spocq:g-year-month-year term1) (spocq:g-year-month-year term2))
1098
(= (spocq:g-year-month-month term1) (spocq:g-year-month-month term2))
1099
(= (spocq:g-year-month-day term1) (spocq:g-year-month-day term2))
1100
(= (spocq:g-year-month-hour term1) (spocq:g-year-month-hour term2))
1101
(= (spocq:g-year-month-minute term1) (spocq:g-year-month-minute term2))))
1103
;;; no absolute ordering operators (see note at head)
1104
(defmethod spocq.e:< ((term1 spocq:g-year-month) (term2 spocq:g-year-month))
1105
(if (= (spocq:g-year-month-year term1) (spocq:g-year-month-year term2))
1106
(< (spocq:g-year-month-month term1) (spocq:g-year-month-month term2))
1107
(< (spocq:g-year-month-year term1) (spocq:g-year-month-year term2))))
1108
(defmethod spocq.e:<= ((term1 spocq:g-year-month) (term2 spocq:g-year-month))
1109
(or (spocq.e:= term1 term2)
1110
(spocq.e:< term1 term2)))
1117
(defmacro |xsd|:|time| (expression)
1118
"( ( (or xsd:string numeric) ) xsd:time )"
1119
(setf (variable-opacity (expression-variables expression)) :transparent)
1120
`(spocq.e:time ,expression))
1122
(defgeneric spocq.e:time (object)
1123
(:method ((object string))
1124
(let ((result (intern-term-aspects :literal object |xsd|:|time| nil)))
1127
(t (call-next-method)))))
1128
(:method ((object spocq:time))
1130
(:method ((object spocq:date))
1131
(spocq:make-time :hour 0 :minute 0 :second 0))
1132
(:method ((object spocq:date-time))
1133
(multiple-value-bind (fraction second minute hour)
1134
(decode-date-time object)
1135
(spocq:make-time :second second :minute minute :hour hour
1136
:fraction fraction)))
1137
(:method ((object integer))
1138
(timeline-location-time (* +timeline-units-per-second+ object)))
1139
(:method ((object real))
1140
(timeline-location-time (truncate (* +timeline-units-per-second+ object))))
1141
(:method ((object t))
1142
(invalid-argument-type |xsd|:|time| object |xsd|:|time|)))
1144
(macrolet ((def-xpath-time-accessor (xpath-op implementation-op)
1145
`(progn (defun ,xpath-op (term)
1146
"( ( xsd:time ) xsd:integer )"
1147
(,implementation-op term))
1148
(define-compiler-macro ,xpath-op (expression)
1149
(setf (variable-opacity (expression-variables expression)) :transparent)
1150
(list ',implementation-op expression)))))
1151
(def-xpath-time-accessor |fn|:|hours-from-time| spocq.e:hours)
1152
(def-xpath-time-accessor |fn|:|minutes-from-time| spocq.e:minutes)
1153
(def-xpath-time-accessor |fn|:|seconds-from-time| spocq.e:seconds)
1154
(def-xpath-time-accessor |fn|:|timezone-from-time| spocq.e:time-zone))
1157
(defmethod spocq.e:same-term ((term1 spocq:time) (term2 spocq:time))
1158
"Compare the canonicalized term values. Strict term identity would require also that the zones match."
1159
(= (time-timeline-location term1) (time-timeline-location term2)))
1161
(defmethod spocq.e:= ((term1 spocq:time) (term2 spocq:time))
1162
"Compare the canonicalized term values."
1163
(= (time-timeline-location term1) (time-timeline-location term2)))
1165
(defmethod spocq.e:< ((term1 spocq:time) (term2 spocq:time))
1166
"Compare the canonicalized term values."
1167
(< (time-timeline-location term1) (time-timeline-location term2)))
1169
(defmethod spocq.e:<= ((term1 spocq:time) (term2 spocq:time))
1170
"Compare the canonicalized term values."
1171
(<= (time-timeline-location term1) (time-timeline-location term2)))
1174
;;; should there be a distinct operator for the other order which does not truncate
1175
;;; https://www.w3.org/TR/xpath-functions-31/#func-add-dayTimeDuration-to-time
1176
(defmethod spocq.e:|+| ((term1 spocq:time) (term2 spocq:day-time-duration))
1177
(setf term2 (spocq::copy-day-time-duration term2))
1178
(setf (spocq::day-time-duration-day term2) 0
1179
(spocq:day-time-duration-second-location term2) nil)
1180
(let* ((time-location (time-second-location term1))
1181
(duration-location (day-time-duration-second-location term2))
1182
(result-location (+ time-location duration-location)))
1183
;; use the full second-fraction value and allow normalization
1184
(make-time result-location 0 0 0)))
1186
(defmethod spocq.e:|+| ((term1 spocq:day-time-duration) (term2 spocq:time))
1187
;(spocq.e:|+| term2 term1)
1188
(let* ((time-location (time-second-location term1))
1189
(duration-location (day-time-duration-second-location term2))
1190
(result-location (+ time-location duration-location)))
1191
;; use the full second-fraction value and allow normalization
1192
(spocq:make-day-time-duration :second-location result-location)))
1194
(defmethod spocq.e:|-| ((term1 spocq:time) (term2 spocq:time))
1195
"Combine two full date-times basedon their timeline locations. On one hand the XPath specification
1196
refers to the algorithm for adding durations, of which this should be an inverse, but
1197
as the result is in the dayTimeduration value domein, it is in seconds, which means
1198
that algorithms's logic for pinned days and month normalization has no relvance.
1199
first in the months domain, thn in the seconds domain
1200
with the intermediate step to pin the result day.
1201
This requires deconstruction in order to effect the correct day/month normalization.
1202
(see http://www.w3.org/TR/xpath-functions-30/#func-subtract-DateTimes
1203
and http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes)"
1204
(let ((timeline-difference (- (date-time-timeline-location term1) (date-time-timeline-location term2))))
1205
(spocq:make-day-time-duration :second-location timeline-difference)))
1207
;;; should there be a distinct operator for the other order which does not truncate
1208
;;; https://www.w3.org/TR/xpath-functions-31/#func-add-dayTimeDuration-to-time
1209
(defmethod spocq.e:|-| ((term1 spocq:time) (term2 spocq:day-time-duration))
1210
(setf term2 (spocq::copy-day-time-duration term2))
1211
(setf (spocq::day-time-duration-day term2) 0
1212
(spocq:day-time-duration-second-location term2) nil)
1213
(let* ((time-location (time-second-location term1))
1214
(duration-location (day-time-duration-second-location term2))
1215
(result-location (- time-location duration-location)))
1216
;; use the full second-fraction value and allow normalization
1217
(make-time result-location 0 0 0)))
1219
(defmethod spocq.e:|-| ((term1 spocq:day-time-duration) (term2 spocq:time))
1220
(let* ((time-location (time-second-location term1))
1221
(duration-location (day-time-duration-second-location term2))
1222
(result-location (- time-location duration-location)))
1223
;; use the full second-fraction value and allow normalization
1224
(spocq:make-day-time-duration :second-location result-location)))
1226
(defmethod predicate-argument-type-error (operator (term1 spocq:time) (term2 t) &optional (type (spocq.a:|datatype| term1)))
1227
(spocq.e:argument-type-error :operator operator :expected-type type :datum term2))
1229
(defmethod predicate-argument-type-error (operator (term1 t) (term2 spocq:time) &optional (type (spocq.a:|datatype| term2)))
1230
(predicate-argument-type-error operator term2 term1 type))
1234
;;; year-month-duration
1236
(defmacro |xsd|:|yearMonthDuration| (expression)
1237
"( ( (or xsd:string numeric) ) xsd:yearMonthDuration )"
1238
(setf (variable-opacity (expression-variables expression)) :transparent)
1239
`(spocq.e:year-month-duration ,expression))
1241
(defgeneric spocq.e:year-month-duration (object)
1242
(:method ((object string))
1243
(let ((result (intern-term-aspects :literal object |xsd|:|yearMonthDuration| nil)))
1245
(spocq:year-month-duration result)
1246
(t (call-next-method)))))
1247
(:method ((object spocq:year-month-duration))
1249
(:method ((object spocq:temporal))
1250
(make-year-month-duration 0 0))
1252
(:method ((object integer))
1253
(timeline-location-year-month-duration (* +timeline-units-per-second+ object)))
1254
(:method ((object real))
1255
(timeline-location-year-month-duration (truncate (* +timeline-units-per-second+ object))))
1256
(:method ((object t))
1257
(invalid-argument-type |xsd|:|dayTimeDuration| object |xsd|:|dayTimeDuration|)))
1260
(defmethod spocq.e:same-term ((term1 spocq:year-month-duration) (term2 spocq:year-month-duration))
1261
"Compare the canonicalized term values. Strict term identity would require also that the zones match."
1262
(= (year-month-duration-month-location term1) (year-month-duration-month-location term2)))
1264
(defmethod spocq.e:< ((term1 spocq:year-month-duration) (term2 spocq:year-month-duration))
1265
(< (year-month-duration-month-location term1) (year-month-duration-month-location term2)))
1267
(defmethod spocq.e:<= ((term1 spocq:year-month-duration) (term2 spocq:year-month-duration))
1268
(<= (year-month-duration-month-location term1) (year-month-duration-month-location term2)))
1270
(defmethod spocq.e:= ((term1 spocq:year-month-duration) (term2 spocq:year-month-duration))
1271
(= (year-month-duration-month-location term1) (year-month-duration-month-location term2)))
1273
(defmethod spocq.e:= ((term1 spocq:year-month-duration) (term2 spocq:year-month-duration))
1274
"Compare the canonicalized term values."
1275
(= (year-month-duration-month-location term1) (year-month-duration-month-location term2)))
1277
(defmethod spocq.e:<= ((term1 spocq:year-month-duration) (term2 spocq:year-month-duration))
1278
"Compare the canonicalized term values."
1279
(<= (year-month-duration-month-location term1) (year-month-duration-month-location term2)))
1282
(defmethod spocq.e:+ ((term1 spocq:year-month-duration) (term2 spocq:year-month-duration))
1283
(spocq:make-year-month-duration :month-location (+ (year-month-duration-month-location term1)
1284
(year-month-duration-month-location term2))
1285
:second-location 0))
1287
(defmethod spocq.e:- ((term1 spocq:year-month-duration) (term2 spocq:year-month-duration))
1288
(spocq:make-year-month-duration :month-location (- (year-month-duration-month-location term1)
1289
(year-month-duration-month-location term2))
1290
:second-location 0))
1292
(defmethod spocq.e:* ((term1 spocq:year-month-duration) (term2 number))
1293
(spocq:make-year-month-duration :month-location (round (* (year-month-duration-month-location term1)
1295
:second-location 0))
1297
(defmethod spocq.e:* ((term1 number) (term2 spocq:year-month-duration))
1298
(spocq:make-year-month-duration :month-location (round (* (year-month-duration-month-location term2)
1300
:second-location 0))
1302
(defmethod spocq.e:/ ((term1 spocq:year-month-duration) (term2 number))
1303
(spocq:make-year-month-duration :month-location (round (/ (year-month-duration-month-location term1)
1305
:second-location 0))
1307
(defmethod spocq.e:/ ((term1 spocq:year-month-duration) (term2 spocq:year-month-duration))
1308
(/ (year-month-duration-month-location term1)
1309
(year-month-duration-month-location term2)))
1312
(defmethod predicate-argument-type-error (operator (term1 spocq:year-month-duration) (term2 t) &optional (type (spocq.a:|datatype| term1)))
1313
(spocq.e:argument-type-error :operator operator :expected-type type :datum term2))
1315
(defmethod predicate-argument-type-error (operator (term1 t) (term2 spocq:year-month-duration) &optional (type (spocq.a:|datatype| term2)))
1316
(predicate-argument-type-error operator term2 term1 type))
1321
;;; temporal-proximity-by-exclusion-nex-1
1323
(setq *algebra-trace-output* (setq *data-trace-output* *trace-output*))
1324
(setq *bgp-trace-output* *trace-output*)
1327
PREFIX ex: <http://www.w3.org/2009/sparql/docs/tests/data-sparql11/negation#>
1328
PREFIX dc: <http://purl.org/dc/elements/1.1/>
1330
# The closest pre-operative physical examination
1331
SELECT ?exam ?date {
1332
?exam a ex:PhysicalExamination;
1334
ex:precedes ex:operation1 .
1335
?op a ex:SurgicalProcedure; dc:date ?opDT .
1337
?otherExam a ex:PhysicalExamination;
1339
ex:precedes ex:operation1
1343
:repository-id (lookup-repository-id :repository-name "negation-temporal-proximity-by-exclusion-nex-1" :account-name "jhacker"))