ledger_models/fintekkers/wrappers/models/utils/
datetime.rs1use chrono::{DateTime, Local, NaiveDateTime, TimeZone, Utc};
2use chrono_tz::Tz;
3use prost_types::Timestamp;
4use std::borrow::Borrow;
5use std::fmt;
6use std::str::FromStr;
7use std::string::ParseError;
8
9use crate::fintekkers::models::util::LocalTimestampProto;
10
11pub struct LocalTimestampWrapper {
12 pub proto: LocalTimestampProto,
13}
14
15impl AsRef<LocalTimestampProto> for LocalTimestampWrapper {
16 fn as_ref(&self) -> &LocalTimestampProto {
17 &self.proto
18 }
19}
20
21
22impl LocalTimestampWrapper {
23 pub fn new(proto: LocalTimestampProto) -> Self {
24 LocalTimestampWrapper { proto }
25 }
26
27 pub fn now() -> Self {
28 let time_zone_str = iana_time_zone::get_timezone().unwrap_or_else(|_| {
29 panic!(
30 "{}",
31 String::from(
32 "Failed to get default timezone from \
33 the operating system"
34 )
35 )
36 });
37
38 let now = Local::now().naive_local();
39 Self::from_datetime(now, time_zone_str)
40 }
41
42
43 pub fn from_utc_datetime(now: NaiveDateTime) -> Self {
44 Self::from_datetime(now, "UTC".to_string())
45 }
46
47 pub fn from_datetime(now: NaiveDateTime, time_zone_str: String) -> Self {
48 let seconds = now.timestamp();
49 let nanos = now.timestamp_subsec_nanos();
50
51 let timestamp = Timestamp {
52 seconds,
53 nanos: nanos as i32,
54 };
55
56 LocalTimestampWrapper {
57 proto: LocalTimestampProto {
58 timestamp: Some(timestamp),
59 time_zone: time_zone_str,
60 },
61 }
62 }
63}
64
65
66impl From<&LocalTimestampWrapper> for NaiveDateTime {
67 fn from(wrapper: &LocalTimestampWrapper) -> NaiveDateTime {
68 let timestamp = wrapper.proto.timestamp.as_ref().unwrap().clone();
69
70 let naive_date_time =
71 NaiveDateTime::from_timestamp_opt(timestamp.seconds, timestamp.nanos as u32).unwrap();
72
73 naive_date_time
74 }
75}
76
77impl From<&LocalTimestampWrapper> for DateTime<Tz> {
78 fn from(wrapper: &LocalTimestampWrapper) -> DateTime<Tz> {
79 let timestamp = wrapper.proto.timestamp.as_ref().unwrap().clone();
80
81 let naive_date_time =
82 NaiveDateTime::from_timestamp_opt(timestamp.seconds, timestamp.nanos as u32).unwrap();
83
84 let tz: Tz = wrapper.proto.time_zone.parse().unwrap();
85 let date_timezone = tz.offset_from_utc_datetime(&naive_date_time);
86
87 DateTime::from_utc(naive_date_time, date_timezone)
88 }
89}
90
91impl From<&LocalTimestampWrapper> for DateTime<Utc> {
92 fn from(wrapper: &LocalTimestampWrapper) -> DateTime<Utc> {
93 let timestamp = wrapper.proto.timestamp.as_ref().unwrap().clone();
94
95 Utc.timestamp_opt(timestamp.seconds, timestamp.nanos as u32)
96 .unwrap()
97 }
98}
99impl From<LocalTimestampWrapper> for LocalTimestampProto {
100 fn from(wrapper: LocalTimestampWrapper) -> LocalTimestampProto {
101 wrapper.proto
102 }
103}
104
105impl fmt::Display for LocalTimestampWrapper {
106 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
107 let datetime: DateTime<Tz> = self.into();
108 fmt.write_str(datetime.to_string().borrow()).unwrap();
109 Ok(())
110 }
111}
112
113impl FromStr for LocalTimestampWrapper {
120 type Err = ParseError;
121
122 fn from_str(s: &str) -> Result<Self, Self::Err> {
123 let datetime: DateTime<Utc> = DateTime::parse_from_rfc3339(s)
124 .map(|dt| dt.with_timezone(&Utc))
125 .expect("");
126
127 let time_zone_str = iana_time_zone::get_timezone().unwrap_or_else(|_| {
128 panic!(
129 "{}",
130 String::from(
131 "Failed to get default timezone from \
132 the operating system"
133 )
134 )
135 });
136
137 Ok(LocalTimestampWrapper {
138 proto: LocalTimestampProto {
139 timestamp: Some(create_timestamp_from_datetime(datetime)),
140 time_zone: time_zone_str,
141 },
142 })
143 }
144}
145
146fn create_timestamp_from_datetime(now: DateTime<Utc>) -> Timestamp {
147 let seconds = now.timestamp();
148 let nanos = now.timestamp_subsec_nanos() as i32;
149 Timestamp { seconds, nanos }
150}
151#[cfg(test)]
152mod test {
153 use super::*;
154
155 #[test]
156 fn test_proto_to_date() {
157 let input = LocalTimestampWrapper {
158 proto: LocalTimestampProto {
159 timestamp: Some(Timestamp {
160 seconds: 1,
161 nanos: 0,
162 }),
163 time_zone: "America/New_York".to_string(),
164 },
165 };
166
167 let output: DateTime<Tz> = input.borrow().into();
168
169 assert_eq!(output.to_string(), "1969-12-31 19:00:01 EST");
172 }
173
174 #[test]
175 fn test_date_from_naive_date() {
176 let date_time: DateTime<Utc> = Utc::now();
177 let wrapper = LocalTimestampWrapper::from_utc_datetime(date_time.naive_utc());
178
179 let wrapper_seconds = wrapper.proto.timestamp.unwrap().seconds;
181 assert_eq!(date_time.timestamp(), wrapper_seconds);
182 assert_eq!(date_time.timezone().to_string(), wrapper.proto.time_zone);
183
184 let utc_wrapper = LocalTimestampWrapper::from_datetime(date_time.naive_utc(), "UTC".to_string());
186
187 let utc_wrapper_seconds = utc_wrapper.proto.timestamp.unwrap().seconds;
188 assert_eq!(date_time.timestamp(), utc_wrapper_seconds);
189 assert_eq!(
190 date_time.timezone().to_string(),
191 utc_wrapper.proto.time_zone
192 );
193
194 let ny_wrapper =
196 LocalTimestampWrapper::from_datetime(date_time.naive_local(), "America/New_York".to_string());
197
198 let ny_wrapper_seconds = ny_wrapper.proto.timestamp.unwrap().seconds;
199 assert_eq!(date_time.timestamp(), ny_wrapper_seconds);
200 assert_eq!("America/New_York".to_string(), ny_wrapper.proto.time_zone);
201 }
202
203 #[test]
204 fn test_date_from_string() {
205 let date = LocalTimestampWrapper::from_str("2023-03-17T12:34:56Z").unwrap();
206
207 let option = date.proto.timestamp.unwrap();
208 assert_eq!(option.seconds, 1679056496);
209 }
210
211
212 #[test]
213 fn test_timezones() {
214 let now = LocalTimestampWrapper::now();
215 let string = now.to_string();
216 println!("{}", string);
217 }
218
219}