Skip to content

Commit

Permalink
Add UniformDateOfBirth
Browse files Browse the repository at this point in the history
  • Loading branch information
bouzuya committed Dec 6, 2023
1 parent 454ddfa commit 8c99fb1
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 23 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -21,6 +21,7 @@ time = { version = "0.3.23", features = [
"formatting",
"macros",
"parsing",
"rand",
"serde",
] }
tokio = { version = "1.29.1", features = ["full"] }
Expand Down
95 changes: 72 additions & 23 deletions src/model/date_of_birth.rs
@@ -1,36 +1,18 @@
use std::str::FromStr;
use std::{ops::Range, str::FromStr};

use rand::{thread_rng, Rng};
use time::{macros::format_description, Date, Month, OffsetDateTime};
use time::{macros::format_description, Date, Duration, OffsetDateTime};

#[derive(Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DateOfBirth(Date);

impl DateOfBirth {
pub fn gen() -> Self {
let mut rng = thread_rng();
let current_year = OffsetDateTime::now_utc().year();
let year = rng.gen_range(current_year - 120..=current_year);
let month = rng.gen_range(1..=12);
let is_leap = (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
let last_day_of_month = match month {
2 => {
if is_leap {
29
} else {
28
}
}
4 | 6 | 9 | 11 => 30,
_ => 31,
};
let day = rng.gen_range(1..=last_day_of_month);
let date = Date::from_calendar_date(
year,
Month::try_from(month as u8).expect("invalid month"),
day,
)
.expect("invalid date");
let date = rng.gen::<Date>();
let date = Date::from_calendar_date(year, date.month(), date.day()).expect("invalid date");
Self(date)
}
}
Expand All @@ -56,8 +38,56 @@ impl serde::Serialize for DateOfBirth {
}
}

pub struct UniformDateOfBirth(Range<Date>);

impl rand::distributions::uniform::UniformSampler for UniformDateOfBirth {
type X = DateOfBirth;

fn new<B1, B2>(low: B1, high: B2) -> Self
where
B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
{
let (low, high) = (low.borrow().0, high.borrow().0);
if low >= high {
panic!("low must be less than high")
}
Self(low..high)
}

fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
where
B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
{
let (low, high) = (
low.borrow().0,
high.borrow().0.saturating_add(Duration::days(1)),
);
if low > high {
panic!("low must be less than or equal to high")
}
Self(low..high)
}

fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
DateOfBirth(
Date::from_julian_day(
rng.gen_range(self.0.start.to_julian_day()..self.0.end.to_julian_day()),
)
.expect("invalid date"),
)
}
}

impl rand::distributions::uniform::SampleUniform for DateOfBirth {
type Sampler = UniformDateOfBirth;
}

#[cfg(test)]
mod tests {
use std::collections::HashSet;

use super::*;

#[test]
Expand All @@ -68,6 +98,25 @@ mod tests {
Ok(())
}

#[test]
fn test_gen_range() -> anyhow::Result<()> {
let mut rng = rand::thread_rng();

let low = "2020-01-01".parse::<DateOfBirth>()?;
let high = "2020-01-02".parse::<DateOfBirth>()?;
let gen = rng.gen_range(low..high);
assert_eq!(gen, low);

let mut set = HashSet::new();
for _ in 0..100 {
let gen = rng.gen_range(low..=high);
set.insert(gen);
}
assert_eq!(set.len(), 2);

Ok(())
}

#[test]
fn test_gen() {
assert_ne!(DateOfBirth::gen(), DateOfBirth::gen());
Expand Down

0 comments on commit 8c99fb1

Please sign in to comment.