time.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{fmt, time::UNIX_EPOCH};
  19. #[cfg(feature = "async-serial")]
  20. use darkfi_serial::async_trait;
  21. use darkfi_serial::{SerialDecodable, SerialEncodable};
  22. use crate::{Error, Result};
  23. const SECS_IN_DAY: u64 = 86400;
  24. const MIN_IN_HOUR: u64 = 60;
  25. const SECS_IN_HOUR: u64 = 3600;
  26. /// Represents the number of days in each month for both leap and non-leap years.
  27. const DAYS_IN_MONTHS: [[u64; 12]; 2] = [
  28. [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
  29. [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], // Leap years
  30. ];
  31. /// Wrapper struct to represent system timestamps.
  32. #[derive(
  33. Hash,
  34. Clone,
  35. Copy,
  36. Debug,
  37. SerialEncodable,
  38. SerialDecodable,
  39. PartialEq,
  40. PartialOrd,
  41. Ord,
  42. Eq,
  43. Default,
  44. )]
  45. pub struct Timestamp(u64);
  46. impl Timestamp {
  47. /// Returns the inner `u64` of `Timestamp`
  48. pub fn inner(&self) -> u64 {
  49. self.0
  50. }
  51. /// Generate a `Timestamp` of the current time.
  52. pub fn current_time() -> Self {
  53. Self(UNIX_EPOCH.elapsed().unwrap().as_secs())
  54. }
  55. /// Calculates the elapsed time of a `Timestamp` up to the time of calling the function.
  56. pub fn elapsed(&self) -> Result<Self> {
  57. Self::current_time().checked_sub(*self)
  58. }
  59. /// Add `self` to a given timestamp
  60. /// Errors on integer overflow.
  61. pub fn checked_add(&self, ts: Timestamp) -> Result<Self> {
  62. if let Some(result) = self.inner().checked_add(ts.inner()) {
  63. Ok(Self(result))
  64. } else {
  65. Err(Error::AdditionOverflow)
  66. }
  67. }
  68. /// Subtract `self` with a given timestamp
  69. /// Errors on integer underflow.
  70. pub fn checked_sub(&self, ts: Timestamp) -> Result<Self> {
  71. if let Some(result) = self.inner().checked_sub(ts.inner()) {
  72. Ok(Self(result))
  73. } else {
  74. Err(Error::SubtractionUnderflow)
  75. }
  76. }
  77. pub const fn from_u64(x: u64) -> Self {
  78. Self(x)
  79. }
  80. }
  81. impl From<u64> for Timestamp {
  82. fn from(x: u64) -> Self {
  83. Self(x)
  84. }
  85. }
  86. impl fmt::Display for Timestamp {
  87. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  88. let date = timestamp_to_date(self.0, DateFormat::DateTime);
  89. write!(f, "{}", date)
  90. }
  91. }
  92. #[derive(Clone, Copy, Debug, SerialEncodable, SerialDecodable, PartialEq, PartialOrd, Eq)]
  93. pub struct NanoTimestamp(pub u128);
  94. impl NanoTimestamp {
  95. pub fn current_time() -> Self {
  96. Self(UNIX_EPOCH.elapsed().unwrap().as_nanos())
  97. }
  98. }
  99. impl fmt::Display for NanoTimestamp {
  100. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  101. let date = timestamp_to_date(self.0.try_into().unwrap(), DateFormat::Nanos);
  102. write!(f, "{}", date)
  103. }
  104. }
  105. pub enum DateFormat {
  106. Default,
  107. Date,
  108. DateTime,
  109. Nanos,
  110. }
  111. /// Represents a UTC `DateTime` with individual fields for date and time components.
  112. #[derive(Clone, Debug, Default, Eq, PartialEq, SerialEncodable, SerialDecodable)]
  113. pub struct DateTime {
  114. pub year: u32,
  115. pub month: u32,
  116. pub day: u32,
  117. pub hour: u32,
  118. pub min: u32,
  119. pub sec: u32,
  120. pub nanos: u32,
  121. }
  122. impl DateTime {
  123. pub fn new() -> Self {
  124. Self { year: 0, month: 0, day: 0, hour: 0, min: 0, sec: 0, nanos: 0 }
  125. }
  126. pub fn date(&self) -> Date {
  127. Date { year: self.year, month: self.month, day: self.day }
  128. }
  129. pub fn from_timestamp(secs: u64, nsecs: u32) -> Self {
  130. let leap_year = |year| -> bool { year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) };
  131. let mut date_time = DateTime::new();
  132. let mut year = 1970;
  133. let time = secs % SECS_IN_DAY;
  134. let mut day_number = secs / SECS_IN_DAY;
  135. date_time.nanos = nsecs;
  136. date_time.sec = (time % MIN_IN_HOUR) as u32;
  137. date_time.min = ((time % SECS_IN_HOUR) / MIN_IN_HOUR) as u32;
  138. date_time.hour = (time / SECS_IN_HOUR) as u32;
  139. loop {
  140. let year_size = if leap_year(year) { 366 } else { 365 };
  141. if day_number >= year_size {
  142. day_number -= year_size;
  143. year += 1;
  144. } else {
  145. break
  146. }
  147. }
  148. date_time.year = year;
  149. let mut month = 0;
  150. while day_number >= DAYS_IN_MONTHS[if leap_year(year) { 1 } else { 0 }][month] {
  151. day_number -= DAYS_IN_MONTHS[if leap_year(year) { 1 } else { 0 }][month];
  152. month += 1;
  153. }
  154. date_time.month = month as u32 + 1;
  155. date_time.day = day_number as u32 + 1;
  156. date_time
  157. }
  158. /// Provides a `DateTime` instance from a string in "YYYY-MM-DDTHH:mm:ss" format.
  159. ///
  160. /// This function parses and validates the timestamp string, returning a `DateTime` instance
  161. /// with the parsed year, month, day, hour, minute, and second. Nanoseconds are not included
  162. /// in the input string and default to zero. If the input string does not match the expected
  163. /// format or contains invalid date or time values, it returns an [`Error::ParseFailed`] error.
  164. pub fn from_timestamp_str(timestamp_str: &str) -> Result<Self> {
  165. // Split the input string into date and time based on the 'T' separator
  166. let parts: Vec<&str> = timestamp_str.split('T').collect();
  167. // Check if the split parts have the correct length
  168. if parts.len() != 2 {
  169. return Err(Error::ParseFailed("Invalid timestamp format"));
  170. }
  171. // Parse the date into a vec
  172. let date_components: Vec<u32> = parts[0]
  173. .split('-')
  174. .map(|s| s.parse::<u32>().map_err(|_| Error::ParseFailed("Invalid date component")))
  175. .collect::<Result<Vec<u32>>>()?;
  176. // Verify year, month, and day are provided
  177. if date_components.len() != 3 {
  178. return Err(Error::ParseFailed("Invalid date format"));
  179. }
  180. // Parse the time into a vec
  181. let time_components: Vec<u32> = parts[1]
  182. .split(':')
  183. .map(|s| s.parse::<u32>().map_err(|_| Error::ParseFailed("Invalid time component")))
  184. .collect::<Result<Vec<u32>>>()?;
  185. // Verify that hour, minute, second are provided
  186. if time_components.len() != 3 {
  187. return Err(Error::ParseFailed("Invalid time format"));
  188. }
  189. // Destructure the date components into year, month, and day
  190. let (year, month, day) = (date_components[0], date_components[1], date_components[2]);
  191. // Validate month and day
  192. if !(1..=12).contains(&month) || !Self::is_valid_day(year, month, day) {
  193. return Err(Error::ParseFailed("Invalid month or day"));
  194. }
  195. // Destructure the time components into hour, minute, and second
  196. let (hour, min, sec) = (time_components[0], time_components[1], time_components[2]);
  197. // Validate hour, minute, and second values
  198. if hour > 23 || min > 59 || sec > 59 {
  199. return Err(Error::ParseFailed("Invalid hour, minute or second"));
  200. }
  201. // Return a new DateTime instance with parsed values and default nanoseconds set to 0
  202. Ok(DateTime { year, month, day, hour, min, sec, nanos: 0 })
  203. }
  204. /// Auxiliary function that determines whether the specified day is within the valid range
  205. /// for the given month and year, accounting for leap years. It returns `true` if the day
  206. /// is valid.
  207. fn is_valid_day(year: u32, month: u32, day: u32) -> bool {
  208. let days_in_month = DAYS_IN_MONTHS
  209. [(year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) as usize]
  210. [(month - 1) as usize];
  211. day > 0 && day <= days_in_month as u32
  212. }
  213. }
  214. impl fmt::Display for DateTime {
  215. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  216. write!(
  217. f,
  218. "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}",
  219. self.year, self.month, self.day, self.hour, self.min, self.sec
  220. )
  221. }
  222. }
  223. #[derive(Clone, Debug, Default)]
  224. pub struct Date {
  225. pub day: u32,
  226. pub month: u32,
  227. pub year: u32,
  228. }
  229. impl fmt::Display for Date {
  230. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  231. write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
  232. }
  233. }
  234. // TODO: fix logic and add corresponding test case
  235. pub fn timestamp_to_date(timestamp: u64, format: DateFormat) -> String {
  236. if timestamp == 0 {
  237. return "".to_string();
  238. }
  239. match format {
  240. DateFormat::Default => "".to_string(),
  241. DateFormat::Date => DateTime::from_timestamp(timestamp, 0).date().to_string(),
  242. DateFormat::DateTime => DateTime::from_timestamp(timestamp, 0).to_string(),
  243. DateFormat::Nanos => {
  244. const A_BILLION: u64 = 1_000_000_000;
  245. let dt =
  246. DateTime::from_timestamp(timestamp / A_BILLION, (timestamp % A_BILLION) as u32);
  247. format!(
  248. "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{}",
  249. dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec, dt.nanos
  250. )
  251. }
  252. }
  253. }
  254. #[cfg(test)]
  255. mod tests {
  256. use super::{DateTime, Timestamp};
  257. #[test]
  258. fn check_ts_add_overflow() {
  259. assert!(Timestamp::current_time().checked_add(u64::MAX.into()).is_err());
  260. }
  261. #[test]
  262. fn check_ts_sub_underflow() {
  263. let cur = Timestamp::current_time().checked_add(10_000.into()).unwrap();
  264. assert!(cur.elapsed().is_err());
  265. }
  266. #[test]
  267. /// Tests the `from_timestamp_str` function to ensure it correctly converts timestamp strings into `DateTime` instances.
  268. fn test_from_timestamp_str() {
  269. // Verify validate dates
  270. let valid_timestamps = vec![
  271. (
  272. "2024-01-01T12:00:00",
  273. DateTime { year: 2024, month: 1, day: 1, hour: 12, min: 0, sec: 0, nanos: 0 },
  274. ),
  275. (
  276. "2024-02-29T23:59:59",
  277. DateTime { year: 2024, month: 2, day: 29, hour: 23, min: 59, sec: 59, nanos: 0 },
  278. ), // Leap year
  279. (
  280. "2023-12-31T00:00:00",
  281. DateTime { year: 2023, month: 12, day: 31, hour: 0, min: 0, sec: 0, nanos: 0 },
  282. ),
  283. (
  284. "1970-01-01T00:00:00",
  285. DateTime { year: 1970, month: 1, day: 1, hour: 0, min: 0, sec: 0, nanos: 0 },
  286. ), // Unix epoch
  287. ];
  288. for (timestamp_str, expected) in valid_timestamps {
  289. let result = DateTime::from_timestamp_str(timestamp_str)
  290. .expect("Valid timestamp should not fail");
  291. assert_eq!(result, expected);
  292. }
  293. // Verify boundary conditions
  294. let boundary_timestamps = vec![
  295. (
  296. "2023-02-28T23:59:59",
  297. DateTime { year: 2023, month: 2, day: 28, hour: 23, min: 59, sec: 59, nanos: 0 },
  298. ),
  299. (
  300. "2023-03-01T00:00:00",
  301. DateTime { year: 2023, month: 3, day: 1, hour: 0, min: 0, sec: 0, nanos: 0 },
  302. ),
  303. (
  304. "2024-02-29T12:30:30",
  305. DateTime { year: 2024, month: 2, day: 29, hour: 12, min: 30, sec: 30, nanos: 0 },
  306. ), // Leap year
  307. ];
  308. for (timestamp_str, expected) in boundary_timestamps {
  309. let result = DateTime::from_timestamp_str(timestamp_str)
  310. .expect("Valid timestamp should not fail");
  311. assert_eq!(result, expected);
  312. }
  313. // Verify invalid timestamps
  314. let invalid_timestamps = vec![
  315. "2023-02-30T12:00:00", // Invalid day
  316. "2023-04-31T12:00:00", // Invalid day
  317. "2023-13-01T12:00:00", // Invalid month
  318. "2023-01-01T12.00.00", // Invalid format
  319. "2023-01-01", // Missing time part
  320. "2023-01-01 12.00.00", // Missing T separator
  321. "2023/01/01T12:00", // Incorrect date separator
  322. "2023-01-01T-12:-60:-60", // Invalid time components
  323. ];
  324. for timestamp_str in invalid_timestamps {
  325. let result = DateTime::from_timestamp_str(timestamp_str);
  326. assert!(result.is_err(), "Expected error for invalid timestamp '{}'", timestamp_str);
  327. }
  328. }
  329. }