1use std::{borrow, fmt, mem, ops};
2
3use chrono::Timelike;
4pub use chrono::{
5 format::{DelayedFormat, StrftimeItems},
6 DateTime, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
7};
8pub use rust_decimal::Decimal;
9use serde::{
10 de::{self, Visitor},
11 Deserialize, Deserializer, Serialize, Serializer,
12};
13
14pub type Int = i64;
15pub type TagNum = u16;
16pub type SeqNum = u32;
17pub type NumInGroup = u8;
18pub type DayOfMonth = u8;
19
20pub type Float = Decimal;
21pub type Qty = Float;
22pub type Price = Float;
23pub type PriceOffset = Float;
24pub type Amt = Float;
25pub type Percentage = Float;
26
27pub type Boolean = bool;
28
29pub type Char = u8;
30pub type MultipleCharValue = Vec<Char>;
31
32#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
33pub struct FixString(Vec<u8>);
34
35#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)]
36#[repr(transparent)]
37pub struct FixStr([u8]);
38
39pub type MultipleStringValue = Vec<FixString>;
40
41pub use crate::{country::Country, currency::Currency};
42pub type Exchange = [u8; 4];
43pub type MonthYear = Vec<u8>;
45pub type Language = [u8; 2];
46
47#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize)]
48pub enum TimePrecision {
49 Secs = 0,
50 Millis = 3,
51 Micros = 6,
52 #[default]
53 Nanos = 9,
54}
55
56#[derive(Clone, Copy, Debug, Default)]
57pub struct UtcTimestamp {
58 timestamp: DateTime<Utc>,
59 precision: TimePrecision,
60}
61
62#[derive(Clone, Copy, Debug, Serialize)]
63pub struct UtcTimeOnly {
64 timestamp: NaiveTime,
65 precision: TimePrecision,
66}
67pub type UtcDateOnly = NaiveDate;
68
69pub type LocalMktTime = NaiveTime;
70pub type LocalMktDate = NaiveDate;
71
72pub type TzTimestamp = Vec<u8>;
74pub type TzTimeOnly = Vec<u8>;
75
76pub type Length = u16;
77pub type Data = Vec<u8>;
78pub type XmlData = Data;
79
80pub type Tenor = Vec<u8>;
82
83#[derive(Debug)]
84pub struct FixStringError {
85 idx: usize,
86 value: u8,
87}
88
89impl FixStringError {
90 pub fn idx(&self) -> usize {
92 self.idx
93 }
94
95 pub fn value(&self) -> u8 {
97 self.value
98 }
99}
100
101impl fmt::Display for FixStringError {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(
104 f,
105 "Unexpected character '{:#04x}' at idx {}",
106 self.value, self.idx
107 )
108 }
109}
110
111impl std::error::Error for FixStringError {}
112
113const fn is_non_control_ascii_char(byte: u8) -> bool {
114 byte > 0x1f && byte < 0x80
115}
116
117impl FixStr {
118 pub const fn from_ascii(buf: &[u8]) -> Result<&FixStr, FixStringError> {
149 let mut i = 0;
150 while i < buf.len() {
151 let c = buf[i];
152 if !is_non_control_ascii_char(c) {
153 return Err(FixStringError { idx: i, value: c });
154 }
155 i += 1;
156 }
157 unsafe { Ok(FixStr::from_ascii_unchecked(buf)) }
159 }
160
161 pub const unsafe fn from_ascii_unchecked(buf: &[u8]) -> &FixStr {
172 mem::transmute(buf)
175 }
176
177 pub const fn as_utf8(&self) -> &str {
178 unsafe { std::str::from_utf8_unchecked(&self.0) }
180 }
181
182 pub const fn as_bytes(&self) -> &[u8] {
183 &self.0
184 }
185
186 pub const fn len(&self) -> usize {
187 self.0.len()
188 }
189
190 pub const fn is_empty(&self) -> bool {
191 self.0.is_empty()
192 }
193}
194
195impl fmt::Display for FixStr {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
197 self.as_utf8().fmt(f)
198 }
199}
200
201impl fmt::Debug for FixStr {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 write!(f, "FixStr(\"{}\")", self)
204 }
205}
206
207impl AsRef<FixStr> for FixStr {
208 fn as_ref(&self) -> &FixStr {
209 self
210 }
211}
212
213impl AsRef<[u8]> for FixStr {
214 fn as_ref(&self) -> &[u8] {
215 self.as_bytes()
216 }
217}
218
219impl AsRef<str> for FixStr {
220 fn as_ref(&self) -> &str {
221 self.as_utf8()
222 }
223}
224
225impl From<&FixStr> for String {
226 fn from(input: &FixStr) -> String {
227 input.to_owned().into()
228 }
229}
230
231impl ToOwned for FixStr {
232 type Owned = FixString;
233
234 #[inline]
235 fn to_owned(&self) -> FixString {
236 unsafe { FixString::from_ascii_unchecked(self.as_bytes().to_owned()) }
237 }
238
239 fn clone_into(&self, target: &mut FixString) {
240 let mut buf = mem::take(target).into_bytes();
241 self.as_bytes().clone_into(&mut buf);
242 *target = unsafe { FixString::from_ascii_unchecked(buf) }
243 }
244}
245
246macro_rules! impl_eq {
247 ($lhs:ty, $lhs_bytes: ident, $rhs: ty, $rhs_bytes: ident) => {
248 impl PartialEq<$rhs> for $lhs {
249 #[inline]
250 fn eq(&self, other: &$rhs) -> bool {
251 PartialEq::eq(self.$lhs_bytes(), other.$rhs_bytes())
252 }
253 }
254
255 impl PartialEq<$lhs> for $rhs {
256 #[inline]
257 fn eq(&self, other: &$lhs) -> bool {
258 PartialEq::eq(self.$rhs_bytes(), other.$lhs_bytes())
259 }
260 }
261 };
262}
263
264impl_eq!([u8], as_ref, FixStr, as_bytes);
265impl_eq!([u8], as_ref, &FixStr, as_bytes);
266impl_eq!(&[u8], as_ref, FixStr, as_bytes);
267impl_eq!(Vec<u8>, as_slice, FixStr, as_bytes);
268impl_eq!(Vec<u8>, as_slice, &FixStr, as_bytes);
269impl_eq!(str, as_bytes, FixStr, as_bytes);
270impl_eq!(&str, as_bytes, FixStr, as_bytes);
271impl_eq!(str, as_bytes, &FixStr, as_bytes);
272impl_eq!(String, as_bytes, FixStr, as_bytes);
273impl_eq!(String, as_bytes, &FixStr, as_bytes);
274
275impl_eq!([u8], as_ref, FixString, as_bytes);
276impl_eq!(&[u8], as_ref, FixString, as_bytes);
277impl_eq!(Vec<u8>, as_slice, FixString, as_bytes);
278impl_eq!(str, as_bytes, FixString, as_bytes);
279impl_eq!(&str, as_bytes, FixString, as_bytes);
280impl_eq!(String, as_bytes, FixString, as_bytes);
281
282impl_eq!(FixString, as_bytes, FixStr, as_bytes);
283impl_eq!(FixString, as_bytes, &FixStr, as_bytes);
284
285impl<const N: usize> PartialEq<[u8; N]> for FixStr {
286 fn eq(&self, other: &[u8; N]) -> bool {
287 self.0.eq(&other[..])
288 }
289}
290
291impl<const N: usize> PartialEq<&'_ [u8; N]> for FixStr {
292 fn eq(&self, other: &&[u8; N]) -> bool {
293 self.0.eq(*other)
294 }
295}
296
297impl<const N: usize> PartialEq<[u8; N]> for &FixStr {
298 fn eq(&self, other: &[u8; N]) -> bool {
299 self.0.eq(&other[..])
300 }
301}
302
303impl<const N: usize> PartialEq<[u8; N]> for FixString {
304 fn eq(&self, other: &[u8; N]) -> bool {
305 self.0.eq(other)
306 }
307}
308
309impl<const N: usize> PartialEq<&'_ [u8; N]> for FixString {
310 fn eq(&self, other: &&[u8; N]) -> bool {
311 self.0.eq(other)
312 }
313}
314
315#[macro_export]
320macro_rules! fix_format {
321 ($($arg:tt)*) => {{
322 FixString::from_ascii_lossy(std::format!($($arg)*).into_bytes())
323 }}
324}
325
326impl FixString {
328 pub const fn new() -> FixString {
329 FixString(Vec::new())
330 }
331
332 pub fn with_capacity(capacity: usize) -> FixString {
333 FixString(Vec::with_capacity(capacity))
334 }
335
336 pub fn from_ascii(buf: Vec<u8>) -> Result<FixString, FixStringError> {
368 for i in 0..buf.len() {
369 let c = unsafe { *buf.get_unchecked(i) };
371 if !is_non_control_ascii_char(c) {
372 return Err(FixStringError { idx: i, value: c });
373 }
374 }
375 Ok(FixString(buf))
376 }
377
378 pub unsafe fn from_ascii_unchecked(buf: Vec<u8>) -> FixString {
392 FixString(buf)
393 }
394
395 pub fn from_ascii_lossy(mut buf: Vec<u8>) -> FixString {
398 for i in 0..buf.len() {
399 let c = unsafe { buf.get_unchecked_mut(i) };
401 if !is_non_control_ascii_char(*c) {
402 *c = b'?';
403 }
404 }
405 FixString(buf)
406 }
407
408 pub fn as_utf8(&self) -> &str {
409 unsafe { std::str::from_utf8_unchecked(&self.0) }
411 }
412
413 pub fn into_utf8(self) -> String {
414 unsafe { String::from_utf8_unchecked(self.0) }
416 }
417
418 pub fn into_bytes(self) -> Vec<u8> {
419 self.0
420 }
421
422 pub fn len(&self) -> usize {
423 self.0.len()
424 }
425
426 pub fn is_empty(&self) -> bool {
427 self.0.is_empty()
428 }
429}
430
431impl fmt::Display for FixString {
432 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
433 self.as_utf8().fmt(f)
434 }
435}
436
437impl fmt::Debug for FixString {
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 write!(f, "FixString(\"{}\")", self)
440 }
441}
442
443impl ops::Deref for FixString {
444 type Target = FixStr;
445
446 fn deref(&self) -> &FixStr {
447 unsafe { FixStr::from_ascii_unchecked(&self.0) }
448 }
449}
450
451impl AsRef<FixStr> for FixString {
452 fn as_ref(&self) -> &FixStr {
453 self
454 }
455}
456
457impl AsRef<[u8]> for FixString {
458 fn as_ref(&self) -> &[u8] {
459 self.as_bytes()
460 }
461}
462
463impl AsRef<str> for FixString {
464 fn as_ref(&self) -> &str {
465 self.as_utf8()
466 }
467}
468
469impl borrow::Borrow<FixStr> for FixString {
470 fn borrow(&self) -> &FixStr {
471 self
472 }
473}
474
475impl From<&FixStr> for FixString {
476 fn from(input: &FixStr) -> FixString {
477 input.to_owned()
478 }
479}
480
481impl From<FixString> for String {
482 fn from(input: FixString) -> String {
483 unsafe { String::from_utf8_unchecked(input.0) }
485 }
486}
487
488impl TryFrom<&[u8]> for FixString {
489 type Error = FixStringError;
490
491 fn try_from(input: &[u8]) -> Result<FixString, Self::Error> {
492 FixString::from_ascii(input.to_vec())
494 }
495}
496
497impl TryFrom<Vec<u8>> for FixString {
498 type Error = FixStringError;
499
500 fn try_from(buf: Vec<u8>) -> Result<FixString, Self::Error> {
501 FixString::from_ascii(buf)
502 }
503}
504
505impl TryFrom<&str> for FixString {
506 type Error = FixStringError;
507
508 fn try_from(buf: &str) -> Result<FixString, Self::Error> {
509 FixString::from_ascii(buf.as_bytes().to_owned())
510 }
511}
512
513impl TryFrom<String> for FixString {
514 type Error = FixStringError;
515
516 fn try_from(buf: String) -> Result<FixString, Self::Error> {
517 FixString::from_ascii(buf.into_bytes())
518 }
519}
520
521impl<const N: usize> TryFrom<[u8; N]> for FixString {
522 type Error = FixStringError;
523
524 fn try_from(buf: [u8; N]) -> Result<FixString, Self::Error> {
525 FixString::from_ascii(buf.to_vec())
526 }
527}
528
529impl<const N: usize> From<&[u8; N]> for FixString {
530 fn from(input: &[u8; N]) -> FixString {
531 FixString(input.as_slice().into())
532 }
533}
534
535struct FixStringVisitor;
536
537impl Visitor<'_> for FixStringVisitor {
538 type Value = FixString;
539
540 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
541 formatter.write_str("string")
542 }
543
544 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
545 where
546 E: de::Error,
547 {
548 value.try_into().map_err(de::Error::custom)
549 }
550}
551
552impl<'de> Deserialize<'de> for FixString {
553 fn deserialize<D>(deserializer: D) -> Result<FixString, D::Error>
554 where
555 D: Deserializer<'de>,
556 {
557 deserializer.deserialize_str(FixStringVisitor)
558 }
559}
560
561impl Serialize for FixString {
562 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
563 where
564 S: Serializer,
565 {
566 serializer.serialize_str(self.as_utf8())
567 }
568}
569
570pub trait ToFixString {
571 fn to_fix_string(&self) -> FixString;
572}
573
574impl ToFixString for FixStr {
575 fn to_fix_string(&self) -> FixString {
576 unsafe { FixString::from_ascii_unchecked(self.as_bytes().to_owned()) }
578 }
579}
580
581macro_rules! impl_to_fix_string_for_integer {
582 ($t:ty) => {
583 impl ToFixString for $t {
584 fn to_fix_string(&self) -> FixString {
585 unsafe {
587 FixString::from_ascii_unchecked(
588 itoa::Buffer::new().format(*self).as_bytes().to_vec(),
589 )
590 }
591 }
592 }
593 };
594}
595
596impl_to_fix_string_for_integer!(i8);
597impl_to_fix_string_for_integer!(i16);
598impl_to_fix_string_for_integer!(i32);
599impl_to_fix_string_for_integer!(i64);
600impl_to_fix_string_for_integer!(isize);
601impl_to_fix_string_for_integer!(u8);
602impl_to_fix_string_for_integer!(u16);
603impl_to_fix_string_for_integer!(u32);
604impl_to_fix_string_for_integer!(u64);
605impl_to_fix_string_for_integer!(usize);
606
607fn deserialize_fraction_of_second<E>(buf: &[u8]) -> Result<(u32, u8), E>
608where
609 E: de::Error,
610{
611 let [b'.', buf @ ..] = buf else {
620 return Err(de::Error::custom("incorrecct data format for UtcTimestamp"));
621 };
622
623 let mut fraction_of_second: u64 = 0;
624 for i in 0..buf.len() {
625 match unsafe { buf.get_unchecked(i) } {
627 n @ b'0'..=b'9' => {
628 fraction_of_second = fraction_of_second
629 .checked_mul(10)
630 .and_then(|v| v.checked_add((n - b'0') as u64))
631 .ok_or_else(|| de::Error::custom("incorrect fraction of second (overflow)"))?;
632 }
633 _ => {
634 return Err(de::Error::custom(
635 "incorrecct data format for fraction of second",
636 ));
637 }
638 }
639 }
640 let (multiplier, divider) = match buf.len() {
641 3 => (1_000_000, 1),
642 6 => (1_000, 1),
643 9 => (1, 1),
644 12 => (1, 1_000),
647 _ => {
648 return Err(de::Error::custom(
649 "incorrect fraction of second (wrong precision)",
650 ));
651 }
652 };
653 (fraction_of_second * multiplier / divider)
654 .try_into()
655 .map(|adjusted_fraction_of_second| (adjusted_fraction_of_second, buf.len() as u8))
656 .map_err(|_| de::Error::custom("incorrecct data format for UtcTimestamp"))
657}
658
659struct UtcTimestampVisitor;
660
661impl Visitor<'_> for UtcTimestampVisitor {
662 type Value = UtcTimestamp;
663
664 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
665 formatter.write_str("string")
666 }
667
668 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
687 where
688 E: de::Error,
689 {
690 match value.as_bytes() {
691 [
692 y3 @ b'0'..=b'9', y2 @ b'0'..=b'9', y1 @ b'0'..=b'9', y0 @ b'0'..=b'9',
694 m1 @ b'0'..=b'1', m0 @ b'0'..=b'9',
696 d1 @ b'0'..=b'3', d0 @ b'0'..=b'9',
698 b'-',
699 h1 @ b'0'..=b'2', h0 @ b'0'..=b'9',
701 b':',
702 mm1 @ b'0'..=b'5', mm0 @ b'0'..=b'9',
704 b':',
705 s1 @ b'0'..=b'5', s0 @ b'0'..=b'9',
708 ..
709 ] => {
710 let value = &value[17..];
711 let year = (y3 - b'0') as i32 * 1000
712 + (y2 - b'0') as i32 * 100
713 + (y1 - b'0') as i32 * 10
714 + (y0 - b'0') as i32;
715 let month = (m1 - b'0') as u32 * 10 + (m0 - b'0') as u32;
716 let day = (d1 - b'0') as u32 * 10 + (d0 - b'0') as u32;
717 let naive_date = NaiveDate::from_ymd_opt(year, month, day)
718 .ok_or_else(|| de::Error::custom("incorrecct data format for UtcTimestamp"))?;
719 let hour = (h1 - b'0') as u32 * 10 + (h0 - b'0') as u32;
720 let min = (mm1 - b'0') as u32 * 10 + (mm0 - b'0') as u32;
721 let sec = (s1 - b'0') as u32 * 10 + (s0 - b'0') as u32;
722 let (fraction_of_second, precision) = deserialize_fraction_of_second(value.as_bytes())?;
723 let naive_date_time = naive_date
724 .and_hms_nano_opt(hour, min, sec, fraction_of_second)
725 .ok_or_else(|| de::Error::custom("incorrecct data format for UtcTimestamp"))?;
726 let timestamp = Utc.from_utc_datetime(&naive_date_time);
727
728 match precision {
729 0 => Ok(UtcTimestamp::with_secs(timestamp)),
730 3 => Ok(UtcTimestamp::with_millis(timestamp)),
731 6 => Ok(UtcTimestamp::with_micros(timestamp)),
732 9 => Ok(UtcTimestamp::with_nanos(timestamp)),
733 12 => Ok(UtcTimestamp::with_nanos(timestamp)),
736 _ => Err(de::Error::custom("incorrecct data format for UtcTimestamp")),
737 }
738 }
739 _ => Err(de::Error::custom("incorrecct data format for UtcTimestamp")),
740 }
741 }
742}
743
744impl<'de> Deserialize<'de> for UtcTimestamp {
745 fn deserialize<D>(deserializer: D) -> Result<UtcTimestamp, D::Error>
746 where
747 D: Deserializer<'de>,
748 {
749 deserializer.deserialize_str(UtcTimestampVisitor)
750 }
751}
752
753impl Serialize for UtcTimestamp {
754 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
755 where
756 S: Serializer,
757 {
758 let formatted_timestamp = self.format_precisely().to_string();
759 serializer.serialize_str(&formatted_timestamp)
760 }
761}
762
763impl PartialEq for UtcTimestamp {
764 fn eq(&self, other: &Self) -> bool {
765 self.timestamp == other.timestamp
766 }
767}
768
769impl Eq for UtcTimestamp {}
770
771impl PartialOrd for UtcTimestamp {
772 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
773 Some(self.timestamp.cmp(&other.timestamp))
774 }
775}
776
777impl Ord for UtcTimestamp {
778 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
779 self.timestamp().cmp(&other.timestamp())
780 }
781}
782
783impl fmt::Display for UtcTimestamp {
784 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
785 let result = self.format_precisely().to_string();
786 write!(f, "{}", result)
787 }
788}
789
790impl UtcTimestamp {
791 pub const MAX_UTC: UtcTimestamp = UtcTimestamp {
792 timestamp: DateTime::<Utc>::MAX_UTC,
793 precision: TimePrecision::Nanos,
794 };
795 pub const MIN_UTC: UtcTimestamp = UtcTimestamp {
796 timestamp: DateTime::<Utc>::MIN_UTC,
797 precision: TimePrecision::Nanos,
798 };
799
800 pub fn now() -> UtcTimestamp {
802 UtcTimestamp::with_precision(Utc::now(), TimePrecision::default())
803 }
804
805 pub fn with_precision(date_time: DateTime<Utc>, precision: TimePrecision) -> UtcTimestamp {
808 match precision {
809 TimePrecision::Secs => UtcTimestamp::with_secs(date_time),
810 TimePrecision::Millis => UtcTimestamp::with_millis(date_time),
811 TimePrecision::Micros => UtcTimestamp::with_micros(date_time),
812 TimePrecision::Nanos => UtcTimestamp::with_nanos(date_time),
813 }
814 }
815
816 fn timestamp_from_secs_and_nsecs(secs: i64, nsecs: u32) -> DateTime<Utc> {
817 DateTime::from_timestamp(secs, nsecs).unwrap()
818 }
819
820 pub fn with_secs(date_time: DateTime<Utc>) -> UtcTimestamp {
823 let secs = date_time.timestamp();
824 UtcTimestamp {
825 timestamp: Self::timestamp_from_secs_and_nsecs(secs, 0),
826 precision: TimePrecision::Secs,
827 }
828 }
829
830 pub fn now_with_secs() -> UtcTimestamp {
831 UtcTimestamp::with_secs(Utc::now())
832 }
833
834 pub fn with_millis(date_time: DateTime<Utc>) -> UtcTimestamp {
837 let secs = date_time.timestamp();
838 let nsecs = date_time.timestamp_subsec_millis() * 1_000_000;
839 UtcTimestamp {
840 timestamp: Self::timestamp_from_secs_and_nsecs(secs, nsecs),
841 precision: TimePrecision::Millis,
842 }
843 }
844
845 pub fn with_micros(date_time: DateTime<Utc>) -> UtcTimestamp {
848 let secs = date_time.timestamp();
849 let nsecs = date_time.timestamp_subsec_micros() * 1_000;
850 UtcTimestamp {
851 timestamp: Self::timestamp_from_secs_and_nsecs(secs, nsecs),
852 precision: TimePrecision::Micros,
853 }
854 }
855
856 pub fn with_nanos(date_time: DateTime<Utc>) -> UtcTimestamp {
859 let secs = date_time.timestamp();
860 let nsecs = date_time.timestamp_subsec_nanos();
861 UtcTimestamp {
862 timestamp: Self::timestamp_from_secs_and_nsecs(secs, nsecs),
863 precision: TimePrecision::Nanos,
864 }
865 }
866
867 pub fn format_precisely(&self) -> DelayedFormat<StrftimeItems> {
869 match self.precision {
870 TimePrecision::Secs => self.format("%Y%m%d-%H:%M:%S"),
871 TimePrecision::Millis => self.format("%Y%m%d-%H:%M:%S%.3f"),
872 TimePrecision::Micros => self.format("%Y%m%d-%H:%M:%S%.6f"),
873 TimePrecision::Nanos => self.format("%Y%m%d-%H:%M:%S%.9f"),
874 }
875 }
876
877 pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
878 self.timestamp.format(fmt)
879 }
880
881 pub fn timestamp(&self) -> DateTime<Utc> {
882 self.timestamp
883 }
884
885 pub fn precision(&self) -> TimePrecision {
886 self.precision
887 }
888}
889
890impl UtcTimeOnly {
891 pub fn with_secs(time: NaiveTime) -> UtcTimeOnly {
894 UtcTimeOnly {
895 timestamp: time.with_nanosecond(0).unwrap(),
896 precision: TimePrecision::Secs,
897 }
898 }
899
900 pub fn with_millis(time: NaiveTime) -> UtcTimeOnly {
903 UtcTimeOnly {
904 timestamp: time.with_nanosecond(time.nanosecond() / 1_000_000).unwrap(),
905 precision: TimePrecision::Millis,
906 }
907 }
908
909 pub fn with_micros(time: NaiveTime) -> UtcTimeOnly {
912 UtcTimeOnly {
913 timestamp: time.with_nanosecond(time.nanosecond() / 1_000).unwrap(),
914 precision: TimePrecision::Micros,
915 }
916 }
917
918 pub fn with_nanos(time: NaiveTime) -> UtcTimeOnly {
921 UtcTimeOnly {
922 timestamp: time,
923 precision: TimePrecision::Nanos,
924 }
925 }
926
927 pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
928 self.timestamp.format(fmt)
929 }
930
931 pub fn timestamp(&self) -> NaiveTime {
932 self.timestamp
933 }
934
935 pub fn precision(&self) -> TimePrecision {
936 self.precision
937 }
938}
939
940#[cfg(test)]
941mod tests {
942 use super::*;
943
944 #[test]
945 fn fix_string_fail_on_ctrl_character() {
946 let buf = b"Hello\x01world!".to_vec();
947 assert!(FixString::from_ascii(buf).is_err());
948 }
949
950 #[test]
951 fn fix_string_fail_on_out_of_range_character() {
952 let buf = b"Hello\x85world!".to_vec();
953 assert!(FixString::from_ascii(buf).is_err());
954 }
955
956 #[test]
957 fn fix_string_replacemen_character_on_ctrl() {
958 let buf = b"Hello\x01world!".to_vec();
959 assert_eq!(FixString::from_ascii_lossy(buf), "Hello?world!");
960 }
961
962 #[test]
963 fn fix_string_replacemen_character_on_out_of_range() {
964 let buf = b"Hello\x85world!".to_vec();
965 assert_eq!(FixString::from_ascii_lossy(buf), "Hello?world!");
966 }
967
968 #[test]
969 fn utc_timestamp_default_precision_nanos() {
970 let now = UtcTimestamp::now();
971 assert_eq!(now.precision(), TimePrecision::Nanos);
972 }
973}