Programmieren in Rust

Algorithmen: Zeitrechnung

Inhaltsverzeichnis

  1. Julianisches Datum
  2. Wochentage

Julianisches Datum

Das julianische Datum ist eine Zählung der Tage seit dem 1. Januar −4712 um 12:00 Uhr. Die Bewandtnis der Umrechnung zwischen Kalenderdatum und julianischem Daten ist

  1. die Entfernung der Komplexität des gregorianischen Kalenders, die astronomische Rechnungen obstruieren würde,
  2. die Erzeugung von Kalenderdaten.

Umrechnung

// Angabe des Kalenders.
// Greg für gregorianischer Kalender,
// Jul  für julianischer Kalender.
pub enum Calendar {Greg, Jul}

// Bruchteil eines Tages sind Stunden/24 + Minuten/(24*60).
pub struct Date {
    pub year: i32, pub month: i32, pub day: f64
}

// Umrechnung eines Kalenderdatums ins julianische Datum.
pub fn julian_date(calendar: Calendar, date: Date) -> f64
{
    let (y, m) = if date.month > 2 {
        (f64::from(date.year), f64::from(date.month))
    } else {
        (f64::from(date.year - 1), f64::from(date.month + 12))
    };

    let b = if let Calendar::Jul = calendar {0.0} else {
        2.0 - (y/100.0).floor() + (y/400.0).floor()
    };

    (365.25*(y + 4716.0)).floor()
    + (30.6001*(m + 1.0)).floor()
    + date.day + b - 1524.5
}

// Umrechnung eines julianischen Datums ins Kalenderdatum.
#[allow(non_snake_case)]
pub fn calendar_date(calendar: Calendar, jd: f64) -> Date {
    let Z = (jd + 0.5).floor();
    let F = jd + 0.5 - Z;
    let A = if let Calendar::Jul = calendar {Z} else {
        let alpha = ((Z - 1867216.25)/36524.25).floor();
        Z + 1.0 + alpha - (alpha/4.0).floor()
    };
    let B = A + 1524.0;
    let C = ((B - 122.1)/365.25).floor();
    let D = (365.25*C).floor();
    let E = ((B - D)/30.6001).floor();

    let day = B - D - (30.6001*E).floor() + F;
    if E <= 13.0 {
        Date {day, month: E as i32 - 1, year: C as i32 - 4716}
    } else {
        Date {day, month: E as i32 - 13, year: C as i32 - 4715}
    }
}

Erzeugung von Kalenderdaten

Diesen Umrechnungen dienen nun als Hilfsmittel zur Formulierung der Funktion shift_date, die das um eine Anzahl von Tagen verschobene Datum berechnet. Im julianischen Datum ist diese Verschiebung nämlich einfach die Addition der Anzahl. Demzufolge muss man zunächst ins julianische Datum umrechnen, dann die Anzahl addieren, und schließlich wieder zurück ins Kalenderdatum umrechnen.

pub fn shift_date(date: Date, days: f64) -> Date {
    let jd = julian_date(Calendar::Greg, date);
    let mut date = calendar_date(Calendar::Greg, jd + days);
    date.day = date.day.round();
    date
}

Wochentage

Zellers Kongruenz

Zellers Kongruenz ist eine Formel zur Ermittlung des Wochentags zu einem Datum.

static DAY_DE: [&str; 7] = [
    "Sa", "So", "Mo", "Di", "Mi", "Do", "Fr"
];

static DAY_EN: [&str; 7] = [
    "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"
];

pub struct Day(i32);

impl Day {
    pub fn de(self) -> &'static str {DAY_DE[self.0 as usize]}
    pub fn en(self) -> &'static str {DAY_EN[self.0 as usize]}
}

pub fn day_of_the_week(year: i32, month: i32, day: i32) -> Day {
    let j = year/100;
    let k = (year - if month>2 {0} else {1}) % 100;
    let m = month + if month>2 {0} else {12};
    let n = (day + (m+1)*13/5 + k + k/4 + j/4 - 2*j) % 7;
    Day(n)
}

Via julianisches Datum

Die Ermittlung des Wochentags ist auch über das julianische Datum möglich. Den abgerundeten Wert braucht man nur um +3 verschieben und modulo sieben nehmen.

pub fn day_of_the_week(year: i32, month: i32, day: i32) -> Day {
    let date = Date {year, month, day: f64::from(day)};
    let jd = julian_date(Calendar::Greg, date);
    Day((jd as i32 + 3) % 7)
}