Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 49 additions & 58 deletions src/db/table/operations/helpers/datetime_functions/julian_day.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ const JULIAN_DAY_NOON_OFFSET: f64 = 0.5;
const UNIX_EPOCH_JULIAN_DAY: f64 = 2440587.5;
const JULIAN_DAY_EPOCH_OFFSET: i64 = 32045;
const YEAR_OFFSET: i64 = 4800;

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct JulianDay {
jdn: f64,
is_subsecond: bool,
}

impl JulianDay {
pub fn as_date(&self) -> String {
pub fn to_calendar_components(&self) -> (i64, i64, i64, i64, i64, i64, f64) {
let jdn_value = self.value();
let jd_int: i64 = ((jdn_value + JULIAN_DAY_NOON_OFFSET).floor()) as i64;
let jd_int = ((jdn_value + JULIAN_DAY_NOON_OFFSET).floor()) as i64;
let jd_fractional = (jdn_value + JULIAN_DAY_NOON_OFFSET) - (jd_int as f64);

// Converts Julian Day Number to Gregorian calendar components using inverse calendar-to-JDN formula (see https://en.wikipedia.org/wiki/Julian_day#Converting_Julian_or_Gregorian_calendar_date_to_Julian_Day_Number)
let day = ((5
* (((4 * (jd_int + 1401 + (((4 * jd_int + 274277) / 146097) * 3) / 4 - 38) + 3)
% 1461)
Expand All @@ -34,49 +36,41 @@ impl JulianDay {
/ 1461
- 4716
+ (12 + 2 - month) / 12;

let total_seconds = (jd_fractional * 86400.0 * 1000.0).round() / 1000.0;
let hour = (total_seconds / 3600.0).floor() as i64;
let minute = ((total_seconds % 3600.0) / 60.0).floor() as i64;
let second_val = (total_seconds % 3600.0) % 60.0;
let second = second_val.floor() as i64;
let subsecond = second_val - second as f64;

(year, month, day, hour, minute, second, subsecond)
}

pub fn as_date(&self) -> String {
let (year, month, day, _, _, _, _) = self.to_calendar_components();
format!("{:04}-{:02}-{:02}", year, month, day)
}

pub fn as_time(&self) -> String {
let jdn_value = self.value();
let jd_int = ((jdn_value + JULIAN_DAY_NOON_OFFSET).floor()) as i64;
let jd_fractional = (jdn_value + JULIAN_DAY_NOON_OFFSET) - (jd_int as f64);
let total_seconds = jd_fractional * 86400.0;
let hour = (total_seconds / 3600.0).floor() as i64;
let minute = ((total_seconds % 3600.0) / 60.0).floor() as i64;
let second_with_fraction = (total_seconds % 3600.0) % 60.0;
let second = second_with_fraction.floor() as i64;

if self.is_subsecond {
let fractional_seconds = second_with_fraction - second as f64;
let milliseconds = (fractional_seconds * 1000.0).round() as i64;
format!(
"{:02}:{:02}:{:02}.{:03}",
hour, minute, second, milliseconds
)
} else {
format!("{:02}:{:02}:{:02}", hour, minute, second)
}
let (_, _, _, hour, minute, second, _) = self.to_calendar_components();
format!("{:02}:{:02}:{:02}", hour, minute, second)
}

pub fn as_datetime(&self) -> String {
format!("{} {}", self.as_date(), self.as_time())
let (year, month, day, hour, minute, second, _) = self.to_calendar_components();
format!(
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
year, month, day, hour, minute, second
)
}

pub fn as_unix_epoch(&self) -> f64 {
let jdn_value = self.value();
if self.is_subsecond {
(jdn_value - UNIX_EPOCH_JULIAN_DAY) * 86400000.0
} else {
(jdn_value - UNIX_EPOCH_JULIAN_DAY) * 86400.0
}
(self.jdn - UNIX_EPOCH_JULIAN_DAY) * 86400.0
}

pub fn new(jdn: f64) -> Self {
Self {
jdn,
is_subsecond: false,
}
Self { jdn }
}

// https://en.wikipedia.org/wiki/Julian_day
Expand All @@ -97,38 +91,16 @@ impl JulianDay {
let year_int = year.floor() as i64;
let month_int = month.floor() as i64;
let day_int = day.floor() as i64;
let day_fraction = day - day.floor();

let a = (14 - month_int) / 12;
let y = year_int + YEAR_OFFSET - a;
let m = month_int + 12 * a - 3;

let jdn_int = day_int + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400
- JULIAN_DAY_EPOCH_OFFSET;

let jdn = (jdn_int as f64) + day_fraction + time_fraction - JULIAN_DAY_NOON_OFFSET;
Self {
jdn,
is_subsecond: false,
}
}

pub fn new_relative_from_datetime_vals(
y: f64,
m: f64,
d: f64,
h: f64,
mi: f64,
s: f64,
fs: f64,
) -> Self {
let jdn = Self::new_from_datetime_vals(y, m, d, h, mi, s, fs).value();
let gregorian_year_zero =
Self::new_from_datetime_vals(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).value();
let jdn = jdn - gregorian_year_zero;
Self {
jdn,
is_subsecond: false,
}
let jdn = (jdn_int as f64) + (day - day.floor()) + time_fraction - JULIAN_DAY_NOON_OFFSET;
Self { jdn }
}

pub fn value(&self) -> f64 {
Expand All @@ -139,3 +111,22 @@ impl JulianDay {
&mut self.jdn
}
}

pub fn days_in_month(year: i64, month: i64) -> i64 {
match month {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
2 => {
if is_leap_year(year) {
29
} else {
28
}
}
_ => unreachable!(),
}
}

pub fn is_leap_year(year: i64) -> bool {
(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
}
Loading
Loading