1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//!
//! `time` module provides re-export of WASM32-compatible `Instant` and provides
//! platform neutral implementations for [`unixtime_as_millis_u128()`] and
//! [`unixtime_as_millis_f64()`].
//!

use cfg_if::cfg_if;

/// re-export of [`instant`] crate supporting native and WASM implementations
pub use instant::*;

pub const SECONDS: u64 = 1000;
pub const MINUTES: u64 = SECONDS * 60;
pub const HOURS: u64 = MINUTES * 60;
pub const DAYS: u64 = HOURS * 24;

pub enum TimeFormat {
    Time24,
    Time12,
    Locale,
    Custom(String),
}

cfg_if! {
    if #[cfg(target_arch = "wasm32")] {
        use js_sys::{Date,Intl,Reflect};
        use wasm_bindgen::prelude::JsValue;

        #[inline(always)]
        pub fn unixtime_as_millis_u128() -> u128 {
            Date::now() as u128
        }

        #[inline(always)]
        pub fn unixtime_as_millis_f64() -> f64 {
            Date::now()
        }

        #[inline(always)]
        pub fn unixtime_as_millis_u64() -> u64 {
            Date::now() as u64
        }

        #[inline(always)]
        pub fn unixtime_to_locale_string(unixtime : u64) -> String {
            let date = Date::new(&JsValue::from(unixtime as f64));
            date.to_locale_string(default_locale().as_str(), &JsValue::UNDEFINED).as_string().unwrap()
        }

        fn default_locale() -> String {
            static mut LOCALE: Option<String> = None;
            unsafe {
                LOCALE.get_or_insert_with(|| {
                    let date_time_format = Intl::DateTimeFormat::default();
                    let resolved_options = date_time_format.resolved_options();
                    let locale = Reflect::get(&resolved_options, &JsValue::from("locale")).expect("Intl::DateTimeFormat().resolvedOptions().locale is not defined");
                    locale.as_string().expect("Intl::DateTimeFormat().resolvedOptions().locale()")
                }).clone()
            }
        }

        pub fn init_desired_time_format(_time_format : TimeFormat) {
            // time format is ignored in WASM and
            // the browser's locale is used instead
        }

    } else {
        use chrono::{Local, TimeZone};

        #[inline(always)]
        pub fn unixtime_as_millis_u128() -> u128 {
            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).expect("unixtime_as_millis_u64").as_millis()
        }

        #[inline(always)]
        pub fn unixtime_as_millis_f64() -> f64 {
            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).expect("unixtime_as_millis_u64").as_millis() as f64
        }

        #[inline(always)]
        pub fn unixtime_as_millis_u64() -> u64 {
            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).expect("unixtime_as_millis_u64").as_millis() as u64
        }

        static mut TIME_FORMAT: Option<String> = None;

        #[inline(always)]
        fn time_format() -> &'static str {
            unsafe {
                TIME_FORMAT.get_or_insert_with(|| {
                    "%Y-%m-%d %H:%M:%S".to_string()
                }).as_str()
            }
        }

        pub fn init_desired_time_format(time_format : TimeFormat) {
            unsafe {
                match time_format {
                    TimeFormat::Time24 => {
                        TIME_FORMAT = Some("%Y-%m-%d %H:%M:%S".to_string());
                    },
                    TimeFormat::Time12 => {
                        TIME_FORMAT = Some("%Y-%m-%d %I:%M:%S %p".to_string());
                    },
                    TimeFormat::Locale => {
                        TIME_FORMAT = Some("%c".to_string());
                    },
                    TimeFormat::Custom(format) => {
                        TIME_FORMAT = Some(format);
                    }
                }
            }
        }

        #[inline(always)]
        pub fn unixtime_to_locale_string(unixtime : u64) -> String {
            let local = Local.timestamp_millis_opt(unixtime as i64).unwrap();
            local.format(time_format()).to_string()
        }
    }
}

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

    #[test]
    fn test_unixtime_to_locale_string() {
        let now = unixtime_as_millis_u64();
        let locale_string = unixtime_to_locale_string(now);
        println!("locale_string: {}", locale_string);
    }
}
*/