initial commit: basic julian/gregorian tables + julian/gregorian date ⇔ julian day conversion

This commit is contained in:
YK 2024-12-07 06:12:33 +03:00
commit c4045c9142
7 changed files with 224 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
/Cargo.lock

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "esodate"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = "0.4.26"
thiserror = "1.0.47"

3
TODO.md Normal file
View File

@ -0,0 +1,3 @@
- [ ] use TryFrom for converting JulianDay into NaiveJulianDate instead of From
- [ ] Sanity checks for NJD are needed (?)
- [ ] Change trait impl

8
src/error.rs Normal file
View File

@ -0,0 +1,8 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConversionError {
#[error("Error during date conversion")]
Generic
}

171
src/julian.rs Normal file
View File

@ -0,0 +1,171 @@
#![allow(non_snake_case)]
use chrono::{ NaiveTime, NaiveDate, Datelike };
use crate::prelude::*;
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone, Debug)]
pub struct NaiveJulianDate {
year: i32,
day: u32,
month: u32,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct JulianDayNumber (u32);
impl From<NaiveDate> for JulianDayNumber {
fn from (value: NaiveDate) -> Self {
use tables::julian_gregorian::*;
let D = value.day();
let M = value.month();
let Y = value.year();
let h = M as i32 - m;
let g = Y as i32 + y - (n - h) / n;
let f = (h - 1 + n) % n;
let e = (p * g + q) / r + D as i32 - 1 - j;
let J = e + (s * f + t) / u;
let J = J - (3 * ((g + A) / 100)) / 4 - C;
Self(J as u32)
}
}
impl From<NaiveJulianDate> for JulianDayNumber {
fn from (NaiveJulianDate { day: D, year: Y, month: M }: NaiveJulianDate) -> Self {
use tables::julian_gregorian::*;
let h = M as i32 - m;
let g = Y as i32 + y - (n - h) / n;
let f = (h - 1 + n) % n;
let e = (p * g + q) / r + D as i32 - 1 - j;
let J = e + (s * f + t) / u;
Self(J as u32)
}
}
impl From<JulianDayNumber> for NaiveJulianDate {
fn from (JulianDayNumber(J): JulianDayNumber) -> Self {
use tables::julian_gregorian::*;
let f = J as i32 + j;
let e = r * f + v;
let g = e % p / r;
let h = u * g + w;
let day = (h % s / u + 1) as u32;
let month = ((h / s + m) % n + 1) as u32;
let year = e / p - y + (n + m - month as i32) / n;
Self { year, day, month }
}
}
impl TryFrom<JulianDayNumber> for NaiveDate {
type Error = ConversionError;
fn try_from (JulianDayNumber(J): JulianDayNumber) -> Result<Self, Self::Error> {
use tables::julian_gregorian::*;
let f = J as i32 + j + (((J as i32 * 4 + B) / 146_097) * 3) / 4 + C;
let e = r * f + v;
let g = e % p / r;
let h = u * g + w;
let day = (h % s / u + 1) as u32;
let month = ((h / s + m) % n + 1) as u32;
let year = e / p - y + (n + m - month as i32) / n;
Self::from_ymd_opt(year, month, day).ok_or_else(|| ConversionError::Generic)
}
}
impl From<NaiveDate> for NaiveJulianDate {
fn from (value: NaiveDate) -> Self {
Into::<JulianDayNumber>::into(value).into()
}
}
impl TryFrom<NaiveJulianDate> for NaiveDate {
type Error = ConversionError;
fn try_from (value: NaiveJulianDate) -> Result<Self, Self::Error> {
Into::<JulianDayNumber>::into(value).try_into()
}
}
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)]
pub struct NaiveJulianDateTime {
pub date: NaiveJulianDate,
pub time: NaiveTime,
}
#[cfg(test)]
#[allow(non_upper_case_globals)]
#[allow(unused_imports)]
mod test {
use super::*;
use crate::prelude::*;
const _jdt: NaiveJulianDate = NaiveJulianDate { year: 2024, day: 23, month: 11 };
const _jd: JulianDayNumber = JulianDayNumber(2460651);
fn gd () -> NaiveDate {
NaiveDate::from_ymd_opt(2024, 12, 6).unwrap()
}
#[test]
fn gregorian_date_to_julian_day_basic () {
let gd = gd();
let jd: JulianDayNumber = gd.into();
assert_eq!(_jd, jd);
}
#[test]
fn julian_day_to_julian_date_basic () {
let jdt: NaiveJulianDate = _jd.into();
assert_eq!(_jdt, jdt);
}
#[test]
fn gregorian_date_to_julian_day_to_gregorian_date_basic () {
let gd = gd();
let jd: JulianDayNumber = gd.into();
let nd: NaiveDate = jd.try_into().unwrap();
assert_eq!(gd, nd);
}
#[test]
fn julian_date_to_julian_day_to_julian_date_basic () {
let jd: JulianDayNumber = _jdt.into();
let nd: NaiveJulianDate = jd.try_into().unwrap();
assert_eq!(_jdt, nd);
}
#[test]
fn gregorian_date_to_julian_date_basic () {
let gd = gd();
let jd: NaiveJulianDate = gd.into();
assert_eq!(_jdt, jd);
}
#[test]
fn julian_date_to_gregorian_date_basic () {
let _gd = gd();
let gd: NaiveDate = _jdt.try_into().unwrap();
assert_eq!(gd, _gd);
}
}

9
src/lib.rs Normal file
View File

@ -0,0 +1,9 @@
pub mod tables;
pub mod julian;
pub mod error;
pub mod prelude {
pub use crate::tables;
pub use crate::error::*;
}

21
src/tables.rs Normal file
View File

@ -0,0 +1,21 @@
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
pub mod julian_gregorian {
// Taken from Explanatory Supplement to the Astronomical Almanac, 3rd Edition, p. 617
pub const y: i32 = 4716;
pub const j: i32 = 1401;
pub const m: i32 = 2;
pub const n: i32 = 12;
pub const r: i32 = 4;
pub const p: i32 = 1461;
pub const q: i32 = 0;
pub const v: i32 = 3;
pub const u: i32 = 5;
pub const s: i32 = 153;
pub const t: i32 = 2;
pub const w: i32 = 2;
pub const A: i32 = 184;
pub const B: i32 = 274244;
pub const C: i32 = -38;
}