1use core::cmp;
25
26use crate::Uuid;
27
28pub const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub struct Timestamp {
44 seconds: u64,
45 subsec_nanos: u32,
46 counter: u128,
47 usable_counter_bits: u8,
48}
49
50impl Timestamp {
51 #[cfg(feature = "std")]
55 pub fn now(context: impl ClockSequence<Output = impl Into<u128>>) -> Self {
56 let (seconds, subsec_nanos) = now();
57
58 let (counter, seconds, subsec_nanos) =
59 context.generate_timestamp_sequence(seconds, subsec_nanos);
60 let counter = counter.into();
61 let usable_counter_bits = context.usable_bits() as u8;
62
63 Timestamp {
64 seconds,
65 subsec_nanos,
66 counter,
67 usable_counter_bits,
68 }
69 }
70
71 pub const fn from_gregorian(ticks: u64, counter: u16) -> Self {
80 let (seconds, subsec_nanos) = Self::gregorian_to_unix(ticks);
81
82 Timestamp {
83 seconds,
84 subsec_nanos,
85 counter: counter as u128,
86 usable_counter_bits: 14,
87 }
88 }
89
90 pub const fn from_unix_time(
92 seconds: u64,
93 subsec_nanos: u32,
94 counter: u128,
95 usable_counter_bits: u8,
96 ) -> Self {
97 Timestamp {
98 seconds,
99 subsec_nanos,
100 counter,
101 usable_counter_bits,
102 }
103 }
104
105 pub fn from_unix(
107 context: impl ClockSequence<Output = impl Into<u128>>,
108 seconds: u64,
109 subsec_nanos: u32,
110 ) -> Self {
111 let (counter, seconds, subsec_nanos) =
112 context.generate_timestamp_sequence(seconds, subsec_nanos);
113 let counter = counter.into();
114 let usable_counter_bits = context.usable_bits() as u8;
115
116 Timestamp {
117 seconds,
118 subsec_nanos,
119 counter,
120 usable_counter_bits,
121 }
122 }
123
124 pub const fn to_gregorian(&self) -> (u64, u16) {
134 (
135 Self::unix_to_gregorian_ticks(self.seconds, self.subsec_nanos),
136 (self.counter as u16) & 0x3FFF,
137 )
138 }
139
140 #[cfg(feature = "v7")]
143 pub(crate) const fn counter(&self) -> (u128, u8) {
144 (self.counter, self.usable_counter_bits)
145 }
146
147 pub const fn to_unix(&self) -> (u64, u32) {
149 (self.seconds, self.subsec_nanos)
150 }
151
152 const fn unix_to_gregorian_ticks(seconds: u64, nanos: u32) -> u64 {
153 UUID_TICKS_BETWEEN_EPOCHS
154 .wrapping_add(seconds.wrapping_mul(10_000_000))
155 .wrapping_add(nanos as u64 / 100)
156 }
157
158 const fn gregorian_to_unix(ticks: u64) -> (u64, u32) {
159 (
160 ticks.wrapping_sub(UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000,
161 (ticks.wrapping_sub(UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32 * 100,
162 )
163 }
164}
165
166#[doc(hidden)]
167impl Timestamp {
168 #[deprecated(since = "1.11.0", note = "use `Timestamp::from_gregorian(ticks, counter)`")]
169 pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self {
170 Timestamp::from_gregorian(ticks, counter)
171 }
172
173 #[deprecated(since = "1.11.0", note = "use `Timestamp::to_gregorian()`")]
174 pub const fn to_rfc4122(&self) -> (u64, u16) {
175 self.to_gregorian()
176 }
177
178 #[deprecated(since = "1.2.0", note = "`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`")]
179 pub const fn to_unix_nanos(&self) -> u32 {
180 panic!("`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`")
181 }
182}
183
184pub(crate) const fn encode_gregorian_timestamp(
185 ticks: u64,
186 counter: u16,
187 node_id: &[u8; 6],
188) -> Uuid {
189 let time_low = (ticks & 0xFFFF_FFFF) as u32;
190 let time_mid = ((ticks >> 32) & 0xFFFF) as u16;
191 let time_high_and_version = (((ticks >> 48) & 0x0FFF) as u16) | (1 << 12);
192
193 let mut d4 = [0; 8];
194
195 d4[0] = (((counter & 0x3F00) >> 8) as u8) | 0x80;
196 d4[1] = (counter & 0xFF) as u8;
197 d4[2] = node_id[0];
198 d4[3] = node_id[1];
199 d4[4] = node_id[2];
200 d4[5] = node_id[3];
201 d4[6] = node_id[4];
202 d4[7] = node_id[5];
203
204 Uuid::from_fields(time_low, time_mid, time_high_and_version, &d4)
205}
206
207pub(crate) const fn decode_gregorian_timestamp(uuid: &Uuid) -> (u64, u16) {
208 let bytes = uuid.as_bytes();
209
210 let ticks: u64 = ((bytes[6] & 0x0F) as u64) << 56
211 | (bytes[7] as u64) << 48
212 | (bytes[4] as u64) << 40
213 | (bytes[5] as u64) << 32
214 | (bytes[0] as u64) << 24
215 | (bytes[1] as u64) << 16
216 | (bytes[2] as u64) << 8
217 | (bytes[3] as u64);
218
219 let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16);
220
221 (ticks, counter)
222}
223
224pub(crate) const fn encode_sorted_gregorian_timestamp(
225 ticks: u64,
226 counter: u16,
227 node_id: &[u8; 6],
228) -> Uuid {
229 let time_high = ((ticks >> 28) & 0xFFFF_FFFF) as u32;
230 let time_mid = ((ticks >> 12) & 0xFFFF) as u16;
231 let time_low_and_version = ((ticks & 0x0FFF) as u16) | (0x6 << 12);
232
233 let mut d4 = [0; 8];
234
235 d4[0] = (((counter & 0x3F00) >> 8) as u8) | 0x80;
236 d4[1] = (counter & 0xFF) as u8;
237 d4[2] = node_id[0];
238 d4[3] = node_id[1];
239 d4[4] = node_id[2];
240 d4[5] = node_id[3];
241 d4[6] = node_id[4];
242 d4[7] = node_id[5];
243
244 Uuid::from_fields(time_high, time_mid, time_low_and_version, &d4)
245}
246
247pub(crate) const fn decode_sorted_gregorian_timestamp(uuid: &Uuid) -> (u64, u16) {
248 let bytes = uuid.as_bytes();
249
250 let ticks: u64 = ((bytes[0]) as u64) << 52
251 | (bytes[1] as u64) << 44
252 | (bytes[2] as u64) << 36
253 | (bytes[3] as u64) << 28
254 | (bytes[4] as u64) << 20
255 | (bytes[5] as u64) << 12
256 | ((bytes[6] & 0xF) as u64) << 8
257 | (bytes[7] as u64);
258
259 let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16);
260
261 (ticks, counter)
262}
263
264pub(crate) const fn encode_unix_timestamp_millis(
265 millis: u64,
266 counter_random_bytes: &[u8; 10],
267) -> Uuid {
268 let millis_high = ((millis >> 16) & 0xFFFF_FFFF) as u32;
269 let millis_low = (millis & 0xFFFF) as u16;
270
271 let counter_random_version = (counter_random_bytes[1] as u16
272 | ((counter_random_bytes[0] as u16) << 8) & 0x0FFF)
273 | (0x7 << 12);
274
275 let mut d4 = [0; 8];
276
277 d4[0] = (counter_random_bytes[2] & 0x3F) | 0x80;
278 d4[1] = counter_random_bytes[3];
279 d4[2] = counter_random_bytes[4];
280 d4[3] = counter_random_bytes[5];
281 d4[4] = counter_random_bytes[6];
282 d4[5] = counter_random_bytes[7];
283 d4[6] = counter_random_bytes[8];
284 d4[7] = counter_random_bytes[9];
285
286 Uuid::from_fields(millis_high, millis_low, counter_random_version, &d4)
287}
288
289pub(crate) const fn decode_unix_timestamp_millis(uuid: &Uuid) -> u64 {
290 let bytes = uuid.as_bytes();
291
292 let millis: u64 = (bytes[0] as u64) << 40
293 | (bytes[1] as u64) << 32
294 | (bytes[2] as u64) << 24
295 | (bytes[3] as u64) << 16
296 | (bytes[4] as u64) << 8
297 | (bytes[5] as u64);
298
299 millis
300}
301
302#[cfg(all(
303 feature = "std",
304 feature = "js",
305 all(
306 target_arch = "wasm32",
307 target_vendor = "unknown",
308 target_os = "unknown"
309 )
310))]
311fn now() -> (u64, u32) {
312 use wasm_bindgen::prelude::*;
313
314 #[wasm_bindgen]
315 extern "C" {
316 #[wasm_bindgen(js_namespace = Date, catch)]
318 fn now() -> Result<f64, JsValue>;
319 }
320
321 let now = now().unwrap_throw();
322
323 let secs = (now / 1_000.0) as u64;
324 let nanos = ((now % 1_000.0) * 1_000_000.0) as u32;
325
326 (secs, nanos)
327}
328
329#[cfg(all(
330 feature = "std",
331 not(miri),
332 any(
333 not(feature = "js"),
334 not(all(
335 target_arch = "wasm32",
336 target_vendor = "unknown",
337 target_os = "unknown"
338 ))
339 )
340))]
341fn now() -> (u64, u32) {
342 let dur = std::time::SystemTime::UNIX_EPOCH.elapsed().expect(
343 "Getting elapsed time since UNIX_EPOCH. If this fails, we've somehow violated causality",
344 );
345
346 (dur.as_secs(), dur.subsec_nanos())
347}
348
349#[cfg(all(feature = "std", miri))]
350fn now() -> (u64, u32) {
351 use std::{sync::Mutex, time::Duration};
352
353 static TS: Mutex<u64> = Mutex::new(0);
354
355 let ts = Duration::from_nanos({
356 let mut ts = TS.lock().unwrap();
357 *ts += 1;
358 *ts
359 });
360
361 (ts.as_secs(), ts.subsec_nanos())
362}
363
364pub trait ClockSequence {
373 type Output;
375
376 fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output;
382
383 fn generate_timestamp_sequence(
389 &self,
390 seconds: u64,
391 subsec_nanos: u32,
392 ) -> (Self::Output, u64, u32) {
393 (
394 self.generate_sequence(seconds, subsec_nanos),
395 seconds,
396 subsec_nanos,
397 )
398 }
399
400 fn usable_bits(&self) -> usize
408 where
409 Self::Output: Sized,
410 {
411 cmp::min(128, core::mem::size_of::<Self::Output>())
412 }
413}
414
415impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T {
416 type Output = T::Output;
417
418 fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
419 (**self).generate_sequence(seconds, subsec_nanos)
420 }
421
422 fn generate_timestamp_sequence(
423 &self,
424 seconds: u64,
425 subsec_nanos: u32,
426 ) -> (Self::Output, u64, u32) {
427 (**self).generate_timestamp_sequence(seconds, subsec_nanos)
428 }
429
430 fn usable_bits(&self) -> usize
431 where
432 Self::Output: Sized,
433 {
434 (**self).usable_bits()
435 }
436}
437
438pub mod context {
440 use super::ClockSequence;
441
442 #[cfg(any(feature = "v1", feature = "v6"))]
443 mod v1_support {
444 use super::*;
445
446 use atomic::{Atomic, Ordering};
447
448 #[cfg(all(feature = "std", feature = "rng"))]
449 static CONTEXT: Context = Context {
450 count: Atomic::new(0),
451 };
452
453 #[cfg(all(feature = "std", feature = "rng"))]
454 static CONTEXT_INITIALIZED: Atomic<bool> = Atomic::new(false);
455
456 #[cfg(all(feature = "std", feature = "rng"))]
457 pub(crate) fn shared_context() -> &'static Context {
458 if CONTEXT_INITIALIZED
461 .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
462 .is_ok()
463 {
464 CONTEXT.count.store(crate::rng::u16(), Ordering::Release);
465 }
466
467 &CONTEXT
468 }
469
470 #[derive(Debug)]
485 pub struct Context {
486 count: Atomic<u16>,
487 }
488
489 impl Context {
490 pub const fn new(count: u16) -> Self {
496 Self {
497 count: Atomic::<u16>::new(count),
498 }
499 }
500
501 #[cfg(feature = "rng")]
503 pub fn new_random() -> Self {
504 Self {
505 count: Atomic::<u16>::new(crate::rng::u16()),
506 }
507 }
508 }
509
510 impl ClockSequence for Context {
511 type Output = u16;
512
513 fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
514 self.count.fetch_add(1, Ordering::AcqRel) & (u16::MAX >> 2)
520 }
521
522 fn usable_bits(&self) -> usize {
523 14
524 }
525 }
526
527 #[cfg(test)]
528 mod tests {
529 use crate::Timestamp;
530
531 use super::*;
532
533 #[test]
534 fn context() {
535 let seconds = 1_496_854_535;
536 let subsec_nanos = 812_946_000;
537
538 let context = Context::new(u16::MAX >> 2);
539
540 let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
541 assert_eq!(16383, ts.counter);
542 assert_eq!(14, ts.usable_counter_bits);
543
544 let seconds = 1_496_854_536;
545
546 let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
547 assert_eq!(0, ts.counter);
548
549 let seconds = 1_496_854_535;
550
551 let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
552 assert_eq!(1, ts.counter);
553 }
554 }
555 }
556
557 #[cfg(any(feature = "v1", feature = "v6"))]
558 pub use v1_support::*;
559
560 #[cfg(feature = "std")]
561 mod std_support {
562 use super::*;
563
564 use core::panic::{AssertUnwindSafe, RefUnwindSafe};
565 use std::{sync::Mutex, thread::LocalKey};
566
567 pub struct ThreadLocalContext<C: 'static>(&'static LocalKey<C>);
569
570 impl<C> std::fmt::Debug for ThreadLocalContext<C> {
571 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
572 f.debug_struct("ThreadLocalContext").finish_non_exhaustive()
573 }
574 }
575
576 impl<C: 'static> ThreadLocalContext<C> {
577 pub const fn new(local_key: &'static LocalKey<C>) -> Self {
579 ThreadLocalContext(local_key)
580 }
581 }
582
583 impl<C: ClockSequence + 'static> ClockSequence for ThreadLocalContext<C> {
584 type Output = C::Output;
585
586 fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
587 self.0
588 .with(|ctxt| ctxt.generate_sequence(seconds, subsec_nanos))
589 }
590
591 fn generate_timestamp_sequence(
592 &self,
593 seconds: u64,
594 subsec_nanos: u32,
595 ) -> (Self::Output, u64, u32) {
596 self.0
597 .with(|ctxt| ctxt.generate_timestamp_sequence(seconds, subsec_nanos))
598 }
599
600 fn usable_bits(&self) -> usize {
601 self.0.with(|ctxt| ctxt.usable_bits())
602 }
603 }
604
605 impl<C: ClockSequence> ClockSequence for AssertUnwindSafe<C> {
606 type Output = C::Output;
607
608 fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
609 self.0.generate_sequence(seconds, subsec_nanos)
610 }
611
612 fn generate_timestamp_sequence(
613 &self,
614 seconds: u64,
615 subsec_nanos: u32,
616 ) -> (Self::Output, u64, u32) {
617 self.0.generate_timestamp_sequence(seconds, subsec_nanos)
618 }
619
620 fn usable_bits(&self) -> usize
621 where
622 Self::Output: Sized,
623 {
624 self.0.usable_bits()
625 }
626 }
627
628 impl<C: ClockSequence + RefUnwindSafe> ClockSequence for Mutex<C> {
629 type Output = C::Output;
630
631 fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
632 self.lock()
633 .unwrap_or_else(|err| err.into_inner())
634 .generate_sequence(seconds, subsec_nanos)
635 }
636
637 fn generate_timestamp_sequence(
638 &self,
639 seconds: u64,
640 subsec_nanos: u32,
641 ) -> (Self::Output, u64, u32) {
642 self.lock()
643 .unwrap_or_else(|err| err.into_inner())
644 .generate_timestamp_sequence(seconds, subsec_nanos)
645 }
646
647 fn usable_bits(&self) -> usize
648 where
649 Self::Output: Sized,
650 {
651 self.lock()
652 .unwrap_or_else(|err| err.into_inner())
653 .usable_bits()
654 }
655 }
656 }
657
658 #[cfg(feature = "std")]
659 pub use std_support::*;
660
661 #[cfg(feature = "v7")]
662 mod v7_support {
663 use super::*;
664
665 use core::{cell::Cell, panic::RefUnwindSafe};
666
667 #[cfg(feature = "std")]
668 static CONTEXT_V7: SharedContextV7 =
669 SharedContextV7(std::sync::Mutex::new(ContextV7::new()));
670
671 #[cfg(feature = "std")]
672 pub(crate) fn shared_context_v7() -> &'static SharedContextV7 {
673 &CONTEXT_V7
674 }
675
676 const USABLE_BITS: usize = 42;
677
678 const RESEED_MASK: u64 = u64::MAX >> 23;
683 const MAX_COUNTER: u64 = u64::MAX >> 22;
684
685 #[derive(Debug)]
702 pub struct ContextV7 {
703 last_reseed: Cell<LastReseed>,
704 counter: Cell<u64>,
705 }
706
707 #[derive(Debug, Default, Clone, Copy)]
708 struct LastReseed {
709 millis: u64,
710 ts_seconds: u64,
711 ts_subsec_nanos: u32,
712 }
713
714 impl LastReseed {
715 fn from_millis(millis: u64) -> Self {
716 LastReseed {
717 millis,
718 ts_seconds: millis / 1_000,
719 ts_subsec_nanos: (millis % 1_000) as u32 * 1_000_000,
720 }
721 }
722 }
723
724 impl RefUnwindSafe for ContextV7 {}
725
726 impl ContextV7 {
727 pub const fn new() -> Self {
730 ContextV7 {
731 last_reseed: Cell::new(LastReseed {
732 millis: 0,
733 ts_seconds: 0,
734 ts_subsec_nanos: 0,
735 }),
736 counter: Cell::new(0),
737 }
738 }
739 }
740
741 impl ClockSequence for ContextV7 {
742 type Output = u64;
743
744 fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
745 self.generate_timestamp_sequence(seconds, subsec_nanos).0
746 }
747
748 fn generate_timestamp_sequence(
749 &self,
750 seconds: u64,
751 subsec_nanos: u32,
752 ) -> (Self::Output, u64, u32) {
753 let millis = (seconds * 1_000).saturating_add(subsec_nanos as u64 / 1_000_000);
754
755 let last_reseed = self.last_reseed.get();
756
757 if millis > last_reseed.millis {
759 let last_reseed = LastReseed::from_millis(millis);
760 self.last_reseed.set(last_reseed);
761
762 let counter = crate::rng::u64() & RESEED_MASK;
763 self.counter.set(counter);
764
765 (counter, last_reseed.ts_seconds, last_reseed.ts_subsec_nanos)
766 }
767 else {
769 let millis = ();
773 let _ = millis;
774
775 let counter = self.counter.get() + 1;
777
778 if counter <= MAX_COUNTER {
780 self.counter.set(counter);
781
782 (counter, last_reseed.ts_seconds, last_reseed.ts_subsec_nanos)
783 }
784 else {
788 let last_reseed = LastReseed::from_millis(last_reseed.millis + 1);
790 self.last_reseed.set(last_reseed);
791
792 let counter = crate::rng::u64() & RESEED_MASK;
794 self.counter.set(counter);
795
796 (counter, last_reseed.ts_seconds, last_reseed.ts_subsec_nanos)
797 }
798 }
799 }
800
801 fn usable_bits(&self) -> usize {
802 USABLE_BITS
803 }
804 }
805
806 #[cfg(feature = "std")]
807 pub(crate) struct SharedContextV7(std::sync::Mutex<ContextV7>);
808
809 #[cfg(feature = "std")]
810 impl ClockSequence for SharedContextV7 {
811 type Output = u64;
812
813 fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
814 self.0.generate_sequence(seconds, subsec_nanos)
815 }
816
817 fn generate_timestamp_sequence(
818 &self,
819 seconds: u64,
820 subsec_nanos: u32,
821 ) -> (Self::Output, u64, u32) {
822 self.0.generate_timestamp_sequence(seconds, subsec_nanos)
823 }
824
825 fn usable_bits(&self) -> usize
826 where
827 Self::Output: Sized,
828 {
829 USABLE_BITS
830 }
831 }
832
833 #[cfg(test)]
834 mod tests {
835 use core::time::Duration;
836
837 use super::*;
838
839 use crate::Timestamp;
840
841 #[test]
842 fn context() {
843 let seconds = 1_496_854_535;
844 let subsec_nanos = 812_946_000;
845
846 let context = ContextV7::new();
847
848 let ts1 = Timestamp::from_unix(&context, seconds, subsec_nanos);
849 assert_eq!(42, ts1.usable_counter_bits);
850
851 let seconds = 1_496_854_534;
853
854 let ts2 = Timestamp::from_unix(&context, seconds, subsec_nanos);
855
856 assert_eq!(ts1.seconds, ts2.seconds);
859 assert_eq!(ts1.subsec_nanos, ts2.subsec_nanos);
860 assert_eq!(ts1.counter + 1, ts2.counter);
861
862 let seconds = 1_496_854_536;
864
865 let ts3 = Timestamp::from_unix(&context, seconds, subsec_nanos);
866
867 assert_ne!(ts2.counter + 1, ts3.counter);
869 assert_ne!(0, ts3.counter);
870 }
871
872 #[test]
873 fn context_wrap() {
874 let seconds = 1_496_854_535u64;
875 let subsec_nanos = 812_946_000u32;
876
877 let millis = (seconds * 1000).saturating_add(subsec_nanos as u64 / 1_000_000);
878
879 let context = ContextV7 {
881 last_reseed: Cell::new(LastReseed::from_millis(millis)),
882 counter: Cell::new(u64::MAX >> 22),
883 };
884
885 let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);
886
887 let expected_ts = Duration::new(seconds, subsec_nanos / 1_000_000 * 1_000_000)
889 + Duration::from_millis(1);
890 assert_eq!(expected_ts.as_secs(), ts.seconds);
891 assert_eq!(expected_ts.subsec_nanos(), ts.subsec_nanos);
892
893 assert!(ts.counter < (u64::MAX >> 22) as u128);
895 assert_ne!(0, ts.counter);
896 }
897 }
898 }
899
900 #[cfg(feature = "v7")]
901 pub use v7_support::*;
902
903 #[derive(Debug, Clone, Copy, Default)]
914 pub struct NoContext;
915
916 impl ClockSequence for NoContext {
917 type Output = u16;
918
919 fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
920 0
921 }
922
923 fn usable_bits(&self) -> usize {
924 0
925 }
926 }
927}
928
929#[cfg(all(test, any(feature = "v1", feature = "v6")))]
930mod tests {
931 use super::*;
932
933 #[cfg(all(
934 target_arch = "wasm32",
935 target_vendor = "unknown",
936 target_os = "unknown"
937 ))]
938 use wasm_bindgen_test::*;
939
940 #[test]
941 #[cfg_attr(
942 all(
943 target_arch = "wasm32",
944 target_vendor = "unknown",
945 target_os = "unknown"
946 ),
947 wasm_bindgen_test
948 )]
949 fn gregorian_unix_does_not_panic() {
950 Timestamp::unix_to_gregorian_ticks(u64::MAX, 0);
952 Timestamp::unix_to_gregorian_ticks(0, u32::MAX);
953 Timestamp::unix_to_gregorian_ticks(u64::MAX, u32::MAX);
954
955 Timestamp::gregorian_to_unix(u64::MAX);
956 }
957
958 #[test]
959 #[cfg_attr(
960 all(
961 target_arch = "wasm32",
962 target_vendor = "unknown",
963 target_os = "unknown"
964 ),
965 wasm_bindgen_test
966 )]
967 fn to_gregorian_truncates_to_usable_bits() {
968 let ts = Timestamp::from_gregorian(123, u16::MAX);
969
970 assert_eq!((123, u16::MAX >> 2), ts.to_gregorian());
971 }
972}