33#[ cfg( test) ]
44mod tests;
55
6- use std:: time:: { Duration , SystemTime } ;
7-
8- use anyhow:: Context ;
6+ use chrono:: { Duration , TimeZone , Utc } ;
97
108use crate :: { providers:: CatalystSignedDocumentProvider , CatalystSignedDocument } ;
119
@@ -16,11 +14,10 @@ pub(crate) struct IdRule;
1614impl IdRule {
1715 /// Validates document `id` field on the timestamps:
1816 /// 1. If `provider.future_threshold()` not `None`, document `id` cannot be too far in
19- /// the future (`future_threshold` arg) from `SystemTime::now()` based on the
20- /// provide threshold
21- /// 2. If `provider.future_threshold()` not `None`, document `id` cannot be too far
22- /// behind (`past_threshold` arg) from `SystemTime::now()` based on the provide
17+ /// the future (`future_threshold` arg) from `Utc::now()` based on the provided
2318 /// threshold
19+ /// 2. If `provider.past_threshold()` not `None`, document `id` cannot be too far
20+ /// behind (`past_threshold` arg) from `Utc::now()` based on the provided threshold
2421 #[ allow( clippy:: unused_async) ]
2522 pub ( crate ) async fn check < Provider > (
2623 & self ,
@@ -46,46 +43,46 @@ impl IdRule {
4643 . ok_or ( anyhow:: anyhow!( "Document `id` field must be a UUIDv7" ) ) ?
4744 . to_unix ( ) ;
4845
49- let Some ( id_time) =
50- SystemTime :: UNIX_EPOCH . checked_add ( Duration :: new ( id_time_secs, id_time_nanos) )
51- else {
52- doc. report ( ) . invalid_value (
53- "id" ,
54- & id. to_string ( ) ,
55- "Must a valid duration since `UNIX_EPOCH`" ,
56- "Cannot instantiate a valid `SystemTime` value from the provided `id` field timestamp." ,
57- ) ;
58- return Ok ( false ) ;
59- } ;
46+ let id_time = Utc
47+ . timestamp_opt ( i64:: try_from ( id_time_secs) . unwrap_or ( 0 ) , id_time_nanos)
48+ . single ( )
49+ . ok_or_else ( || anyhow:: anyhow!( "Invalid timestamp in document `id` field" ) ) ?;
50+
51+ let now = Utc :: now ( ) ;
6052
61- let now = SystemTime :: now ( ) ;
53+ let diff = id_time . signed_duration_since ( now ) ;
6254
63- if let Ok ( id_age ) = id_time . duration_since ( now ) {
64- // `now` is earlier than `id_time`
55+ if diff . num_nanoseconds ( ) . unwrap_or ( 0 ) > 0 {
56+ // id_time is in the future
6557 if let Some ( future_threshold) = provider. future_threshold ( ) {
66- if id_age > future_threshold {
58+ let threshold = Duration :: from_std ( future_threshold) ?;
59+ if diff > threshold {
6760 doc. report ( ) . invalid_value (
6861 "id" ,
6962 & id. to_string ( ) ,
7063 "id < now + future_threshold" ,
71- & format ! ( "Document Version timestamp {id} cannot be too far in future (threshold: {future_threshold:?}) from now: {now:?}" ) ,
64+ & format ! (
65+ "Document Version timestamp {id} cannot be too far in future (threshold: {threshold:?}) from now: {now:?}"
66+ ) ,
7267 ) ;
7368 is_valid = false ;
7469 }
7570 }
7671 } else {
77- // `id_time` is earlier than `now`
78- let id_age = now
79- . duration_since ( id_time)
80- . context ( "BUG! `id_time` must be earlier than `now` at this place" ) ?;
72+ // id_time is in the past
73+ // make positive duration
74+ let id_age = diff. abs ( ) ;
8175
8276 if let Some ( past_threshold) = provider. past_threshold ( ) {
83- if id_age > past_threshold {
77+ let threshold = Duration :: from_std ( past_threshold) ?;
78+ if id_age > threshold {
8479 doc. report ( ) . invalid_value (
8580 "id" ,
8681 & id. to_string ( ) ,
8782 "id > now - past_threshold" ,
88- & format ! ( "Document Version timestamp {id} cannot be too far behind (threshold: {past_threshold:?}) from now: {now:?}" , ) ,
83+ & format ! (
84+ "Document Version timestamp {id} cannot be too far behind (threshold: {threshold:?}) from now: {now:?}"
85+ ) ,
8986 ) ;
9087 is_valid = false ;
9188 }
0 commit comments