root/src/time.rs

// time.rs
//
// Copyright 2021-2023 nee <nee-git@patchouli.garden>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: GPL-3.0-or-later

use chrono::prelude::*;

// a decimal second is 0.864 of a standard second
// 100 seconds = 1 minute
// 100 minutes = 1 hour
// 10 hours = 1 day
#[derive(PartialEq, Debug)]
pub struct DecimalTime {
    pub secs: i32,
    pub frac: i32, // Still in std nanoseconds
}

impl DecimalTime {
    pub fn hours(&self) -> i32 {
        self.secs / (100 * 100)
    }
    pub fn minutes(&self) -> i32 {
        (self.secs / 100) % 100
    }
    pub fn seconds(&self) -> i32 {
        self.secs % 100
    }
    pub fn num_seconds_from_midnight(&self) -> i32 {
        self.secs
    }
    pub fn standard_seconds(&self) -> i32 {
        (self.secs as f32 / SECOND_TO_DEC_FACTOR) as i32
    }
    pub fn standard_nanoseconds(&self) -> i32 {
        self.frac
    }
    pub fn fraction(&self) -> f32 {
        (self.secs as f32) / (10.0 * 100.0 * 100.0)
    }
}

pub const STD_SECONDS_IN_STD_DAY: u32 = 86400;
const DEC_SECONDS_IN_DECIMAL_DAY: u32 = 10 * 100 * 100;
const SECOND_TO_DEC_FACTOR: f32 = DEC_SECONDS_IN_DECIMAL_DAY as f32 / STD_SECONDS_IN_STD_DAY as f32;
const STD_NANOSECONDS_IN_STD_SECOND: u32 = 1000000000;
const STD_NANOSECONDS_IN_DEC_SECOND: u32 =
    (STD_NANOSECONDS_IN_STD_SECOND as f32 / SECOND_TO_DEC_FACTOR) as u32;
impl From<NaiveTime> for DecimalTime {
    fn from(v: NaiveTime) -> DecimalTime {
        let nano = v.nanosecond();
        if nano >= STD_NANOSECONDS_IN_DEC_SECOND {
            DecimalTime {
                secs: (v.num_seconds_from_midnight() as f32 * SECOND_TO_DEC_FACTOR) as i32 + 1,
                frac: (nano - STD_NANOSECONDS_IN_DEC_SECOND) as i32,
            }
        } else {
            DecimalTime {
                secs: (v.num_seconds_from_midnight() as f32 * SECOND_TO_DEC_FACTOR) as i32,
                frac: nano as i32,
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn from() {
        (|| {
            let date = NaiveTime::from_hms_opt(0, 0, 0)?;
            assert_eq!(DecimalTime { secs: 0, frac: 0 }, DecimalTime::from(date));

            let date = NaiveTime::from_hms_opt(12, 0, 0)?;
            assert_eq!(
                DecimalTime {
                    secs: 50000,
                    frac: 0
                },
                DecimalTime::from(date)
            );

            let date = NaiveTime::from_hms_opt(6, 0, 0)?;
            assert_eq!(
                DecimalTime {
                    secs: 25000,
                    frac: 0
                },
                DecimalTime::from(date)
            );

            let date = NaiveTime::from_hms_opt(18, 0, 0)?;
            assert_eq!(
                DecimalTime {
                    secs: 75000,
                    frac: 0
                },
                DecimalTime::from(date)
            );

            let date = NaiveTime::from_hms_opt(23, 59, 59)?;
            assert_eq!(
                DecimalTime {
                    secs: 99998,
                    frac: 0
                },
                DecimalTime::from(date)
            );

            let date = NaiveTime::from_hms_nano_opt(23, 59, 59, STD_NANOSECONDS_IN_DEC_SECOND - 1)?;
            assert_eq!(
                DecimalTime {
                    secs: 99998,
                    frac: 863999999
                },
                DecimalTime::from(date)
            );

            let date = NaiveTime::from_hms_nano_opt(23, 59, 59, STD_NANOSECONDS_IN_DEC_SECOND)?;
            assert_eq!(
                DecimalTime {
                    secs: 99999,
                    frac: 0
                },
                DecimalTime::from(date)
            );

            let date = NaiveTime::from_hms_nano_opt(23, 59, 59, STD_NANOSECONDS_IN_DEC_SECOND + 1)?;
            assert_eq!(
                DecimalTime {
                    secs: 99999,
                    frac: 1
                },
                DecimalTime::from(date)
            );

            let date = NaiveTime::from_hms_nano_opt(23, 59, 59, STD_NANOSECONDS_IN_STD_SECOND - 1)?;
            assert_eq!(
                DecimalTime {
                    secs: 99999,
                    frac: 135999999
                },
                DecimalTime::from(date)
            );

            Some(())
        })();
    }
}