1/*  Part of SWI-Prolog
    2
    3    Author:        Willem Robert van Hage
    4    E-mail:        W.R.van.Hage@vu.nl
    5    WWW:           http://www.few.vu.nl/~wrvhage
    6    Copyright (c)  2010-2013, Vrije Universiteit Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35/*
   36    Internally, all time objects are stored as intervals.
   37    point(T) is translated to interval(T,T).
   38    If fuzzy time intervals,
   39    interval(EarlyBegin,LateBegin,EarlyEnd,LateEnd),
   40    should ever be implemented, this could be done by postprocessing
   41    over operations on an index containing interval(EarlyBegin,LateEnd).
   42
   43    Per handle there are two indices, one containing the begin points
   44    and the other containing the end points of the intervals.
   45    The index containing the end points actually contains negative time
   46    points to inverse the order of the index.
   47*/
   48
   49% TODO: Make version of atom_map that allows double type datums and
   50%       that has nondet search functions.
   51%       This would remove the need for the explicit EpochOffset and
   52%       would speed up all search predicates.
   53
   54:- module(timeindex,
   55          [  time_index/1,        % ?Index
   56             time_index/4,        % +Index, ?BeginIndex, ?EndIndex, ?Epoch
   57             time_setting/2,      % +Index ?Setting
   58             time_setting/1,      % ?Setting  (uses default index)
   59             time_assert/3,       % +URI, +Time, +Index
   60             time_assert/2,       % +URI, +Time  (uses default index)
   61             time_retract/3,      % +URI, +Time, +Index
   62             time_retract/2,      % +URI, +Time  (uses default index)
   63             time_clear/2,        % +Index, +NewEpochOffset
   64             time_clear/1,        % +Index
   65             time_clear/0,        % (uses default index)
   66             time_index_all/1,    % +Index
   67             time_index_all/0,    % (uses default index)
   68             time_bulkload/2,     % :CandidatePred, +Index
   69             time_bulkload/1,     % :CandidatePred
   70             time_intersects/3,   % +Time, -URI, +Index
   71             time_intersects/2,   % (uses default index)
   72             time_contains/3,     % +Time, -URI, +Index
   73             time_contains/2,     % (uses default index)
   74             time_prev_end/3,     % +Time, -URI, +Index
   75             time_prev_end/2,     % (uses default index)
   76             time_next_begin/3,   % +Time, -URI, +Index
   77             time_next_begin/2,   % (uses default index)
   78             uri_time/4,          % ?URI, ?Time, ?Source, +EpochOffset
   79             uri_time/3,          % ?URI, ?Time, ?Source (uses offset 0)
   80             uri_time/2,          % ?URI, ?Time (uses offset 0)
   81             parse_timestamp/3,   % +TimeStamp, -Epoch, +EpochOffset
   82             parse_timestamp/2,   % ?TimeStamp, ?Epoch (uses offset 0)
   83
   84             % Allen's Interval Algebra
   85             time_before/2,       % +Time, +Time
   86             time_after/2,
   87             time_meets/2,
   88             time_meets_inverse/2,
   89             time_overlaps/2,
   90             time_overlaps_inverse/2,
   91             time_starts/2,
   92             time_starts_inverse/2,
   93             time_during/2,
   94             time_during_inverse/2,
   95             time_finishes/2,
   96             time_finishes_inverse/2,
   97             time_equal/2,
   98
   99             % Time operations
  100             time_expand_before/3,   % +Time, +Duration, -Time
  101             time_expand_after/3,    % +Time, +Duration, -Time
  102             time_expand/3,          % +Time, +Duration, -Time
  103             time_duration/2,        % +Time, -Duration
  104             time_duration_before/3, % +Time, +Duration, -Time
  105             time_duration_after/3,  % +Time, +Duration, -Time
  106             time_duration/3,        % +Time, +Time, -Duration (epoch)
  107             time_between/3,         % +Time, +Time, -Duration (epoch)
  108
  109             timestamp_duration/2   % +TimeStamp, -Duration (duration(Y,M,D,H,Min,S))
  110          ]).  111
  112:- use_module(library(semweb/rdf_db)).  113:- use_module(library(semweb/rdf_litindex)).  114:- use_module(library(ordsets)).  115:- use_module(library(pairs)).  116
  117:- dynamic time_indices/4.  118
  119:- rdf_meta  time_index(r),
  120             time_index(r,?,?,?),
  121             time_setting(r,?),
  122             time_assert(r,?,r),
  123             time_assert(r,?),
  124             time_retract(r,?,r),
  125             time_retract(r,?),
  126             time_clear(r,?),
  127             time_clear(r),
  128             time_index_all(r),
  129             time_bulkload(?,r),
  130             time_intersects(?,r,r),
  131             time_intersects(?,r),
  132             time_contains(?,r,r),
  133             time_contains(?,r),
  134             time_prev_end(?,r,r),
  135             time_prev_end(?,r),
  136             time_next_begin(?,r,r),
  137             time_next_begin(?,r),
  138             uri_time(r,?,t,?),
  139             uri_time(r,?,t),
  140             uri_time(r,?).
  141
  142
  143time_index(Index) :- time_indices(Index,_,_,_).
  144
  145time_index(Index, IdxB, IdxE, Epoch) :-
  146    time_indices(Index, IdxB, IdxE, Epoch), !.
  147time_index(Index, IdxB, IdxE, Epoch) :-
  148    nonvar(Epoch),
  149    time_new(Index, Epoch),
  150    time_indices(Index, IdxB, IdxE, Epoch), !.
  151time_index(Index, IdxB, IdxE, Epoch) :-
  152    var(Epoch),
  153%   time_new(Index),
  154    time_indices(Index, IdxB, IdxE, Epoch), !.
 time_setting(?Option) is det
Sets/retrieves settings and current values of an index. Supported Options are: size(-N), N is the number of URI-Time pairs in the index. epoch(+Epoch), sets a new Epoch for the index, clears the index. epoch(-Epoch), Epoch is the current Epoch of the index.
  164time_setting(Option) :- time_setting(default, Option).
  165time_setting(Index, size(N)) :-
  166    time_indices(Index,B,_,_), !,
  167    rdf_statistics_literal_map(B,size(N,_)).
  168time_setting(Index, epoch(E)) :-
  169    var(E),
  170    time_indices(Index,_,_,E), !.
  171time_setting(Index, epoch(E)) :-
  172    nonvar(E),
  173    format('% Clearing index ~w, setting new Epoch to ~w\n', [Index,E]),
  174    time_clear(Index, E).
 time_new(+IndexName) is det
 time_new(+IndexName, +Offset) is det
Creates a new temporal index. Offset is the epoch used internally by the index.
  183time_new(Index) :- time_new(Index, 0).
  184time_new(Index, EpochOffset) :-
  185    rdf_new_literal_map(B),
  186    rdf_new_literal_map(E),
  187    assert(time_indices(Index, B, E, EpochOffset)).
 time_assert(+URI, +Time) is det
 time_assert(+URI, +Time, +IndexName) is det
Inserts a new URI-Time pair into the index.
  195time_assert(URI, T) :- time_assert(URI, T, default).
  196time_assert(URI, interval(TB, TE), Index) :-
  197    (   time_index(Index)
  198    ->  true
  199    ;   time_clear(Index, TB)
  200    ),
  201    time_index(Index, IdxB, IdxE, EpochOffset),
  202    TBE is integer(TB - EpochOffset),
  203    TEE is integer(-1 * (TE - EpochOffset)),
  204    rdf_insert_literal_map(IdxB, TBE, URI),
  205    rdf_insert_literal_map(IdxE, TEE, URI), !.
  206time_assert(URI, point(T), Index) :-
  207    time_assert(URI, interval(T,T), Index), !.
  208time_assert(URI, TimeExpr, Index) :-
  209    atom(TimeExpr),
  210    parse_timestamp(TimeExpr, T),
  211    time_assert(URI, point(T), Index), !.
  212time_assert(URI, TimeExpr, Index) :-
  213    number(TimeExpr),
  214    time_assert(URI, point(TimeExpr), Index), !.
 time_retract(+URI, +Time) is det
 time_retract(+URI, +Time, +IndexName) is det
Removes a URI-Time pair from the index.
  221time_retract(URI, T) :- time_retract(URI, T, default).
  222time_retract(URI, interval(TB, TE), Index) :-
  223    time_indices(Index, IdxB, IdxE, EpochOffset),
  224    TBE is integer(TB - EpochOffset),
  225    TEE is integer(-1 * (TE - EpochOffset)),
  226    rdf_delete_literal_map(IdxB, TBE, URI),
  227    rdf_delete_literal_map(IdxE, TEE, URI).
 time_clear(+IndexName, +NewOffset) is det
 time_clear(+IndexName) is det
 time_clear is det
Clears an index. Optionally sets a new epoch for the index that will be used for all future asserts into the index.
  236time_clear :- time_clear(default).
  237time_clear(Index) :-
  238    (   time_index(Index, IdxB, IdxE, OldEpochOffset)
  239    ->  retractall(time_indices(Index, _, _, _)),
  240        rdf_destroy_literal_map(IdxB),
  241        rdf_destroy_literal_map(IdxE),
  242        time_new(Index, OldEpochOffset)
  243    ;   true
  244    ).
  245time_clear(Index, NewEpochOffset) :-
  246    number(NewEpochOffset),
  247    (   time_index(Index, IdxB, IdxE, _OldEpochOffset)
  248    ->  retractall(time_indices(Index, _, _, _)),
  249        rdf_destroy_literal_map(IdxB),
  250        rdf_destroy_literal_map(IdxE)
  251    ;   true
  252    ),
  253    time_new(Index, NewEpochOffset), !.
 time_index_all(+IndexName) is det
 time_index_all is det
Adds all URI-Time pairs found by the uri_time predicate into the index.
  261time_index_all :- time_index_all(default).
  262time_index_all(Index) :- time_bulkload(uri_time, Index).
  263
  264:- meta_predicate time_bulkload(2), time_bulkload(2,+).
 time_bulkload(:CandidatePred, +Index) is det
 time_bulkload(:CandidatePred) is det
Like time_index_all, but indexes URI-Time pairs found by the custom predicate CandidatePred.
  272time_bulkload(CandidatePred) :- time_bulkload(CandidatePred, default).
  273time_bulkload(CandidatePred, Index) :-
  274    time_clear(Index),
  275    forall(call(CandidatePred, URI, Time),
  276           time_assert(URI, Time, Index)),
  277    time_index(Index,B,_,_),
  278    rdf_statistics_literal_map(B,size(K,_)),
  279    format('% Added ~w URI-Time pairs to ~w\n',[K,Index]).
 time_intersects(+Time, -URI, +Index) is nondet
 time_intersects(+Time, -URI) is nondet
Finds all URIs that have an indexed time interval that intersects with the interval Time = interval(Begin,End).

NB! The implementation currently does not return intervals that contain the query interval, hence the name time_intersects is currently a misnomer.

  292time_intersects(T, URI) :- time_intersects(T, URI, default).
  293
  294time_intersects(point(T), URI, Index) :-
  295    time_intersects(interval(T,T), URI, Index).
  296time_intersects(interval(TB, TE), URI, Index) :-
  297    time_intersects_impl1(interval(TB, TE), URI, Index).
  298% FIXME: buggy implementation, does not find intervals that start
  299% before the query interval and end after the query interval.
  300% The obvious solution would be to compute the intersection of the
  301% set of intervals ending after the begin of the query and the
  302% set of intervals starting before the end of the query, but that
  303% is a very expensive query.
  304% As soon as there is a nondet version of rdf_keys_in_literal_map
  305% this implementation could become viable if the hard solutions are
  306% delayed until after all easy solutions have been found.
  307time_intersects_impl1(interval(TB, TE), URI, Index) :-
  308    time_index(Index, IdxB, IdxE, EO),
  309    parse_timestamp(TB, TBE, EO),
  310    parse_timestamp(TE, TEE, EO),
  311    TBI is integer(TBE),
  312    TEI is integer(TEE),
  313    rdf_keys_in_literal_map(IdxB, between(TBI, TEI), BeginMatch),
  314    rdf_litindex:list_to_or(BeginMatch, between(TBI, TEI), BeginOr),
  315    rdf_litindex:lookup(BeginOr, IdxB, B2, B3),
  316    match_results(B2, B3, B4),
  317    TBR is integer(-1 * TBE),
  318    TER is integer(-1 * TEE),
  319    rdf_keys_in_literal_map(IdxE, between(TER, TBR), EndMatch),
  320    rdf_litindex:list_to_or(EndMatch, between(TER, TBR), EndOr),
  321    rdf_litindex:lookup(EndOr, IdxE, E2, E3),
  322    match_results(E2, E3, E4),
  323    append(E4, B4, Matches),
  324    % predsort(ord, E4B4, Matches), !,
  325    pairs_values(Matches, Values), !,
  326    list_to_set(Values, ValueSet),
  327    member(URI, ValueSet).
 time_contains(+Time, -URI, +Index) is nondet
 time_contains(+Time, -URI) is nondet
Finds all URIs that have an indexed time interval that are contained by the interval Time = interval(Begin,End).
  335time_contains(T, URI) :- time_contains(T, URI, default).
  336time_contains(interval(-,-), URI, Index) :- !,
  337    time_contains_all(interval(-,-), URI, Index).
  338time_contains(interval(-,End), URI, Index) :- !,
  339    time_contains_le(interval(-,End), URI, Index).
  340time_contains(interval(Begin,-), URI, Index) :- !,
  341    time_contains_ge(interval(Begin,-), URI, Index).
  342time_contains(interval(TB, TE), URI, Index) :-
  343    time_index(Index, IdxB, IdxE, EO),
  344    parse_timestamp(TB, TBE, EO),
  345    TBI is integer(TBE),
  346    TBR is integer(-1 * TBE),
  347    parse_timestamp(TE, TEE, EO),
  348    TEI is integer(TEE),
  349    TER is integer(-1 * TEE),
  350    rdf_keys_in_literal_map(IdxB, between(TBI, TEI), BeginMatch),
  351    rdf_litindex:list_to_or(BeginMatch, between(TBI, TEI), BeginOr),
  352    rdf_litindex:lookup(BeginOr, IdxB, B2, B3),
  353    match_results(B2, B3, B4),
  354    rdf_keys_in_literal_map(IdxE, between(TER, TBR), EndMatch),
  355    rdf_litindex:list_to_or(EndMatch, between(TER, TBR), EndOr),
  356    rdf_litindex:lookup(EndOr, IdxE, E2, E3),
  357    match_results(E2, E3, E4),
  358    predsort(ord, B4, BS),
  359    predsort(rev, E4, ES),
  360    pairs_values(BS, BSValues),
  361    pairs_values(ES, ESValues),
  362    ord_intersection(BSValues, ESValues, Matches), !,
  363    member(URI, Matches).
  364
  365lookup(Index,Key,A,B) :-
  366    rdf_litindex:lookup(Key, Index, [A], [B]).
  367
  368time_contains_all(interval(-,-), URI, Index) :-
  369    time_index(Index, IdxB, IdxE, _EO),
  370    rdf_keys_in_literal_map(IdxB, all, BeginMatch),
  371    rdf_keys_in_literal_map(IdxE, all, EndMatch),
  372    maplist(lookup(IdxB),BeginMatch,BM1,BM2),
  373    maplist(lookup(IdxB),EndMatch,EM1,EM2),
  374    match_results(BM1, BM2, BMatches),
  375    match_results(EM1, EM2, EMatches),
  376    pairs_values(BMatches, BSValues),
  377    pairs_values(EMatches, ESValues),
  378    append(BSValues, ESValues, Matches2),
  379    list_to_set(Matches2,Matches), !,
  380    member(URI, Matches).
  381time_contains_le(interval(-, TE), URI, Index) :-
  382    time_prev_end(point(TE), URI, Index).
  383time_contains_ge(interval(TB, -), URI, Index) :-
  384    time_next_begin(point(TB), URI, Index).
 time_prev_end(+Time, -URI, +Index) is nondet
 time_prev_end(+Time, -URI) is nondet
Finds all URIs that have an indexed time interval that ends before time point or interval Time in order of increasing duration.
  393time_prev_end(point(T), URI) :- time_prev_end(interval(T,T), URI, default).
  394time_prev_end(interval(T,T1), URI) :- time_prev_end(interval(T,T1), URI, default).
  395time_prev_end(point(T), URI, Index) :- time_prev_end(interval(T,T), URI, Index).
  396time_prev_end(interval(_,T), URI, Index) :-
  397    time_index(Index, _, IdxE, EO),
  398    parse_timestamp(T, TE, EO),
  399    TER is integer(-1 * TE),
  400    rdf_keys_in_literal_map(IdxE, ge(TER), EndMatch),
  401    rdf_litindex:list_to_or(EndMatch, ge(TER), EndOr),
  402    rdf_litindex:lookup(EndOr, IdxE, E2, E3),
  403    match_result(E2, E3, _-URI).
 time_next_begin(+Time, -URI, +Index) is nondet
 time_next_begin(+Time, -URI) is nondet
Finds all URIs that have an indexed time interval that begins after time point or interval Time in order of increasing duration.
  412time_next_begin(point(T), URI) :- time_next_begin(interval(T,T), URI, default).
  413time_next_begin(interval(T0,T), URI) :- time_next_begin(interval(T0,T), URI, default).
  414time_next_begin(point(T), URI, Index) :- time_next_begin(interval(T,T), URI, Index).
  415time_next_begin(interval(T,_), URI, Index) :-
  416    time_index(Index, IdxB, _, EO),
  417    parse_timestamp(T, TE, EO),
  418    TEI is integer(TE),
  419    rdf_keys_in_literal_map(IdxB, ge(TEI), BeginMatch),
  420    rdf_litindex:list_to_or(BeginMatch, ge(TEI), BeginOr),
  421    rdf_litindex:lookup(BeginOr, IdxB, B2, B3),
  422    match_result(B2, B3, _-URI).
  423
  424zip_tree_member([H0|T0], [H1|T1], R) :-
  425    is_list(H0),
  426    (   zip_tree_member(H0, H1, R)
  427    ;   zip_tree_member(T0, T1, R)
  428    ).
  429zip_tree_member([[H0]], [H1|T1], R) :-
  430    (   member(H, H1),
  431        R = H0-H
  432    ;   zip_tree_member([H0], T1, R)
  433    ).
  434
  435zip_tree(_, [], []).
  436zip_tree(A, B, A-B) :- \+is_list(A).
  437zip_tree([], [B], nil-B).
  438zip_tree([H0], [H1|T1], [H|T]) :-
  439    \+is_list(H0),
  440    zip_tree(H0, H1, H),
  441    zip_tree([H0], T1, T).
  442zip_tree([H0|T0], [H1|T1], [H|T]) :-
  443    zip_tree(H0, H1, H),
  444    zip_tree(T0, T1, T).
  445
  446match_results(A, B, C) :-
  447    zip_tree(A, B, Z),
  448    flatten(Z, C).
  449match_result(A, B, C) :-
  450    zip_tree_member(A, B, C).
  451
  452ord(>, between(_,_,A)-_, between(_,_,B)-_) :- A > B.
  453ord(<, between(_,_,A)-_, between(_,_,B)-_) :- A < B.
  454ord(>, le(_,A)-_, le(_,B)-_) :- A > B.
  455ord(<, le(_,A)-_, le(_,B)-_) :- A < B.
  456ord(=, _, _).
  457rev(>, between(_,_,A)-_, between(_,_,B)-_) :- A < B.
  458rev(<, between(_,_,A)-_, between(_,_,B)-_) :- A > B.
  459rev(>, le(_,A)-_, le(_,B)-_) :- A < B.
  460rev(>, le(_,A)-_, le(_,B)-_) :- A > B.
  461rev(=, _, _).
 uri_time(?URI, ?Time, ?Source, +Offset) is semidet
 uri_time(?URI, ?Time, ?Source) is semidet
 uri_time(?URI, ?Time) is semidet
Finds all URI-Time pairs described in the RDF database. Source matches the graphs in which the pair is described. Optionally, the Begin and End of the time interval are returned with respect to a given Offset.
  472uri_time(URI, interval(Begin, End)) :- uri_time(URI, interval(Begin, End), _Source, 0).
  473uri_time(URI, interval(Begin, End), Source) :- uri_time(URI, interval(Begin, End), Source, 0).
  474uri_time(URI, interval(Begin, End), Source, EpochOffset) :-
  475    time_candidate(URI, interval(TB,TE), Source),
  476    parse_timestamp(TB, Begin0),
  477    parse_timestamp(TE, End0),
  478    Begin is Begin0 - EpochOffset,
  479    End is End0 - EpochOffset.
  480
  481:- rdf_register_ns(sem, 'http://semanticweb.cs.vu.nl/2009/11/sem/').  482:- rdf_register_ns(owltime, 'http://www.w3.org/2006/time#').  483
  484time_candidate(URI, TimeStamp) :- time_candidate(URI, TimeStamp, _Source).
  485time_candidate(URI, TimeStamp, Source) :-
  486    owl_time_xsd_candidate(URI, TimeStamp, Source).
  487time_candidate(URI, TimeStamp, Source) :-
  488    sem_time_candidate(URI, TimeStamp, Source).
  489
  490sem_time_candidate(URI, TimeStamp) :- sem_time_candidate(URI, TimeStamp, _Source).
  491sem_time_candidate(URI, interval(Begin,End), Source) :-
  492    nonvar(Source),
  493    (   rdf(URI, sem:hasBeginTimeStamp, literal(TB), Source)
  494    ->  (   TB = type(_,Begin)
  495        ->  true
  496        ;   Begin = TB
  497        )
  498    ;   Begin = -
  499    ),
  500    (   rdf(URI, sem:hasEndTimeStamp, literal(TE), Source)
  501    ->  (   TE = type(_,End)
  502        ->  true
  503        ;   End = TE
  504        )
  505    ;   End = -
  506    ),
  507    (   Begin = -, End = -
  508    ->  fail
  509    ;   true
  510    ).
  511sem_time_candidate(URI, interval(Begin,End), Source) :-
  512    var(Source),
  513    (   rdf(URI, sem:hasBeginTimeStamp, literal(TB), Source1)
  514    ->  (   TB = type(_,Begin)
  515        ->  true
  516        ;   Begin = TB
  517        )
  518    ;   Begin = -
  519    ),
  520    (   Source1 = Source:_
  521    ->  true
  522    ;   Source = Source1
  523    ),
  524    (   rdf(URI, sem:hasEndTimeStamp, literal(TE), Source)
  525    ->  (   TE = type(_,End)
  526        ->  true
  527        ;   End = TE
  528        )
  529    ;   End = -
  530    ),
  531    (   Begin = -, End = -
  532    ->  fail
  533    ;   true
  534    ).
  535
  536sem_time_candidate(URI, interval(TimeStamp,TimeStamp), Source) :-
  537    rdf(URI, sem:hasTimeStamp, literal(L), Source),
  538    (   L = type(_,TimeStamp)
  539    ->  true
  540    ;   L = TimeStamp
  541    ).
  542
  543owl_time_xsd_candidate(URI, interval(TimeStamp,TimeStamp), Source) :-
  544    rdf(URI, owltime:inXSDDateTime, literal(L), Source),
  545    (   L = type(_,TimeStamp)
  546    ->  true
  547    ;   L = TimeStamp
  548    ).
 parse_timestamp(?TimeStampAtom, ?EpochTimeStamp) is det
Converts in both directions between a literal time representation and a numerical time representation based on the epoch. The format of the generated atom is ISO 8601 in UTC.
  557parse_timestamp(TimeStamp, Epoch) :-
  558    var(TimeStamp),
  559    number(Epoch),
  560    stamp_date_time(Epoch, Date, 'UTC'),
  561    format_time(atom(TimeStamp), '%FT%TZ', Date), !.
  562parse_timestamp(TimeStamp, Epoch) :- parse_timestamp(TimeStamp, Epoch, 0).
 parse_timestamp(+TimeStampAtom, -Epoch, +EpochOffset) is det
Converts between a literal TimeStamp atom and a numerical time representation based on the epoch - EpochOffset. This allows for a more fine grained representation of time for time points far away from the epoch.
  571parse_timestamp(TimeStamp, Epoch, EpochOffset) :-
  572    nonvar(TimeStamp),
  573    (   number(TimeStamp)
  574    ->  E = TimeStamp
  575    ;   atom(TimeStamp), iso_timestamp_epoch(TimeStamp, E), !
  576    ;   atom(TimeStamp), sic_timestamp_epoch(TimeStamp, E), !
  577    ;   \+atom(TimeStamp), timex_timestamp_epoch(TimeStamp, E), !
  578        % extend here
  579    ),
  580    Epoch is E - EpochOffset.
  581
  582
  583iso_timestamp_epoch(TimeStamp, T) :-
  584    parse_time(TimeStamp, T).
  585
  586sic_timestamp_epoch(TimeStamp, T) :-
  587    atom_number(TimeStamp, T).
  588
  589timex_timestamp_epoch(type(Type,TimeStamp), T) :-
  590    rdf_equal(Type, rdf:'XMLLiteral'),
  591    xml_timestamp(TimeStamp, T).
  592
  593xml_timestamp(TimeStamp, T) :-
  594    (   xpath(TimeStamp, //(timex2), element(_,Attr,_)) % plain XML timex2 tag
  595    ->  true
  596    ;   xpath(TimeStamp, //(_:timex2), element(_,Attr,_)) % timex2 in some namespace
  597    ), !,
  598    memberchk('VAL'=ISO, Attr),
  599    parse_time(ISO, T). % Doesn't deal with local time
  600xml_timestamp(TimeStamp, T) :-
  601    (   xpath(TimeStamp, //(timex3), element(_,Attr,_)) % plain XML timex3 tag
  602    ->  true
  603    ;   xpath(TimeStamp, //(_:timex3), element(_,Attr,_)) % timex3 in some namespace
  604    ), !,
  605    memberchk(value=ISO, Attr),
  606    parse_time(ISO, T). % Doesn't deal with local time
  607
  608
  609/*
  610 *  Allen's interval algebra
  611 */
  612
  613% assumes point(T) is equal to interval(T,T)
  614
  615time_to_interval(interval(T1,T2), interval(T1,T2)) :-
  616    number(T1), number(T2), !.
  617time_to_interval(point(T), interval(T,T)) :-
  618    number(T), !.
  619time_to_interval(interval(T1,T2), interval(T3,T4)) :-
  620    parse_timestamp(T1,T3),
  621    parse_timestamp(T2,T4), !.
  622time_to_interval(point(T1), interval(T2,T2)) :-
  623    parse_timestamp(T1,T2), !.
  624time_to_interval(T1, interval(T2,T2)) :-
  625    parse_timestamp(T1,T2), !.
  626
  627
  628time_overlaps(Ta, Tb) :-
  629    time_to_interval(Ta,interval(T1,T2)),
  630    time_to_interval(Tb,interval(T3,T4)),
  631    T1 =< T4,
  632    T2 >= T3.
  633
  634time_overlaps_inverse(T1,T2) :- time_overlaps(T2,T1).
  635
  636time_during(Ta, Tb) :-
  637    time_to_interval(Ta, interval(T1,T2)),
  638    time_to_interval(Tb, interval(T3,T4)),
  639    T1 =< T3,
  640    T2 >= T4.
  641
  642time_during_inverse(T1,T2) :- time_during(T2,T1).
  643
  644time_before(Ta, Tb) :-
  645    time_to_interval(Ta, interval(_,T1)),
  646    time_to_interval(Tb, interval(T2,_)),
  647    T1 < T2.
  648
  649time_after(T1,T2) :- time_before(T2,T1).
  650
  651time_meets(Ta, Tb) :-
  652    time_to_interval(Ta, interval(_,T)),
  653    time_to_interval(Tb, interval(T,_)).
  654
  655time_meets_inverse(T1,T2) :- time_meets_inverse(T2,T1).
  656
  657time_starts(Ta, Tb) :-
  658    time_to_interval(Ta, interval(T,T1)),
  659    time_to_interval(Tb, interval(T,T2)),
  660    T1 =< T2.
  661
  662time_starts_inverse(T1,T2) :- time_starts(T2,T1).
  663
  664time_finishes(Ta, Tb) :-
  665    time_to_interval(Ta, interval(T1,T)),
  666    time_to_interval(Tb, interval(T2,T)),
  667    T1 >= T2.
  668
  669time_finishes_inverse(T1,T2) :- time_finishes(T2,T1).
  670
  671time_equal(T,T) :- time_to_interval(T,_).
  672
  673
  674/*
  675 *  Operations on Time points and intervals
  676 */
  677
  678time_expand_before(T, Margin, interval(T1,Tb)) :-
  679    time_to_interval(T, interval(Ta,Tb)),
  680    T1 is Ta - Margin.
  681
  682time_expand_after(T, Margin, interval(Ta,T1)) :-
  683    time_to_interval(T, interval(Ta,Tb)),
  684    T1 is Tb + Margin.
  685
  686time_expand(T, Margin, interval(T1,T2)) :-
  687    time_to_interval(T, interval(Ta,Tb)),
  688    T1 is Ta - Margin,
  689    T2 is Tb + Margin.
  690
  691time_duration(T, D) :-
  692    time_to_interval(T, interval(Ta,Tb)),
  693    D is Tb - Ta.
  694
  695time_duration_before(T, D, point(T2)) :-
  696    time_to_interval(T,interval(T1,_)),
  697    T2 is T1 - D.
  698
  699time_duration_after(T, D, point(T2)) :-
  700    time_to_interval(T,interval(_,T1)),
  701    T2 is T1 + D.
  702
  703time_duration(Ta, Tb, D) :-
  704    time_to_interval(Ta, interval(T0,T1)),
  705    time_to_interval(Tb, interval(T2,T3)),
  706    (   time_before(interval(T0,T1), interval(T2,T3))
  707    ->  time_duration(interval(T0,T3), D)
  708    ;   time_duration(interval(T2,T1), D0),
  709        D is -1 * D0
  710    ).
  711
  712time_between(Ta, Tb, D) :-
  713    time_to_interval(Ta, interval(T0,T1)),
  714    time_to_interval(Tb, interval(T2,T3)),
  715    (   time_before(interval(T0,T1), interval(T2,T3))
  716    ->  time_duration(interval(T1,T2), D)
  717    ;   time_duration(interval(T3,T0), D0),
  718        D is -1 * D0
  719    ).
  720
  721timestamp_duration(TimeStamp, duration(Years,Months,Days,Hours,Minutes,Seconds)) :-
  722    abs(TimeStamp,T),
  723%   sign(TimeStamp,Sign),
  724    stamp_date_time(0, date(Y0,M0,D0,H0,Mi0,S0,_,_,_), 0),
  725    stamp_date_time(T, date(Y1,M1,D1,H1,Mi1,S1,_,_,_), 0),
  726    Years is Y1 - Y0,
  727    Months is M1 - M0,
  728    Days is D1 - D0,
  729    Hours is H1 - H0,
  730    Minutes is Mi1 - Mi0,
  731    Seconds is S1 - S0.
  732
  733parse_duration(ISO, duration(Y,M,D,H,Mi,S)) :-
  734    phrase(duration(Y,M,D,H,Mi,S),Ts),
  735    atom_codes(ISO,Ts), !.
  736
  737
  738% does not follow the entire ISO 8601 spec yet
  739duration(Y,M,D,H,Mi,S) -->
  740    { nonvar(Y),
  741      number_codes(Y,Yc),
  742      number_codes(M,Mc),
  743      number_codes(D,Dc),
  744      number_codes(H,Hc),
  745      number_codes(Mi,Mic),
  746      number_codes(S,Sc)
  747    },
  748    "P", Yc, "Y", Mc, "M", Dc, "D", "T", Hc, "H", Mic, "M", Sc, "S".
  749
  750duration(Y,M,D,H,Mi,S) -->
  751    { var(Y) },
  752    "P", Yc, "Y", Mc, "M", Dc, "D", "T", Hc, "H", Mic, "M", Sc, "S",
  753    {
  754      number_codes(Y,Yc),
  755      number_codes(M,Mc),
  756      number_codes(D,Dc),
  757      number_codes(H,Hc),
  758      number_codes(Mi,Mic),
  759      number_codes(S,Sc)
  760    }