time.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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. /// Wrapper struct to represent system timestamps.
  27. #[derive(
  28. Hash, Clone, Copy, Debug, SerialEncodable, SerialDecodable, PartialEq, PartialOrd, Ord, Eq,
  29. )]
  30. pub struct Timestamp(u64);
  31. impl Timestamp {
  32. /// Returns the inner `u64` of `Timestamp`
  33. pub fn inner(&self) -> u64 {
  34. self.0
  35. }
  36. /// Generate a `Timestamp` of the current time.
  37. pub fn current_time() -> Self {
  38. Self(UNIX_EPOCH.elapsed().unwrap().as_secs())
  39. }
  40. /// Calculates the elapsed time of a `Timestamp` up to the time of calling the function.
  41. pub fn elapsed(&self) -> Result<Self> {
  42. Self::current_time().checked_sub(*self)
  43. }
  44. /// Add `self` to a given timestamp
  45. /// Errors on integer overflow.
  46. pub fn checked_add(&self, ts: Timestamp) -> Result<Self> {
  47. if let Some(result) = self.inner().checked_add(ts.inner()) {
  48. Ok(Self(result))
  49. } else {
  50. Err(Error::AdditionOverflow)
  51. }
  52. }
  53. /// Subtract `self` with a given timestamp
  54. /// Errors on integer underflow.
  55. pub fn checked_sub(&self, ts: Timestamp) -> Result<Self> {
  56. if let Some(result) = self.inner().checked_sub(ts.inner()) {
  57. Ok(Self(result))
  58. } else {
  59. Err(Error::SubtractionUnderflow)
  60. }
  61. }
  62. pub const fn from_u64(x: u64) -> Self {
  63. Self(x)
  64. }
  65. }
  66. impl From<u64> for Timestamp {
  67. fn from(x: u64) -> Self {
  68. Self(x)
  69. }
  70. }
  71. impl std::fmt::Display for Timestamp {
  72. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  73. let date = timestamp_to_date(self.0, DateFormat::DateTime);
  74. write!(f, "{}", date)
  75. }
  76. }
  77. #[derive(Clone, Copy, Debug, SerialEncodable, SerialDecodable, PartialEq, PartialOrd, Eq)]
  78. pub struct NanoTimestamp(pub u128);
  79. impl NanoTimestamp {
  80. pub fn current_time() -> Self {
  81. Self(UNIX_EPOCH.elapsed().unwrap().as_nanos())
  82. }
  83. }
  84. impl std::fmt::Display for NanoTimestamp {
  85. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  86. let date = timestamp_to_date(self.0.try_into().unwrap(), DateFormat::Nanos);
  87. write!(f, "{}", date)
  88. }
  89. }
  90. pub enum DateFormat {
  91. Default,
  92. Date,
  93. DateTime,
  94. Nanos,
  95. }
  96. #[derive(Clone, Debug, Default)]
  97. pub struct DateTime {
  98. pub nanos: u32,
  99. pub sec: u32,
  100. pub min: u32,
  101. pub hour: u32,
  102. pub day: u32,
  103. pub month: u32,
  104. pub year: u32,
  105. }
  106. impl DateTime {
  107. pub fn new() -> Self {
  108. Self { nanos: 0, sec: 0, min: 0, hour: 0, day: 0, month: 0, year: 0 }
  109. }
  110. pub fn date(&self) -> Date {
  111. Date { day: self.day, month: self.month, year: self.year }
  112. }
  113. pub fn from_timestamp(secs: u64, nsecs: u32) -> Self {
  114. let leap_year = |year| -> bool { year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) };
  115. static MONTHS: [[u64; 12]; 2] = [
  116. [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
  117. [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
  118. ];
  119. let mut date_time = DateTime::new();
  120. let mut year = 1970;
  121. let time = secs % SECS_IN_DAY;
  122. let mut day_number = secs / SECS_IN_DAY;
  123. date_time.nanos = nsecs;
  124. date_time.sec = (time % MIN_IN_HOUR) as u32;
  125. date_time.min = ((time % SECS_IN_HOUR) / MIN_IN_HOUR) as u32;
  126. date_time.hour = (time / SECS_IN_HOUR) as u32;
  127. loop {
  128. let year_size = if leap_year(year) { 366 } else { 365 };
  129. if day_number >= year_size {
  130. day_number -= year_size;
  131. year += 1;
  132. } else {
  133. break
  134. }
  135. }
  136. date_time.year = year;
  137. let mut month = 0;
  138. while day_number >= MONTHS[if leap_year(year) { 1 } else { 0 }][month] {
  139. day_number -= MONTHS[if leap_year(year) { 1 } else { 0 }][month];
  140. month += 1;
  141. }
  142. date_time.month = month as u32 + 1;
  143. date_time.day = day_number as u32 + 1;
  144. date_time
  145. }
  146. }
  147. impl fmt::Display for DateTime {
  148. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  149. write!(
  150. f,
  151. "{:04}-{:02}-{:02} {:02}:{:02}:{:02} UTC",
  152. self.year, self.month, self.day, self.hour, self.min, self.sec
  153. )
  154. }
  155. }
  156. #[derive(Clone, Debug, Default)]
  157. pub struct Date {
  158. pub day: u32,
  159. pub month: u32,
  160. pub year: u32,
  161. }
  162. impl fmt::Display for Date {
  163. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  164. write!(f, "{:04}-{:02}-{:02} UTC", self.year, self.month, self.day)
  165. }
  166. }
  167. pub fn timestamp_to_date(timestamp: u64, format: DateFormat) -> String {
  168. if timestamp == 0 {
  169. return "".to_string()
  170. }
  171. match format {
  172. DateFormat::Default => "".to_string(),
  173. DateFormat::Date => DateTime::from_timestamp(timestamp, 0).date().to_string(),
  174. DateFormat::DateTime => DateTime::from_timestamp(timestamp, 0).to_string(),
  175. DateFormat::Nanos => {
  176. const A_BILLION: u64 = 1_000_000_000;
  177. let dt =
  178. DateTime::from_timestamp(timestamp / A_BILLION, (timestamp % A_BILLION) as u32);
  179. format!(
  180. "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{} UTC",
  181. dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec, dt.nanos
  182. )
  183. }
  184. }
  185. }
  186. #[cfg(test)]
  187. mod tests {
  188. use super::Timestamp;
  189. #[test]
  190. fn check_ts_add_overflow() {
  191. assert!(Timestamp::current_time().checked_add(u64::MAX.into()).is_err());
  192. }
  193. #[test]
  194. fn check_ts_sub_underflow() {
  195. let cur = Timestamp::current_time().checked_add(10_000.into()).unwrap();
  196. assert!(cur.elapsed().is_err());
  197. }
  198. }