Skip to content

Commit

Permalink
date-time: LocalDateTime -> DateTime
Browse files Browse the repository at this point in the history
  • Loading branch information
bouzuya committed Oct 18, 2021
1 parent 3f67828 commit 91518fe
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 63 deletions.
30 changes: 15 additions & 15 deletions date_time/src/date_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use crate::{Date, ParseDateError, ParseTimeError, Time};
use thiserror::Error;

#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct LocalDateTime {
pub struct DateTime {
date: Date,
time: Time,
}

#[derive(Debug, Eq, Error, PartialEq)]
pub enum ParseLocalDateTimeError {
pub enum ParseDateTimeError {
#[error("invalid format")]
InvalidFormat,
#[error("invalid length")]
Expand All @@ -20,7 +20,7 @@ pub enum ParseLocalDateTimeError {
ParseTime(ParseTimeError),
}

impl LocalDateTime {
impl DateTime {
pub fn from_dt(date: Date, time: Time) -> Self {
Self { date, time }
}
Expand All @@ -34,25 +34,25 @@ impl LocalDateTime {
}
}

impl std::fmt::Display for LocalDateTime {
impl std::fmt::Display for DateTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}T{}", self.date, self.time)
}
}

impl std::str::FromStr for LocalDateTime {
type Err = ParseLocalDateTimeError;
impl std::str::FromStr for DateTime {
type Err = ParseDateTimeError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 19 {
return Err(Self::Err::InvalidLength);
}
let date = Date::from_str(&s[0..10]).map_err(ParseLocalDateTimeError::ParseDate)?;
let date = Date::from_str(&s[0..10]).map_err(ParseDateTimeError::ParseDate)?;
if s.as_bytes().get(10) != Some(&b'T') {
return Err(Self::Err::InvalidFormat);
}
let time = Time::from_str(&s[11..19]).map_err(ParseLocalDateTimeError::ParseTime)?;
Ok(LocalDateTime { date, time })
let time = Time::from_str(&s[11..19]).map_err(ParseDateTimeError::ParseTime)?;
Ok(DateTime { date, time })
}
}

Expand All @@ -67,16 +67,16 @@ mod tests {
let date = Date::from_str("2021-02-03")?;
let time = Time::from_str("04:05:06")?;
assert_eq!(
LocalDateTime::from_dt(date, time),
LocalDateTime::from_str("2021-02-03T04:05:06")?
DateTime::from_dt(date, time),
DateTime::from_str("2021-02-03T04:05:06")?
);
Ok(())
}

#[test]
fn str_conversion_test() {
type E = ParseLocalDateTimeError;
let f = |s: &str| LocalDateTime::from_str(s);
type E = ParseDateTimeError;
let f = |s: &str| DateTime::from_str(s);

assert!(matches!(f("2021-02-03T04:05:06"), Ok(_)));
assert!(matches!(f("20021-02-03T04:05:06"), Err(E::InvalidLength)));
Expand All @@ -91,14 +91,14 @@ mod tests {

#[test]
fn date_test() -> anyhow::Result<()> {
let dt = LocalDateTime::from_str("2021-02-03T04:05:06")?;
let dt = DateTime::from_str("2021-02-03T04:05:06")?;
assert_eq!(dt.date(), Date::from_str("2021-02-03")?);
Ok(())
}

#[test]
fn time_test() -> anyhow::Result<()> {
let dt = LocalDateTime::from_str("2021-02-03T04:05:06")?;
let dt = DateTime::from_str("2021-02-03T04:05:06")?;
assert_eq!(dt.time(), Time::from_str("04:05:06")?);
Ok(())
}
Expand Down
53 changes: 24 additions & 29 deletions date_time/src/offset_date_time.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
use std::{convert::TryFrom, str::FromStr};

use crate::{
Instant, LocalDateTime, ParseLocalDateTimeError, ParseTimeZoneOffsetError, TimeZoneOffset,
};
use crate::{DateTime, Instant, ParseDateTimeError, ParseTimeZoneOffsetError, TimeZoneOffset};

use thiserror::Error;

#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct OffsetDateTime {
local_date_time: LocalDateTime,
date_time: DateTime,
time_zone_offset: TimeZoneOffset,
}

#[derive(Debug, Eq, Error, PartialEq)]
pub enum ParseOffsetDateTimeError {
#[error("invalid length")]
InvalidLength,
#[error("parse local date time")]
ParseLocalDateTime(ParseLocalDateTimeError),
#[error("parse date time")]
ParseDateTime(ParseDateTimeError),
#[error("parse time zone offset")]
ParseTimeZoneOffset(ParseTimeZoneOffsetError),
}
Expand All @@ -29,10 +27,10 @@ pub enum TryFromOffsetDateTimeError {
}

impl OffsetDateTime {
pub fn new(local_date_time: LocalDateTime, time_zone_offset: TimeZoneOffset) -> Self {
pub fn new(date_time: DateTime, time_zone_offset: TimeZoneOffset) -> Self {
// FIXME: (1970-01-01T00:00:00 & -00:01) or (9999-12-31T00:00:00 & +00:01)
Self {
local_date_time,
date_time,
time_zone_offset,
}
}
Expand All @@ -44,16 +42,16 @@ impl OffsetDateTime {
let timestamp = i64::from(instant) + time_zone_offset.offset_in_minutes() as i64 * 60;
// check range
Instant::try_from(timestamp).map_err(|_| TryFromOffsetDateTimeError::OutOfRange)?;
let local_date_time = local_date_time_from_timestamp(timestamp);
Ok(Self::new(local_date_time, time_zone_offset))
let date_time = date_time_from_timestamp(timestamp);
Ok(Self::new(date_time, time_zone_offset))
}

pub fn instant(&self) -> Instant {
Instant::from(*self)
}

pub fn local_date_time(&self) -> LocalDateTime {
self.local_date_time
pub fn date_time(&self) -> DateTime {
self.date_time
}

pub fn time_zone_offset(&self) -> TimeZoneOffset {
Expand All @@ -66,7 +64,7 @@ impl std::fmt::Display for OffsetDateTime {
write!(
f,
"{}{}",
self.local_date_time,
self.date_time,
if self.time_zone_offset == TimeZoneOffset::utc() {
"Z".to_string()
} else {
Expand All @@ -83,8 +81,8 @@ impl std::str::FromStr for OffsetDateTime {
if s.len() != 25 && s.len() != 20 {
return Err(Self::Err::InvalidLength);
}
let local_date_time = LocalDateTime::from_str(&s[0..19])
.map_err(ParseOffsetDateTimeError::ParseLocalDateTime)?;
let date_time =
DateTime::from_str(&s[0..19]).map_err(ParseOffsetDateTimeError::ParseDateTime)?;
let time_zone_offset = if s.len() == 25 {
TimeZoneOffset::from_str(&s[19..25])
.map_err(ParseOffsetDateTimeError::ParseTimeZoneOffset)
Expand All @@ -96,38 +94,38 @@ impl std::str::FromStr for OffsetDateTime {
ParseTimeZoneOffsetError::InvalidFormat,
))
}?;
Ok(Self::new(local_date_time, time_zone_offset))
Ok(Self::new(date_time, time_zone_offset))
}
}

impl From<Instant> for OffsetDateTime {
fn from(instant: Instant) -> Self {
let local_date_time = local_date_time_from_timestamp(i64::from(instant));
Self::new(local_date_time, TimeZoneOffset::utc())
let date_time = date_time_from_timestamp(i64::from(instant));
Self::new(date_time, TimeZoneOffset::utc())
}
}

impl From<OffsetDateTime> for Instant {
fn from(offset_date_time: OffsetDateTime) -> Self {
let local_timestamp = timestamp_from_local_date_time(offset_date_time.local_date_time());
let local_timestamp = timestamp_from_date_time(offset_date_time.date_time());
let offset_in_seconds = offset_date_time.time_zone_offset().offset_in_minutes() as i64 * 60;
let utc_timestamp = local_timestamp - offset_in_seconds;
Instant::try_from(utc_timestamp).expect("OffsetDateTime is broken")
}
}

fn local_date_time_from_timestamp(timestamp: i64) -> LocalDateTime {
fn date_time_from_timestamp(timestamp: i64) -> DateTime {
use chrono::NaiveDateTime;

let naive_date_time = NaiveDateTime::from_timestamp(timestamp, 0);
LocalDateTime::from_str(&format!("{:?}", naive_date_time))
DateTime::from_str(&format!("{:?}", naive_date_time))
.expect("unexpected NaiveDateTime debug format")
}

fn timestamp_from_local_date_time(local_date_time: LocalDateTime) -> i64 {
fn timestamp_from_date_time(date_time: DateTime) -> i64 {
use chrono::NaiveDateTime;

NaiveDateTime::from_str(&local_date_time.to_string())
NaiveDateTime::from_str(&date_time.to_string())
.expect("unexpected NaiveDateTime::from_str")
.timestamp()
}
Expand Down Expand Up @@ -225,7 +223,7 @@ mod tests {
));
assert!(matches!(
f("2021+02-03T04:05:06+07:00"),
Err(E::ParseLocalDateTime(_))
Err(E::ParseDateTime(_))
));
assert!(matches!(
f("2021-02-03T04:05:06+07-00"),
Expand All @@ -247,12 +245,9 @@ mod tests {
}

#[test]
fn local_date_time_test() -> anyhow::Result<()> {
fn date_time_test() -> anyhow::Result<()> {
let dt = OffsetDateTime::from_str("2021-02-03T04:05:06+07:00")?;
assert_eq!(
dt.local_date_time(),
LocalDateTime::from_str("2021-02-03T04:05:06")?
);
assert_eq!(dt.date_time(), DateTime::from_str("2021-02-03T04:05:06")?);
Ok(())
}

Expand Down
35 changes: 16 additions & 19 deletions date_time/tests/v0.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{convert::TryFrom, str::FromStr};

use date_time::{Date, DayOfMonth, Instant, LocalDateTime, OffsetDateTime};
use date_time::{Date, DateTime, DayOfMonth, Instant, OffsetDateTime};

#[test]
fn use_case_offset_date_time_plus_days() -> anyhow::Result<()> {
Expand Down Expand Up @@ -35,18 +35,17 @@ fn use_case_offset_date_time_plus_days() -> anyhow::Result<()> {
fn use_case_offset_date_time_with_day_of_month() -> anyhow::Result<()> {
let with_day_of_month =
|offset_date_time: OffsetDateTime, day_of_month: u8| -> anyhow::Result<OffsetDateTime> {
let local_date_time = offset_date_time.local_date_time();
let date_time = offset_date_time.date_time();
let time_zone_offset = offset_date_time.time_zone_offset();
let date = local_date_time.date();
let time = local_date_time.time();
let date = date_time.date();
let time = date_time.time();

// TODO: day_of_month -> date -> date
let day_of_month = DayOfMonth::try_from(day_of_month)?;
let updated_date = Date::from_ymd(date.year(), date.month(), day_of_month)?;

let updated_local_date_time = LocalDateTime::from_dt(updated_date, time);
let updated_offset_date_time =
OffsetDateTime::new(updated_local_date_time, time_zone_offset);
let updated_date_time = DateTime::from_dt(updated_date, time);
let updated_offset_date_time = OffsetDateTime::new(updated_date_time, time_zone_offset);
Ok(updated_offset_date_time)
};

Expand All @@ -64,16 +63,15 @@ fn use_case_offset_date_time_with_day_of_month() -> anyhow::Result<()> {
#[test]
fn use_case_offset_date_time_next_date() -> anyhow::Result<()> {
let next_date = |offset_date_time: OffsetDateTime| -> anyhow::Result<OffsetDateTime> {
let local_date_time = offset_date_time.local_date_time();
let date_time = offset_date_time.date_time();
let time_zone_offset = offset_date_time.time_zone_offset();
let date = local_date_time.date();
let time = local_date_time.time();
let date = date_time.date();
let time = date_time.time();
let updated_date = date
.succ()
.ok_or_else(|| anyhow::anyhow!("LocalDate out of range"))?;
let updated_local_date_time = LocalDateTime::from_dt(updated_date, time);
let updated_offset_date_time =
OffsetDateTime::new(updated_local_date_time, time_zone_offset);
let updated_date_time = DateTime::from_dt(updated_date, time);
let updated_offset_date_time = OffsetDateTime::new(updated_date_time, time_zone_offset);
Ok(updated_offset_date_time)
};

Expand All @@ -90,10 +88,10 @@ fn use_case_offset_date_time_next_date() -> anyhow::Result<()> {
#[test]
fn use_case_offset_date_time_next_month() -> anyhow::Result<()> {
let next_month = |offset_date_time: OffsetDateTime| -> anyhow::Result<OffsetDateTime> {
let local_date_time = offset_date_time.local_date_time();
let date_time = offset_date_time.date_time();
let time_zone_offset = offset_date_time.time_zone_offset();
let date = local_date_time.date();
let time = local_date_time.time();
let date = date_time.date();
let time = date_time.time();
let next_year_month = date
.year_month()
.succ()
Expand All @@ -103,9 +101,8 @@ fn use_case_offset_date_time_next_month() -> anyhow::Result<()> {
next_year_month.month(),
date.day_of_month(),
)?;
let updated_local_date_time = LocalDateTime::from_dt(updated_date, time);
let updated_offset_date_time =
OffsetDateTime::new(updated_local_date_time, time_zone_offset);
let updated_date_time = DateTime::from_dt(updated_date, time);
let updated_offset_date_time = OffsetDateTime::new(updated_date_time, time_zone_offset);
Ok(updated_offset_date_time)
};

Expand Down

0 comments on commit 91518fe

Please sign in to comment.