Compare commits
10 commits
c0def257fb
...
f73aeb7769
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f73aeb7769 | ||
![]() |
10faf03a0f | ||
e1c975ba74 | |||
ffea975527 | |||
43f39fe226 | |||
559cfa3cb3 | |||
e2910695d2 | |||
![]() |
b3ea308997 | ||
9563b1da0d | |||
a440e730fa |
10 changed files with 166 additions and 44 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -11,36 +11,36 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.196"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.196"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -49,9 +49,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -60,6 +60,6 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
|
|
@ -4,6 +4,8 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
serde = ["dep:serde"]
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
|
|
19
README.md
Normal file
19
README.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
Contents
|
||||
========
|
||||
- [Summary](#summary)
|
||||
- [Usage](#usage)
|
||||
|
||||
## Summary
|
||||
Epoch is a small library for converting Unix timestamps to human readable datetime
|
||||
and vice versa. It includes functionality for displaying a DateTime as a human
|
||||
readable string, parsing from string, and comparison for equality or ordering.
|
||||
|
||||
This crate is ideal when you want a lighter and simpler date/time library than the
|
||||
excellent and very complete [chrono](https://crates.io/crates/chrono), which is
|
||||
likely overkill for many use cases.
|
||||
|
||||
## Usage
|
||||
Add epoch to your Cargo.toml
|
||||
```Toml
|
||||
epoch = { git = "http://git.hitchhiker-linux.org/jeang3nie/epoch-rs.git" }
|
||||
```
|
124
src/datetime.rs
124
src/datetime.rs
|
@ -1,13 +1,47 @@
|
|||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use {
|
||||
crate::{
|
||||
month::Month, weekday::Weekday, year::Year, zone::TimeZone, SECONDS_PER_DAY,
|
||||
SECONDS_PER_HOUR, SECONDS_PER_MINUTE,
|
||||
},
|
||||
serde::{Deserialize, Serialize},
|
||||
std::{cmp, fmt},
|
||||
std::{
|
||||
cmp, fmt,
|
||||
num::TryFromIntError,
|
||||
time::{SystemTime, SystemTimeError, UNIX_EPOCH},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
System(SystemTimeError),
|
||||
RangeError,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::System(e) => write!(f, "{e}"),
|
||||
Self::RangeError => write!(f, "self:?"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SystemTimeError> for Error {
|
||||
fn from(value: SystemTimeError) -> Self {
|
||||
Self::System(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryFromIntError> for Error {
|
||||
fn from(_value: TryFromIntError) -> Self {
|
||||
Self::RangeError
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub struct DateTime {
|
||||
pub year: Year,
|
||||
pub month: Month,
|
||||
|
@ -59,39 +93,40 @@ impl DateTime {
|
|||
seconds
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
/// Converts a Unix timestamp to a `DateTime` struct
|
||||
pub fn from_timestamp(ts: i64) -> Self {
|
||||
if ts < 0 {
|
||||
let mut seconds: i64 = 0;
|
||||
let mut year = Year::new(-1);
|
||||
while seconds > -year.seconds() {
|
||||
seconds -= year.seconds();
|
||||
let mut seconds: i64 = ts;
|
||||
let mut year = Year::new(1969);
|
||||
while seconds < -year.seconds() {
|
||||
seconds += year.seconds();
|
||||
year = year.previous();
|
||||
}
|
||||
let mut month = Some(Month::December);
|
||||
while month.is_some() {
|
||||
let m = month.unwrap();
|
||||
if seconds > m.seconds(year) {
|
||||
if -seconds < m.seconds(year) {
|
||||
break;
|
||||
}
|
||||
seconds -= m.seconds(year);
|
||||
seconds += m.seconds(year);
|
||||
month = m.previous();
|
||||
}
|
||||
let month = month.unwrap();
|
||||
let mut day = month.days(year);
|
||||
while day > 0 && seconds < -SECONDS_PER_DAY {
|
||||
seconds -= SECONDS_PER_HOUR;
|
||||
seconds += SECONDS_PER_DAY;
|
||||
day -= 1;
|
||||
}
|
||||
let mut hour: i8 = 23;
|
||||
while hour >= 0 && seconds < -SECONDS_PER_HOUR {
|
||||
seconds -= SECONDS_PER_HOUR;
|
||||
while hour > 0 && seconds < -SECONDS_PER_HOUR {
|
||||
seconds += SECONDS_PER_HOUR;
|
||||
hour -= 1;
|
||||
}
|
||||
let hour = hour.try_into().unwrap();
|
||||
let mut minute: i8 = 60;
|
||||
while minute >= 0 && seconds < -60 {
|
||||
seconds -= 60;
|
||||
let mut minute: i8 = 59;
|
||||
while minute > 0 && seconds < -60 {
|
||||
seconds += 60;
|
||||
minute -= 1;
|
||||
}
|
||||
let minute = minute.try_into().unwrap();
|
||||
|
@ -145,13 +180,30 @@ impl DateTime {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
/// Creates a `DateTime` from the system time
|
||||
pub fn now() -> Result<Self, Error> {
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH)?;
|
||||
Ok(Self::from_timestamp(i64::try_from(now.as_secs())?))
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
/// Gets the day of the week for this `DateTime`
|
||||
pub fn weekday(&self) -> Weekday {
|
||||
let ts = self.timestamp_naive();
|
||||
let days = ts / SECONDS_PER_DAY;
|
||||
let rem = days % 7;
|
||||
let mut days = ts / SECONDS_PER_DAY;
|
||||
// For negative timestamps this number is actually the following day
|
||||
if ts < 0 {
|
||||
days -= 1;
|
||||
}
|
||||
// Rusts `%` operator is modulo, not modulus. We have to use the
|
||||
// operation which will gove the correct answer for either positive
|
||||
// or negative remainders
|
||||
let rem = days.rem_euclid(7);
|
||||
rem.try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Returns a string representing this date and time in human readable format
|
||||
pub fn display(&self) -> String {
|
||||
format!(
|
||||
"{} {} {} {:0>2}:{:0>2}:{:0>2} {} {}",
|
||||
|
@ -227,6 +279,28 @@ mod tests {
|
|||
},
|
||||
};
|
||||
assert_eq!(dt.weekday(), Weekday::Saturday);
|
||||
dt = DateTime {
|
||||
year: Year::new(1965),
|
||||
month: Month::February,
|
||||
day: 9,
|
||||
hour: 4,
|
||||
minute: 4,
|
||||
second: 37,
|
||||
zone: TimeZone::Utc,
|
||||
};
|
||||
assert_eq!(dt.weekday(), Weekday::Tuesday);
|
||||
dt.day -= 1;
|
||||
assert_eq!(dt.weekday(), Weekday::Monday);
|
||||
dt.day -= 1;
|
||||
assert_eq!(dt.weekday(), Weekday::Sunday);
|
||||
dt.day -= 1;
|
||||
assert_eq!(dt.weekday(), Weekday::Saturday);
|
||||
dt.day -= 1;
|
||||
assert_eq!(dt.weekday(), Weekday::Friday);
|
||||
dt.day -= 1;
|
||||
assert_eq!(dt.weekday(), Weekday::Thursday);
|
||||
dt.day -= 1;
|
||||
assert_eq!(dt.weekday(), Weekday::Wednesday);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -268,6 +342,22 @@ mod tests {
|
|||
assert_eq!(dt, DateTime::from_timestamp(ts));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ts_negative() {
|
||||
let ts: i64 = -154382123;
|
||||
let dt = DateTime {
|
||||
year: Year::new(1965),
|
||||
month: Month::February,
|
||||
day: 9,
|
||||
hour: 4,
|
||||
minute: 4,
|
||||
second: 37,
|
||||
zone: TimeZone::Utc,
|
||||
};
|
||||
assert_eq!(dt.timestamp(), ts);
|
||||
assert_eq!(dt, DateTime::from_timestamp(ts));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fmt() {
|
||||
let mut dt = DateTime::from_timestamp(1706571482);
|
||||
|
|
|
@ -8,6 +8,8 @@ pub(crate) mod year;
|
|||
pub(crate) mod zone;
|
||||
|
||||
pub mod prelude;
|
||||
pub use datetime::DateTime;
|
||||
pub use datetime::Error as DateTimeError;
|
||||
|
||||
pub static SECONDS_PER_MINUTE: i64 = 60;
|
||||
pub static SECONDS_PER_HOUR: i64 = 60 * 60;
|
||||
|
|
11
src/month.rs
11
src/month.rs
|
@ -1,12 +1,13 @@
|
|||
use std::str::FromStr;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use {
|
||||
crate::{year::Year, SECONDS_PER_DAY},
|
||||
serde::{Deserialize, Serialize},
|
||||
std::{cmp, error, fmt},
|
||||
std::{cmp, error, fmt, str},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[repr(u8)]
|
||||
pub enum Month {
|
||||
Janurary = 1,
|
||||
|
@ -138,7 +139,7 @@ impl fmt::Display for Month {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Month {
|
||||
impl str::FromStr for Month {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
pub use crate::{
|
||||
datetime::DateTime,
|
||||
month::{Error as MonthError, Month},
|
||||
weekday::Weekday,
|
||||
year::Year,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[repr(u8)]
|
||||
pub enum Weekday {
|
||||
Thursday,
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use {
|
||||
crate::SECONDS_PER_DAY,
|
||||
serde::{Deserialize, Serialize},
|
||||
std::{cmp, fmt, num::ParseIntError, str::FromStr},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub enum Year {
|
||||
Normal(i32),
|
||||
Leap(i32),
|
||||
|
|
10
src/zone.rs
10
src/zone.rs
|
@ -1,10 +1,13 @@
|
|||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use {
|
||||
crate::SECONDS_PER_HOUR,
|
||||
serde::{Deserialize, Serialize},
|
||||
std::{error, fmt, str::FromStr},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub enum Sign {
|
||||
Positive,
|
||||
Negative,
|
||||
|
@ -23,7 +26,8 @@ impl fmt::Display for Sign {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub enum TimeZone {
|
||||
Offset { sign: Sign, hours: u8, minutes: u8 },
|
||||
Utc,
|
||||
|
|
Loading…
Add table
Reference in a new issue