common_time/
util.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::str::FromStr;
16
17use chrono::{LocalResult, NaiveDateTime, TimeZone};
18use chrono_tz::Tz;
19
20use crate::Timezone;
21use crate::timezone::get_timezone;
22
23pub fn format_utc_datetime(utc: &NaiveDateTime, pattern: &str) -> String {
24    match get_timezone(None) {
25        crate::Timezone::Offset(offset) => {
26            offset.from_utc_datetime(utc).format(pattern).to_string()
27        }
28        crate::Timezone::Named(tz) => tz.from_utc_datetime(utc).format(pattern).to_string(),
29    }
30}
31
32/// Cast a [`NaiveDateTime`] with the given timezone.
33pub fn datetime_to_utc(
34    datetime: &NaiveDateTime,
35    timezone: &Timezone,
36) -> LocalResult<NaiveDateTime> {
37    match timezone {
38        crate::Timezone::Offset(offset) => {
39            offset.from_local_datetime(datetime).map(|x| x.naive_utc())
40        }
41        crate::Timezone::Named(tz) => tz.from_local_datetime(datetime).map(|x| x.naive_utc()),
42    }
43}
44
45pub fn find_tz_from_env() -> Option<Tz> {
46    // Windows does not support "TZ" env variable, which is used in the `Local` timezone under Unix.
47    // However, we are used to set "TZ" env as the default timezone without actually providing a
48    // timezone argument (especially in tests), and it's very convenient to do so, we decide to make
49    // it work under Windows as well.
50    std::env::var("TZ")
51        .ok()
52        .and_then(|tz| Tz::from_str(&tz).ok())
53}
54
55/// A trait for types that provide the current system time.
56pub trait SystemTimer {
57    /// Returns the time duration since UNIX_EPOCH in milliseconds.
58    fn current_time_millis(&self) -> i64;
59
60    /// Returns the current time in rfc3339 format.
61    fn current_time_rfc3339(&self) -> String;
62}
63
64/// Default implementation of [`SystemTimer`]
65#[derive(Debug, Default, Clone, Copy)]
66pub struct DefaultSystemTimer;
67
68impl SystemTimer for DefaultSystemTimer {
69    fn current_time_millis(&self) -> i64 {
70        current_time_millis()
71    }
72
73    fn current_time_rfc3339(&self) -> String {
74        current_time_rfc3339()
75    }
76}
77
78/// Returns the time duration since UNIX_EPOCH in milliseconds.
79pub fn current_time_millis() -> i64 {
80    chrono::Utc::now().timestamp_millis()
81}
82
83/// Returns the current time in rfc3339 format.
84pub fn current_time_rfc3339() -> String {
85    chrono::Utc::now().to_rfc3339()
86}
87
88/// Returns the yesterday time in rfc3339 format.
89pub fn yesterday_rfc3339() -> String {
90    let now = chrono::Utc::now();
91    let day_before = now
92        - chrono::Duration::try_days(1).unwrap_or_else(|| {
93            panic!("now time ('{now}') is too early to calculate the day before")
94        });
95    day_before.to_rfc3339()
96}
97
98/// Port of rust unstable features `int_roundings`.
99pub(crate) fn div_ceil(this: i64, rhs: i64) -> i64 {
100    let d = this / rhs;
101    let r = this % rhs;
102    if r > 0 && rhs > 0 { d + 1 } else { d }
103}
104
105/// Formats nanoseconds into human-readable time with dynamic unit selection.
106///
107/// This function automatically chooses the most appropriate unit (seconds, milliseconds,
108/// microseconds, or nanoseconds) to display the time in a readable format.
109///
110/// # Examples
111///
112/// ```
113/// use common_time::util::format_nanoseconds_human_readable;
114///
115/// assert_eq!("1.23s", format_nanoseconds_human_readable(1_234_567_890));
116/// assert_eq!("456ms", format_nanoseconds_human_readable(456_000_000));
117/// assert_eq!("789us", format_nanoseconds_human_readable(789_000));
118/// assert_eq!("123ns", format_nanoseconds_human_readable(123));
119/// ```
120pub fn format_nanoseconds_human_readable(nanos: usize) -> String {
121    if nanos == 0 {
122        return "0ns".to_string();
123    }
124
125    let nanos_i64 = nanos as i64;
126
127    // Try seconds first (if >= 1 second)
128    if nanos_i64 >= 1_000_000_000 {
129        let secs = nanos_i64 as f64 / 1_000_000_000.0;
130        if secs >= 10.0 {
131            return format!("{:.1}s", secs);
132        } else {
133            return format!("{:.2}s", secs);
134        }
135    }
136
137    // Try milliseconds (if >= 1 millisecond)
138    if nanos_i64 >= 1_000_000 {
139        let millis = nanos_i64 as f64 / 1_000_000.0;
140        if millis >= 10.0 {
141            return format!("{:.0}ms", millis);
142        } else {
143            return format!("{:.1}ms", millis);
144        }
145    }
146
147    // Try microseconds (if >= 1 microsecond)
148    if nanos_i64 >= 1_000 {
149        let micros = nanos_i64 as f64 / 1_000.0;
150        if micros >= 10.0 {
151            return format!("{:.0}us", micros);
152        } else {
153            return format!("{:.1}us", micros);
154        }
155    }
156
157    // Less than 1 microsecond, display as nanoseconds
158    format!("{}ns", nanos_i64)
159}
160
161#[cfg(test)]
162mod tests {
163    use std::time::{self, SystemTime};
164
165    use chrono::{Datelike, TimeZone, Timelike};
166
167    use super::*;
168
169    #[test]
170    fn test_current_time_millis() {
171        let now = current_time_millis();
172
173        let millis_from_std = SystemTime::now()
174            .duration_since(time::UNIX_EPOCH)
175            .unwrap()
176            .as_millis() as i64;
177        let datetime_now = chrono::Utc.timestamp_millis_opt(now).unwrap();
178        let datetime_std = chrono::Utc.timestamp_millis_opt(millis_from_std).unwrap();
179
180        assert_eq!(datetime_std.year(), datetime_now.year());
181        assert_eq!(datetime_std.month(), datetime_now.month());
182        assert_eq!(datetime_std.day(), datetime_now.day());
183        assert_eq!(datetime_std.hour(), datetime_now.hour());
184        assert_eq!(datetime_std.minute(), datetime_now.minute());
185    }
186
187    #[test]
188    fn test_div_ceil() {
189        let v0 = 9223372036854676001;
190        assert_eq!(9223372036854677, div_ceil(v0, 1000));
191    }
192
193    #[test]
194    fn test_format_nanoseconds_human_readable() {
195        // Test zero
196        assert_eq!("0ns", format_nanoseconds_human_readable(0));
197
198        // Test nanoseconds (< 1 microsecond)
199        assert_eq!("1ns", format_nanoseconds_human_readable(1));
200        assert_eq!("123ns", format_nanoseconds_human_readable(123));
201        assert_eq!("999ns", format_nanoseconds_human_readable(999));
202
203        // Test microseconds (>= 1 microsecond, < 1 millisecond)
204        assert_eq!("1.0us", format_nanoseconds_human_readable(1_000));
205        assert_eq!("1.5us", format_nanoseconds_human_readable(1_500));
206        assert_eq!("10us", format_nanoseconds_human_readable(10_000));
207        assert_eq!("123us", format_nanoseconds_human_readable(123_000));
208        assert_eq!("999us", format_nanoseconds_human_readable(999_000));
209
210        // Test milliseconds (>= 1 millisecond, < 1 second)
211        assert_eq!("1.0ms", format_nanoseconds_human_readable(1_000_000));
212        assert_eq!("1.5ms", format_nanoseconds_human_readable(1_500_000));
213        assert_eq!("10ms", format_nanoseconds_human_readable(10_000_000));
214        assert_eq!("123ms", format_nanoseconds_human_readable(123_000_000));
215        assert_eq!("999ms", format_nanoseconds_human_readable(999_000_000));
216
217        // Test seconds (>= 1 second)
218        assert_eq!("1.00s", format_nanoseconds_human_readable(1_000_000_000));
219        assert_eq!("1.23s", format_nanoseconds_human_readable(1_234_567_890));
220        assert_eq!("10.0s", format_nanoseconds_human_readable(10_000_000_000));
221        assert_eq!("123.5s", format_nanoseconds_human_readable(123_456_789_012));
222
223        // Test large values
224        assert_eq!(
225            "1234.6s",
226            format_nanoseconds_human_readable(1_234_567_890_123)
227        );
228    }
229}