Skip to main content

common_time/
timestamp.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 core::default::Default;
16use std::cmp::Ordering;
17use std::fmt::{self, Display, Formatter, Write};
18use std::hash::{Hash, Hasher};
19use std::time::Duration;
20
21use arrow::datatypes::TimeUnit as ArrowTimeUnit;
22use chrono::{
23    DateTime, Days, LocalResult, Months, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeDelta,
24    TimeZone as ChronoTimeZone, Utc,
25};
26use serde::{Deserialize, Serialize};
27use snafu::{OptionExt, ResultExt};
28
29use crate::error;
30use crate::error::{ArithmeticOverflowSnafu, ParseTimestampSnafu, Result, TimestampOverflowSnafu};
31use crate::interval::{IntervalDayTime, IntervalMonthDayNano, IntervalYearMonth};
32use crate::timezone::{Timezone, get_timezone};
33use crate::util::{datetime_to_utc, div_ceil};
34
35/// Timestamp represents the value of units(seconds/milliseconds/microseconds/nanoseconds) elapsed
36/// since UNIX epoch. The valid value range of [Timestamp] depends on it's unit (all in UTC timezone):
37/// - for [TimeUnit::Second]: [-262144-01-01 00:00:00, +262143-12-31 23:59:59]
38/// - for [TimeUnit::Millisecond]: [-262144-01-01 00:00:00.000, +262143-12-31 23:59:59.999]
39/// - for [TimeUnit::Microsecond]: [-262144-01-01 00:00:00.000000, +262143-12-31 23:59:59.999999]
40/// - for [TimeUnit::Nanosecond]: [1677-09-21 00:12:43.145224192, 2262-04-11 23:47:16.854775807]
41///
42/// # Note:
43/// For values out of range, you can still store these timestamps, but while performing arithmetic
44/// or formatting operations, it will return an error or just overflow.
45#[derive(Clone, Default, Copy, Serialize, Deserialize)]
46pub struct Timestamp {
47    value: i64,
48    unit: TimeUnit,
49}
50
51impl Timestamp {
52    /// Creates current timestamp in millisecond.
53    pub fn current_millis() -> Self {
54        Self {
55            value: crate::util::current_time_millis(),
56            unit: TimeUnit::Millisecond,
57        }
58    }
59
60    /// Creates current timestamp in specific time `unit`.
61    pub fn current_time(unit: TimeUnit) -> Timestamp {
62        let now = chrono::Utc::now();
63        let value = match unit {
64            TimeUnit::Second => now.timestamp(),
65            TimeUnit::Millisecond => now.timestamp_millis(),
66            TimeUnit::Microsecond => now.timestamp_micros(),
67            TimeUnit::Nanosecond => now.timestamp_nanos_opt().unwrap_or_default(),
68        };
69        Timestamp { value, unit }
70    }
71
72    /// Subtracts a duration from timestamp.
73    /// # Note
74    /// The result time unit remains unchanged even if `duration` has a different unit with `self`.
75    /// For example, a timestamp with value 1 and time unit second, subtracted by 1 millisecond
76    /// and the result is still 1 second.
77    pub fn sub_duration(&self, duration: Duration) -> error::Result<Self> {
78        let duration: i64 = match self.unit {
79            TimeUnit::Second => {
80                i64::try_from(duration.as_secs()).context(TimestampOverflowSnafu)?
81            }
82            TimeUnit::Millisecond => {
83                i64::try_from(duration.as_millis()).context(TimestampOverflowSnafu)?
84            }
85            TimeUnit::Microsecond => {
86                i64::try_from(duration.as_micros()).context(TimestampOverflowSnafu)?
87            }
88            TimeUnit::Nanosecond => {
89                i64::try_from(duration.as_nanos()).context(TimestampOverflowSnafu)?
90            }
91        };
92
93        let value = self
94            .value
95            .checked_sub(duration)
96            .with_context(|| ArithmeticOverflowSnafu {
97                msg: format!(
98                    "Try to subtract timestamp: {:?} with duration: {:?}",
99                    self, duration
100                ),
101            })?;
102        Ok(Timestamp {
103            value,
104            unit: self.unit,
105        })
106    }
107
108    /// Adds a duration to timestamp.
109    /// # Note
110    /// The result time unit remains unchanged even if `duration` has a different unit with `self`.
111    /// For example, a timestamp with value 1 and time unit second, subtracted by 1 millisecond
112    /// and the result is still 1 second.
113    pub fn add_duration(&self, duration: Duration) -> error::Result<Self> {
114        let duration: i64 = match self.unit {
115            TimeUnit::Second => {
116                i64::try_from(duration.as_secs()).context(TimestampOverflowSnafu)?
117            }
118            TimeUnit::Millisecond => {
119                i64::try_from(duration.as_millis()).context(TimestampOverflowSnafu)?
120            }
121            TimeUnit::Microsecond => {
122                i64::try_from(duration.as_micros()).context(TimestampOverflowSnafu)?
123            }
124            TimeUnit::Nanosecond => {
125                i64::try_from(duration.as_nanos()).context(TimestampOverflowSnafu)?
126            }
127        };
128
129        let value = self
130            .value
131            .checked_add(duration)
132            .with_context(|| ArithmeticOverflowSnafu {
133                msg: format!(
134                    "Try to add timestamp: {:?} with duration: {:?}",
135                    self, duration
136                ),
137            })?;
138        Ok(Timestamp {
139            value,
140            unit: self.unit,
141        })
142    }
143
144    // FIXME(yingwen): remove add/sub intervals later
145    /// Adds given [IntervalYearMonth] to the current timestamp.
146    pub fn add_year_month(&self, interval: IntervalYearMonth) -> Option<Timestamp> {
147        let naive_datetime = self.to_chrono_datetime()?;
148
149        let naive_datetime =
150            naive_datetime.checked_add_months(Months::new(interval.months as u32))?;
151
152        // Have to convert the new timestamp by the current unit.
153        Timestamp::from_chrono_datetime(naive_datetime).and_then(|ts| ts.convert_to(self.unit))
154    }
155
156    /// Adds given [IntervalDayTime] to the current timestamp.
157    pub fn add_day_time(&self, interval: IntervalDayTime) -> Option<Timestamp> {
158        let naive_datetime = self.to_chrono_datetime()?;
159
160        let naive_datetime = naive_datetime
161            .checked_add_days(Days::new(interval.days as u64))?
162            .checked_add_signed(TimeDelta::milliseconds(interval.milliseconds as i64))?;
163
164        // Have to convert the new timestamp by the current unit.
165        Timestamp::from_chrono_datetime(naive_datetime).and_then(|ts| ts.convert_to(self.unit))
166    }
167
168    /// Adds given [IntervalMonthDayNano] to the current timestamp.
169    pub fn add_month_day_nano(&self, interval: IntervalMonthDayNano) -> Option<Timestamp> {
170        let naive_datetime = self.to_chrono_datetime()?;
171
172        let naive_datetime = naive_datetime
173            .checked_add_months(Months::new(interval.months as u32))?
174            .checked_add_days(Days::new(interval.days as u64))?
175            .checked_add_signed(TimeDelta::nanoseconds(interval.nanoseconds))?;
176
177        // Have to convert the new timestamp by the current unit.
178        Timestamp::from_chrono_datetime(naive_datetime).and_then(|ts| ts.convert_to(self.unit))
179    }
180
181    /// Subtracts given [IntervalYearMonth] to the current timestamp.
182    pub fn sub_year_month(&self, interval: IntervalYearMonth) -> Option<Timestamp> {
183        let naive_datetime = self.to_chrono_datetime()?;
184
185        let naive_datetime =
186            naive_datetime.checked_sub_months(Months::new(interval.months as u32))?;
187
188        // Have to convert the new timestamp by the current unit.
189        Timestamp::from_chrono_datetime(naive_datetime).and_then(|ts| ts.convert_to(self.unit))
190    }
191
192    /// Subtracts given [IntervalDayTime] to the current timestamp.
193    pub fn sub_day_time(&self, interval: IntervalDayTime) -> Option<Timestamp> {
194        let naive_datetime = self.to_chrono_datetime()?;
195
196        let naive_datetime = naive_datetime
197            .checked_sub_days(Days::new(interval.days as u64))?
198            .checked_sub_signed(TimeDelta::milliseconds(interval.milliseconds as i64))?;
199
200        // Have to convert the new timestamp by the current unit.
201        Timestamp::from_chrono_datetime(naive_datetime).and_then(|ts| ts.convert_to(self.unit))
202    }
203
204    /// Subtracts given [IntervalMonthDayNano] to the current timestamp.
205    pub fn sub_month_day_nano(&self, interval: IntervalMonthDayNano) -> Option<Timestamp> {
206        let naive_datetime = self.to_chrono_datetime()?;
207
208        let naive_datetime = naive_datetime
209            .checked_sub_months(Months::new(interval.months as u32))?
210            .checked_sub_days(Days::new(interval.days as u64))?
211            .checked_sub_signed(TimeDelta::nanoseconds(interval.nanoseconds))?;
212
213        // Have to convert the new timestamp by the current unit.
214        Timestamp::from_chrono_datetime(naive_datetime).and_then(|ts| ts.convert_to(self.unit))
215    }
216
217    /// Subtracts current timestamp with another timestamp, yielding a duration.
218    pub fn sub(&self, rhs: &Self) -> Option<chrono::Duration> {
219        let lhs = self.to_chrono_datetime()?;
220        let rhs = rhs.to_chrono_datetime()?;
221        Some(lhs - rhs)
222    }
223
224    pub fn new(value: i64, unit: TimeUnit) -> Self {
225        Self { unit, value }
226    }
227
228    pub const fn new_second(value: i64) -> Self {
229        Self {
230            value,
231            unit: TimeUnit::Second,
232        }
233    }
234
235    pub const fn new_millisecond(value: i64) -> Self {
236        Self {
237            value,
238            unit: TimeUnit::Millisecond,
239        }
240    }
241
242    pub const fn new_microsecond(value: i64) -> Self {
243        Self {
244            value,
245            unit: TimeUnit::Microsecond,
246        }
247    }
248
249    pub const fn new_nanosecond(value: i64) -> Self {
250        Self {
251            value,
252            unit: TimeUnit::Nanosecond,
253        }
254    }
255
256    pub fn unit(&self) -> TimeUnit {
257        self.unit
258    }
259
260    pub fn value(&self) -> i64 {
261        self.value
262    }
263
264    /// Convert a timestamp to given time unit.
265    /// Conversion from a timestamp with smaller unit to a larger unit may cause rounding error.
266    /// Return `None` if conversion causes overflow.
267    pub fn convert_to(&self, unit: TimeUnit) -> Option<Timestamp> {
268        if self.unit().factor() >= unit.factor() {
269            let mul = self.unit().factor() / unit.factor();
270            let value = self.value.checked_mul(mul as i64)?;
271            Some(Timestamp::new(value, unit))
272        } else {
273            let mul = unit.factor() / self.unit().factor();
274            Some(Timestamp::new(self.value.div_euclid(mul as i64), unit))
275        }
276    }
277
278    /// Convert a timestamp to given time unit.
279    /// Conversion from a timestamp with smaller unit to a larger unit will round the value
280    /// to ceil (positive infinity).
281    /// Return `None` if conversion causes overflow.
282    pub fn convert_to_ceil(&self, unit: TimeUnit) -> Option<Timestamp> {
283        if self.unit().factor() >= unit.factor() {
284            let mul = self.unit().factor() / unit.factor();
285            let value = self.value.checked_mul(mul as i64)?;
286            Some(Timestamp::new(value, unit))
287        } else {
288            let mul = unit.factor() / self.unit().factor();
289            Some(Timestamp::new(div_ceil(self.value, mul as i64), unit))
290        }
291    }
292
293    /// Split a [Timestamp] into seconds part and nanoseconds part.
294    /// Notice the seconds part of split result is always rounded down to floor.
295    pub fn split(&self) -> (i64, u32) {
296        let sec_mul = (TimeUnit::Second.factor() / self.unit.factor()) as i64;
297        let nsec_mul = (self.unit.factor() / TimeUnit::Nanosecond.factor()) as i64;
298
299        let sec_div = self.value.div_euclid(sec_mul);
300        let sec_mod = self.value.rem_euclid(sec_mul);
301        // safety:  the max possible value of `sec_mod` is 999,999,999
302        let nsec = u32::try_from(sec_mod * nsec_mul).unwrap();
303        (sec_div, nsec)
304    }
305
306    /// Creates a new Timestamp instance from seconds and nanoseconds parts.
307    /// Returns None if overflow.
308    fn from_splits(sec: i64, nsec: u32) -> Option<Self> {
309        if nsec == 0 {
310            Some(Timestamp::new_second(sec))
311        } else if nsec.is_multiple_of(1_000_000) {
312            let millis = nsec / 1_000_000;
313            sec.checked_mul(1000)
314                .and_then(|v| v.checked_add(millis as i64))
315                .map(Timestamp::new_millisecond)
316        } else if nsec.is_multiple_of(1_000) {
317            let micros = nsec / 1000;
318            sec.checked_mul(1_000_000)
319                .and_then(|v| v.checked_add(micros as i64))
320                .map(Timestamp::new_microsecond)
321        } else {
322            // Refer to <https://github.com/chronotope/chrono/issues/1289>
323            //
324            // subsec nanos are always non-negative, however the timestamp itself (both in seconds and in nanos) can be
325            // negative. Now i64::MIN is NOT dividable by 1_000_000_000, so
326            //
327            //   (sec * 1_000_000_000) + nsec
328            //
329            // may underflow (even when in theory we COULD represent the datetime as i64) because we add the non-negative
330            // nanos AFTER the multiplication. This is fixed by converting the negative case to
331            //
332            //   ((sec + 1) * 1_000_000_000) + (nsec - 1_000_000_000)
333            let mut sec = sec;
334            let mut nsec = nsec as i64;
335            if sec < 0 && nsec > 0 {
336                nsec -= 1_000_000_000;
337                sec += 1;
338            }
339
340            sec.checked_mul(1_000_000_000)
341                .and_then(|v| v.checked_add(nsec))
342                .map(Timestamp::new_nanosecond)
343        }
344    }
345
346    /// Format timestamp to ISO8601 string. If the timestamp exceeds what chrono timestamp can
347    /// represent, this function simply print the timestamp unit and value in plain string.
348    pub fn to_iso8601_string(&self) -> String {
349        // Safety: the format is valid
350        self.as_formatted_string("%Y-%m-%d %H:%M:%S%.f%z", None)
351            .unwrap()
352    }
353
354    /// Format timestamp use **system timezone**.
355    pub fn to_local_string(&self) -> String {
356        // Safety: the format is valid
357        self.as_formatted_string("%Y-%m-%d %H:%M:%S%.f", None)
358            .unwrap()
359    }
360
361    /// Format timestamp for given timezone.
362    /// If `tz==None`, the server default timezone will used.
363    pub fn to_timezone_aware_string(&self, tz: Option<&Timezone>) -> String {
364        // Safety: the format is valid
365        self.as_formatted_string("%Y-%m-%d %H:%M:%S%.f", tz)
366            .unwrap()
367    }
368
369    /// Format timestamp for given format and timezone.
370    /// If `tz==None`, the server default timezone will used.
371    pub fn as_formatted_string(self, pattern: &str, timezone: Option<&Timezone>) -> Result<String> {
372        if let Some(v) = self.to_chrono_datetime() {
373            let mut formatted = String::new();
374
375            match get_timezone(timezone) {
376                Timezone::Offset(offset) => {
377                    write!(
378                        formatted,
379                        "{}",
380                        offset.from_utc_datetime(&v).format(pattern)
381                    )
382                    .context(crate::error::FormatSnafu { pattern })?;
383                }
384                Timezone::Named(tz) => {
385                    write!(formatted, "{}", tz.from_utc_datetime(&v).format(pattern))
386                        .context(crate::error::FormatSnafu { pattern })?;
387                }
388            }
389
390            Ok(formatted)
391        } else {
392            Ok(format!("[Timestamp{}: {}]", self.unit, self.value))
393        }
394    }
395
396    pub fn to_chrono_datetime(&self) -> Option<NaiveDateTime> {
397        let (sec, nsec) = self.split();
398        chrono::DateTime::from_timestamp(sec, nsec).map(|x| x.naive_utc())
399    }
400
401    pub fn to_chrono_datetime_with_timezone(&self, tz: Option<&Timezone>) -> Option<NaiveDateTime> {
402        let utc = self.to_chrono_datetime()?;
403        match tz {
404            None => Some(utc),
405            Some(Timezone::Offset(offset)) => utc.checked_add_offset(*offset),
406            Some(Timezone::Named(tz)) => {
407                let offset = tz.offset_from_utc_datetime(&utc).fix();
408                utc.checked_add_offset(offset)
409            }
410        }
411    }
412
413    /// Convert timestamp to chrono date.
414    pub fn to_chrono_date(&self) -> Option<NaiveDate> {
415        self.to_chrono_datetime().map(|ndt| ndt.date())
416    }
417
418    /// Convert timestamp to chrono time.
419    pub fn to_chrono_time(&self) -> Option<NaiveTime> {
420        self.to_chrono_datetime().map(|ndt| ndt.time())
421    }
422
423    pub fn from_chrono_datetime(ndt: NaiveDateTime) -> Option<Self> {
424        let sec = ndt.and_utc().timestamp();
425        let nsec = ndt.and_utc().timestamp_subsec_nanos();
426        Timestamp::from_splits(sec, nsec)
427    }
428
429    pub fn from_chrono_date(date: NaiveDate) -> Option<Self> {
430        Timestamp::from_chrono_datetime(date.and_time(NaiveTime::default()))
431    }
432
433    /// Accepts a string in RFC3339 / ISO8601 standard format and some variants and converts it to a nanosecond precision timestamp.
434    /// It no timezone specified in string, it cast to nanosecond epoch timestamp in UTC.
435    pub fn from_str_utc(s: &str) -> Result<Self> {
436        Self::from_str(s, None)
437    }
438
439    /// Accepts a string in RFC3339 / ISO8601 standard format and some variants and converts it to a nanosecond precision timestamp.
440    /// This code is copied from [arrow-datafusion](https://github.com/apache/arrow-datafusion/blob/arrow2/datafusion-physical-expr/src/arrow_temporal_util.rs#L71)
441    /// with some bugfixes.
442    /// Supported format:
443    /// - `2022-09-20T14:16:43.012345Z` (Zulu timezone)
444    /// - `2022-09-20T14:16:43.012345+08:00` (Explicit offset)
445    /// - `2022-09-20T14:16:43.012345` (The given timezone, with T)
446    /// - `2022-09-20T14:16:43` (Zulu timezone, no fractional seconds, with T)
447    /// - `2022-09-20 14:16:43.012345Z` (Zulu timezone, without T)
448    /// - `2022-09-20 14:16:43` (The given timezone, without T)
449    /// - `2022-09-20 14:16:43.012345` (The given timezone, without T)
450    #[allow(deprecated)]
451    pub fn from_str(s: &str, timezone: Option<&Timezone>) -> Result<Self> {
452        // RFC3339 timestamp (with a T)
453        let s = s.trim();
454        if let Ok(ts) = DateTime::parse_from_rfc3339(s) {
455            return Timestamp::from_chrono_datetime(ts.naive_utc())
456                .context(ParseTimestampSnafu { raw: s });
457        }
458        if let Ok(ts) = DateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S%.f%:z") {
459            return Timestamp::from_chrono_datetime(ts.naive_utc())
460                .context(ParseTimestampSnafu { raw: s });
461        }
462        if let Ok(ts) = chrono::Utc.datetime_from_str(s, "%Y-%m-%d %H:%M:%S%.fZ") {
463            return Timestamp::from_chrono_datetime(ts.naive_utc())
464                .context(ParseTimestampSnafu { raw: s });
465        }
466
467        if let Ok(ts) = NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S") {
468            return naive_datetime_to_timestamp(s, ts, timezone);
469        }
470
471        if let Ok(ts) = NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f") {
472            return naive_datetime_to_timestamp(s, ts, timezone);
473        }
474
475        if let Ok(ts) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
476            return naive_datetime_to_timestamp(s, ts, timezone);
477        }
478
479        if let Ok(ts) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S%.f") {
480            return naive_datetime_to_timestamp(s, ts, timezone);
481        }
482
483        ParseTimestampSnafu { raw: s }.fail()
484    }
485
486    /// Interprets a timezone-less [`NaiveDateTime`] in the given timezone and
487    /// returns the corresponding timestamp.
488    ///
489    /// Datetimes that fall into a DST gap (a local time that does not exist)
490    /// are rejected, while ambiguous datetimes (from a repeated local time)
491    /// are resolved to the earlier instant. This policy is shared with
492    /// [`Timestamp::from_str`] so that the text and binary protocols interpret
493    /// datetimes consistently.
494    pub fn from_naive_datetime(
495        datetime: NaiveDateTime,
496        timezone: &Timezone,
497    ) -> crate::error::Result<Timestamp> {
498        match datetime_to_utc(&datetime, timezone) {
499            LocalResult::Single(utc) | LocalResult::Ambiguous(utc, _) => {
500                Timestamp::from_chrono_datetime(utc).context(ParseTimestampSnafu {
501                    raw: format!("{datetime} (timezone {timezone})"),
502                })
503            }
504            LocalResult::None => ParseTimestampSnafu {
505                raw: format!("{datetime} (timezone {timezone})"),
506            }
507            .fail(),
508        }
509    }
510
511    pub fn negative(mut self) -> Self {
512        self.value = -self.value;
513        self
514    }
515
516    pub fn checked_negative(mut self) -> Option<Self> {
517        self.value = self.value.checked_neg()?;
518        Some(self)
519    }
520}
521
522impl Timestamp {
523    pub const MIN_SECOND: Self = Self::new_second(-8_334_601_228_800);
524    pub const MAX_SECOND: Self = Self::new_second(8_210_266_876_799);
525
526    pub const MIN_MILLISECOND: Self = Self::new_millisecond(-8_334_601_228_800_000);
527    pub const MAX_MILLISECOND: Self = Self::new_millisecond(8_210_266_876_799_999);
528
529    pub const MIN_MICROSECOND: Self = Self::new_microsecond(-8_334_601_228_800_000_000);
530    pub const MAX_MICROSECOND: Self = Self::new_microsecond(8_210_266_876_799_999_999);
531
532    pub const MIN_NANOSECOND: Self = Self::new_nanosecond(i64::MIN);
533    pub const MAX_NANOSECOND: Self = Self::new_nanosecond(i64::MAX);
534
535    /// Checks if a value would overflow for the given time unit.
536    pub fn is_overflow(value: i64, unit: TimeUnit) -> bool {
537        let (min_val, max_val) = match unit {
538            TimeUnit::Second => (Self::MIN_SECOND.value(), Self::MAX_SECOND.value()),
539            TimeUnit::Millisecond => (Self::MIN_MILLISECOND.value(), Self::MAX_MILLISECOND.value()),
540            TimeUnit::Microsecond => (Self::MIN_MICROSECOND.value(), Self::MAX_MICROSECOND.value()),
541            TimeUnit::Nanosecond => (Self::MIN_NANOSECOND.value(), Self::MAX_NANOSECOND.value()),
542        };
543        value < min_val || value > max_val
544    }
545}
546
547/// Converts the naive datetime (which has no specific timezone) to a
548/// nanosecond epoch timestamp in UTC.
549fn naive_datetime_to_timestamp(
550    s: &str,
551    datetime: NaiveDateTime,
552    timezone: Option<&Timezone>,
553) -> crate::error::Result<Timestamp> {
554    let Some(timezone) = timezone else {
555        return Timestamp::from_chrono_datetime(Utc.from_utc_datetime(&datetime).naive_utc())
556            .context(ParseTimestampSnafu { raw: s });
557    };
558
559    Timestamp::from_naive_datetime(datetime, timezone)
560        .map_err(|_| ParseTimestampSnafu { raw: s }.build())
561}
562
563impl From<i64> for Timestamp {
564    fn from(v: i64) -> Self {
565        Self {
566            value: v,
567            unit: TimeUnit::Millisecond,
568        }
569    }
570}
571
572impl From<Timestamp> for i64 {
573    fn from(t: Timestamp) -> Self {
574        t.value
575    }
576}
577
578impl From<Timestamp> for serde_json::Value {
579    fn from(d: Timestamp) -> Self {
580        serde_json::Value::String(d.to_iso8601_string())
581    }
582}
583
584impl fmt::Debug for Timestamp {
585    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
586        write!(f, "{}::{}", self.value, self.unit)
587    }
588}
589
590#[derive(
591    Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
592)]
593pub enum TimeUnit {
594    // The declaration order (Second < Millisecond < Microsecond < Nanosecond) is
595    // the precision order; the derived ordering intentionally reflects it.
596    Second,
597    #[default]
598    Millisecond,
599    Microsecond,
600    Nanosecond,
601}
602
603impl From<&ArrowTimeUnit> for TimeUnit {
604    fn from(unit: &ArrowTimeUnit) -> Self {
605        match unit {
606            ArrowTimeUnit::Second => Self::Second,
607            ArrowTimeUnit::Millisecond => Self::Millisecond,
608            ArrowTimeUnit::Microsecond => Self::Microsecond,
609            ArrowTimeUnit::Nanosecond => Self::Nanosecond,
610        }
611    }
612}
613
614impl From<TimeUnit> for ArrowTimeUnit {
615    fn from(unit: TimeUnit) -> Self {
616        match unit {
617            TimeUnit::Second => Self::Second,
618            TimeUnit::Millisecond => Self::Millisecond,
619            TimeUnit::Microsecond => Self::Microsecond,
620            TimeUnit::Nanosecond => Self::Nanosecond,
621        }
622    }
623}
624
625/// The exact division of a timestamp value into a different unit:
626/// `quotient * from_scale + remainder == value * to_scale` with
627/// `0 <= remainder < from_scale`. `quotient` is the value floored in
628/// `to_unit`; `remainder == 0` iff the value is representable in `to_unit`.
629#[derive(Debug, Clone, Copy, PartialEq, Eq)]
630pub struct UnitQuotient {
631    pub quotient: i64,
632    pub remainder: i128,
633}
634
635/// Divides a `value` given in `from_unit` by `to_unit`, i.e. computes its
636/// value in `to_unit` exactly (floor division). Returns `None` if the
637/// quotient overflows `i64`.
638pub fn div_mod_units(value: i64, from_unit: TimeUnit, to_unit: TimeUnit) -> Option<UnitQuotient> {
639    let from_scale = timestamp_unit_scale(from_unit);
640    let to_scale = timestamp_unit_scale(to_unit);
641    let instant = i128::from(value) * to_scale;
642    let quotient = i64::try_from(instant.div_euclid(from_scale)).ok()?;
643    Some(UnitQuotient {
644        quotient,
645        remainder: instant.rem_euclid(from_scale),
646    })
647}
648
649/// Number of units in one second.
650fn timestamp_unit_scale(unit: TimeUnit) -> i128 {
651    match unit {
652        TimeUnit::Second => 1,
653        TimeUnit::Millisecond => 1_000,
654        TimeUnit::Microsecond => 1_000_000,
655        TimeUnit::Nanosecond => 1_000_000_000,
656    }
657}
658
659impl From<ArrowTimeUnit> for TimeUnit {
660    fn from(unit: ArrowTimeUnit) -> Self {
661        (&unit).into()
662    }
663}
664
665impl Display for TimeUnit {
666    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
667        match self {
668            TimeUnit::Second => {
669                write!(f, "Second")
670            }
671            TimeUnit::Millisecond => {
672                write!(f, "Millisecond")
673            }
674            TimeUnit::Microsecond => {
675                write!(f, "Microsecond")
676            }
677            TimeUnit::Nanosecond => {
678                write!(f, "Nanosecond")
679            }
680        }
681    }
682}
683
684impl TimeUnit {
685    pub fn factor(&self) -> u32 {
686        match self {
687            TimeUnit::Second => 1_000_000_000,
688            TimeUnit::Millisecond => 1_000_000,
689            TimeUnit::Microsecond => 1_000,
690            TimeUnit::Nanosecond => 1,
691        }
692    }
693
694    pub(crate) fn short_name(&self) -> &'static str {
695        match self {
696            TimeUnit::Second => "s",
697            TimeUnit::Millisecond => "ms",
698            TimeUnit::Microsecond => "us",
699            TimeUnit::Nanosecond => "ns",
700        }
701    }
702
703    pub fn as_arrow_time_unit(&self) -> ArrowTimeUnit {
704        match self {
705            Self::Second => ArrowTimeUnit::Second,
706            Self::Millisecond => ArrowTimeUnit::Millisecond,
707            Self::Microsecond => ArrowTimeUnit::Microsecond,
708            Self::Nanosecond => ArrowTimeUnit::Nanosecond,
709        }
710    }
711}
712
713impl PartialOrd for Timestamp {
714    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
715        Some(self.cmp(other))
716    }
717}
718
719/// A proper implementation of total order requires antisymmetry, reflexivity, transitivity and totality.
720/// In this comparison implementation, we map a timestamp uniquely to a `(i64, i64)` tuple which is respectively
721/// total order.
722impl Ord for Timestamp {
723    fn cmp(&self, other: &Self) -> Ordering {
724        // fast path: most comparisons use the same unit.
725        if self.unit == other.unit {
726            return self.value.cmp(&other.value);
727        }
728
729        let (s_sec, s_nsec) = self.split();
730        let (o_sec, o_nsec) = other.split();
731        match s_sec.cmp(&o_sec) {
732            Ordering::Less => Ordering::Less,
733            Ordering::Greater => Ordering::Greater,
734            Ordering::Equal => s_nsec.cmp(&o_nsec),
735        }
736    }
737}
738
739impl PartialEq for Timestamp {
740    fn eq(&self, other: &Self) -> bool {
741        self.cmp(other) == Ordering::Equal
742    }
743}
744
745impl Eq for Timestamp {}
746
747impl Hash for Timestamp {
748    fn hash<H: Hasher>(&self, state: &mut H) {
749        let (sec, nsec) = self.split();
750        state.write_i64(sec);
751        state.write_u32(nsec);
752    }
753}
754
755#[cfg(test)]
756mod tests {
757    use std::collections::hash_map::DefaultHasher;
758
759    use chrono_tz::Tz;
760    use rand::Rng;
761    use serde_json::Value;
762
763    use super::*;
764    use crate::timezone::set_default_timezone;
765
766    #[test]
767    fn test_div_mod_units() {
768        // Representable: 7000ms in us.
769        let q = div_mod_units(7_000, TimeUnit::Millisecond, TimeUnit::Microsecond).unwrap();
770        assert_eq!((7_000_000, 0), (q.quotient, q.remainder));
771        // Not representable: 7_000_500us in ms floors to 7000ms, remainder set.
772        let q = div_mod_units(7_000_500, TimeUnit::Microsecond, TimeUnit::Millisecond).unwrap();
773        assert_eq!(7_000, q.quotient);
774        assert_ne!(0, q.remainder);
775        // Floor semantics for negative instants: -2_500_500us -> -2501ms.
776        let q = div_mod_units(-2_500_500, TimeUnit::Microsecond, TimeUnit::Millisecond).unwrap();
777        assert_eq!(-2_501, q.quotient);
778        // Quotient overflow beyond the target unit's i64 range.
779        assert!(div_mod_units(i64::MAX, TimeUnit::Millisecond, TimeUnit::Nanosecond).is_none());
780    }
781
782    #[test]
783    pub fn test_time_unit() {
784        assert_eq!(
785            TimeUnit::Millisecond.factor() * 1000,
786            TimeUnit::Second.factor()
787        );
788        assert_eq!(
789            TimeUnit::Microsecond.factor() * 1000000,
790            TimeUnit::Second.factor()
791        );
792        assert_eq!(
793            TimeUnit::Nanosecond.factor() * 1000000000,
794            TimeUnit::Second.factor()
795        );
796    }
797
798    #[test]
799    pub fn test_timestamp() {
800        let t = Timestamp::new(1, TimeUnit::Millisecond);
801        assert_eq!(TimeUnit::Millisecond, t.unit());
802        assert_eq!(1, t.value());
803        assert_eq!(Timestamp::new(1000, TimeUnit::Microsecond), t);
804        assert!(t > Timestamp::new(999, TimeUnit::Microsecond));
805    }
806
807    #[test]
808    fn test_timestamp_antisymmetry() {
809        let t1 = Timestamp::new(1, TimeUnit::Second);
810        let t2 = Timestamp::new(1000, TimeUnit::Millisecond);
811        assert!(t1 >= t2);
812        assert!(t2 >= t1);
813        assert_eq!(Ordering::Equal, t1.cmp(&t2));
814    }
815
816    fn gen_random_ts() -> Timestamp {
817        let units = [
818            TimeUnit::Second,
819            TimeUnit::Millisecond,
820            TimeUnit::Microsecond,
821            TimeUnit::Nanosecond,
822        ];
823        let mut rng = rand::rng();
824        let unit_idx: usize = rng.random_range(0..4);
825        let unit = units[unit_idx];
826        let value: i64 = rng.random();
827        Timestamp::new(value, unit)
828    }
829
830    #[test]
831    fn test_add_sub_interval() {
832        let ts = Timestamp::new(1000, TimeUnit::Millisecond);
833
834        let interval = IntervalDayTime::new(1, 200);
835
836        let new_ts = ts.add_day_time(interval).unwrap();
837        assert_eq!(new_ts.unit(), TimeUnit::Millisecond);
838        assert_eq!(new_ts.value(), 1000 + 3600 * 24 * 1000 + 200);
839
840        assert_eq!(ts, new_ts.sub_day_time(interval).unwrap());
841    }
842
843    #[test]
844    fn test_timestamp_reflexivity() {
845        for _ in 0..1000 {
846            let ts = gen_random_ts();
847            assert!(ts >= ts, "ts: {ts:?}");
848        }
849    }
850
851    /// Generate timestamp less than or equal to `threshold`
852    fn gen_ts_le(threshold: &Timestamp) -> Timestamp {
853        let mut rng = rand::rng();
854        let timestamp = rng.random_range(i64::MIN..=threshold.value);
855        Timestamp::new(timestamp, threshold.unit)
856    }
857
858    #[test]
859    fn test_timestamp_transitivity() {
860        let t0 = Timestamp::new_millisecond(100);
861        let t1 = gen_ts_le(&t0);
862        let t2 = gen_ts_le(&t1);
863        assert!(t0 >= t1, "t0: {t0:?}, t1: {t1:?}");
864        assert!(t1 >= t2, "t1: {t1:?}, t2: {t2:?}");
865        assert!(t0 >= t2, "t0: {t0:?}, t2: {t2:?}");
866
867        let t0 = Timestamp::new_millisecond(-100);
868        let t1 = gen_ts_le(&t0); // t0 >= t1
869        let t2 = gen_ts_le(&t1); // t1 >= t2
870        assert!(t0 >= t1, "t0: {t0:?}, t1: {t1:?}");
871        assert!(t1 >= t2, "t1: {t1:?}, t2: {t2:?}");
872        assert!(t0 >= t2, "t0: {t0:?}, t2: {t2:?}"); // check if t0 >= t2
873    }
874
875    #[test]
876    fn test_antisymmetry() {
877        let t0 = Timestamp::new(1, TimeUnit::Second);
878        let t1 = Timestamp::new(1000, TimeUnit::Millisecond);
879        assert!(t0 >= t1, "t0: {t0:?}, t1: {t1:?}");
880        assert!(t1 >= t0, "t0: {t0:?}, t1: {t1:?}");
881        assert_eq!(t1, t0, "t0: {t0:?}, t1: {t1:?}");
882    }
883
884    #[test]
885    fn test_strong_connectivity() {
886        let mut values = Vec::with_capacity(1000);
887        for _ in 0..1000 {
888            values.push(gen_random_ts());
889        }
890
891        for l in &values {
892            for r in &values {
893                assert!(l >= r || l <= r, "l: {l:?}, r: {r:?}");
894            }
895        }
896    }
897
898    #[test]
899    fn test_cmp_timestamp() {
900        let t1 = Timestamp::new(0, TimeUnit::Millisecond);
901        let t2 = Timestamp::new(0, TimeUnit::Second);
902        assert_eq!(t2, t1);
903
904        let t1 = Timestamp::new(1, TimeUnit::Millisecond);
905        let t2 = Timestamp::new(-1, TimeUnit::Second);
906        assert!(t1 > t2);
907
908        let t1 = Timestamp::new(i64::MAX / 1000 * 1000, TimeUnit::Millisecond);
909        let t2 = Timestamp::new(i64::MAX / 1000, TimeUnit::Second);
910        assert_eq!(t2, t1);
911
912        let t1 = Timestamp::new(i64::MAX, TimeUnit::Millisecond);
913        let t2 = Timestamp::new(i64::MAX / 1000 + 1, TimeUnit::Second);
914        assert!(t2 > t1);
915
916        let t1 = Timestamp::new(i64::MAX, TimeUnit::Millisecond);
917        let t2 = Timestamp::new(i64::MAX / 1000, TimeUnit::Second);
918        assert!(t2 < t1);
919
920        let t1 = Timestamp::new(10_010_001, TimeUnit::Millisecond);
921        let t2 = Timestamp::new(100, TimeUnit::Second);
922        assert!(t1 > t2);
923
924        let t1 = Timestamp::new(-100 * 10_001, TimeUnit::Millisecond);
925        let t2 = Timestamp::new(-100, TimeUnit::Second);
926        assert!(t2 > t1);
927
928        let t1 = Timestamp::new(i64::MIN, TimeUnit::Millisecond);
929        let t2 = Timestamp::new(i64::MIN / 1000 - 1, TimeUnit::Second);
930        assert!(t1 > t2);
931
932        let t1 = Timestamp::new(i64::MIN, TimeUnit::Millisecond);
933        let t2 = Timestamp::new(i64::MIN + 1, TimeUnit::Millisecond);
934        assert!(t2 > t1);
935
936        let t1 = Timestamp::new(i64::MAX, TimeUnit::Millisecond);
937        let t2 = Timestamp::new(i64::MIN, TimeUnit::Second);
938        assert!(t1 > t2);
939
940        let t1 = Timestamp::new(0, TimeUnit::Nanosecond);
941        let t2 = Timestamp::new(i64::MIN, TimeUnit::Second);
942        assert!(t1 > t2);
943    }
944
945    fn check_hash_eq(t1: Timestamp, t2: Timestamp) {
946        let mut hasher = DefaultHasher::new();
947        t1.hash(&mut hasher);
948        let t1_hash = hasher.finish();
949
950        let mut hasher = DefaultHasher::new();
951        t2.hash(&mut hasher);
952        let t2_hash = hasher.finish();
953        assert_eq!(t2_hash, t1_hash);
954    }
955
956    #[test]
957    fn test_hash() {
958        check_hash_eq(
959            Timestamp::new(0, TimeUnit::Millisecond),
960            Timestamp::new(0, TimeUnit::Second),
961        );
962        check_hash_eq(
963            Timestamp::new(1000, TimeUnit::Millisecond),
964            Timestamp::new(1, TimeUnit::Second),
965        );
966        check_hash_eq(
967            Timestamp::new(1_000_000, TimeUnit::Microsecond),
968            Timestamp::new(1, TimeUnit::Second),
969        );
970        check_hash_eq(
971            Timestamp::new(1_000_000_000, TimeUnit::Nanosecond),
972            Timestamp::new(1, TimeUnit::Second),
973        );
974    }
975
976    #[test]
977    pub fn test_from_i64() {
978        let t: Timestamp = 42.into();
979        assert_eq!(42, t.value());
980        assert_eq!(TimeUnit::Millisecond, t.unit());
981    }
982
983    // Input timestamp string is regarded as local timezone if no timezone is specified,
984    // but expected timestamp is in UTC timezone
985    fn check_from_str(s: &str, expect: &str) {
986        let ts = Timestamp::from_str_utc(s).unwrap();
987        let time = ts.to_chrono_datetime().unwrap();
988        assert_eq!(expect, time.to_string());
989    }
990
991    #[test]
992    fn test_from_str() {
993        // Explicit Z means timestamp in UTC
994        check_from_str("2020-09-08 13:42:29Z", "2020-09-08 13:42:29");
995        check_from_str("2020-09-08T13:42:29+08:00", "2020-09-08 05:42:29");
996
997        check_from_str("2020-09-08 13:42:29", "2020-09-08 13:42:29");
998
999        check_from_str("2020-09-08 13:42:29.042Z", "2020-09-08 13:42:29.042");
1000        check_from_str("2020-09-08 13:42:29.042+08:00", "2020-09-08 05:42:29.042");
1001
1002        check_from_str(
1003            "2020-09-08T13:42:29.0042+08:00",
1004            "2020-09-08 05:42:29.004200",
1005        );
1006    }
1007
1008    #[test]
1009    fn test_from_naive_datetime() {
1010        let datetime = NaiveDate::from_ymd_opt(2026, 8, 13)
1011            .unwrap()
1012            .and_hms_opt(8, 0, 0)
1013            .unwrap();
1014
1015        // A fixed-offset timezone shifts the datetime by a constant amount.
1016        let shanghai = Timezone::from_tz_string("Asia/Shanghai").unwrap();
1017        assert_eq!(
1018            "2026-08-13 00:00:00",
1019            Timestamp::from_naive_datetime(datetime, &shanghai)
1020                .unwrap()
1021                .to_chrono_datetime()
1022                .unwrap()
1023                .to_string()
1024        );
1025
1026        // 2026-03-08 02:30 does not exist in America/New_York (DST gap).
1027        let new_york = Timezone::from_tz_string("America/New_York").unwrap();
1028        let gap = NaiveDate::from_ymd_opt(2026, 3, 8)
1029            .unwrap()
1030            .and_hms_opt(2, 30, 0)
1031            .unwrap();
1032        assert!(Timestamp::from_naive_datetime(gap, &new_york).is_err());
1033
1034        // 2026-11-01 01:30 is ambiguous in America/New_York; picks the first
1035        // instant (EDT, UTC-4).
1036        let ambiguous = NaiveDate::from_ymd_opt(2026, 11, 1)
1037            .unwrap()
1038            .and_hms_opt(1, 30, 0)
1039            .unwrap();
1040        assert_eq!(
1041            "2026-11-01 05:30:00",
1042            Timestamp::from_naive_datetime(ambiguous, &new_york)
1043                .unwrap()
1044                .to_chrono_datetime()
1045                .unwrap()
1046                .to_string()
1047        );
1048    }
1049
1050    #[test]
1051    fn test_to_iso8601_string() {
1052        set_default_timezone(Some("Asia/Shanghai")).unwrap();
1053        let datetime_str = "2020-09-08 13:42:29.042+0000";
1054        let ts = Timestamp::from_str_utc(datetime_str).unwrap();
1055        assert_eq!("2020-09-08 21:42:29.042+0800", ts.to_iso8601_string());
1056
1057        let ts_millis = 1668070237000;
1058        let ts = Timestamp::new_millisecond(ts_millis);
1059        assert_eq!("2022-11-10 16:50:37+0800", ts.to_iso8601_string());
1060
1061        let ts_millis = -1000;
1062        let ts = Timestamp::new_millisecond(ts_millis);
1063        assert_eq!("1970-01-01 07:59:59+0800", ts.to_iso8601_string());
1064
1065        let ts_millis = -1;
1066        let ts = Timestamp::new_millisecond(ts_millis);
1067        assert_eq!("1970-01-01 07:59:59.999+0800", ts.to_iso8601_string());
1068
1069        let ts_millis = -1001;
1070        let ts = Timestamp::new_millisecond(ts_millis);
1071        assert_eq!("1970-01-01 07:59:58.999+0800", ts.to_iso8601_string());
1072    }
1073
1074    #[test]
1075    fn test_serialize_to_json_value() {
1076        set_default_timezone(Some("Asia/Shanghai")).unwrap();
1077        assert_eq!(
1078            "1970-01-01 08:00:01+0800",
1079            match serde_json::Value::from(Timestamp::new(1, TimeUnit::Second)) {
1080                Value::String(s) => s,
1081                _ => unreachable!(),
1082            }
1083        );
1084
1085        assert_eq!(
1086            "1970-01-01 08:00:00.001+0800",
1087            match serde_json::Value::from(Timestamp::new(1, TimeUnit::Millisecond)) {
1088                Value::String(s) => s,
1089                _ => unreachable!(),
1090            }
1091        );
1092
1093        assert_eq!(
1094            "1970-01-01 08:00:00.000001+0800",
1095            match serde_json::Value::from(Timestamp::new(1, TimeUnit::Microsecond)) {
1096                Value::String(s) => s,
1097                _ => unreachable!(),
1098            }
1099        );
1100
1101        assert_eq!(
1102            "1970-01-01 08:00:00.000000001+0800",
1103            match serde_json::Value::from(Timestamp::new(1, TimeUnit::Nanosecond)) {
1104                Value::String(s) => s,
1105                _ => unreachable!(),
1106            }
1107        );
1108    }
1109
1110    #[test]
1111    fn test_convert_timestamp() {
1112        let ts = Timestamp::new(1, TimeUnit::Second);
1113        assert_eq!(
1114            Timestamp::new(1000, TimeUnit::Millisecond),
1115            ts.convert_to(TimeUnit::Millisecond).unwrap()
1116        );
1117        assert_eq!(
1118            Timestamp::new(1_000_000, TimeUnit::Microsecond),
1119            ts.convert_to(TimeUnit::Microsecond).unwrap()
1120        );
1121        assert_eq!(
1122            Timestamp::new(1_000_000_000, TimeUnit::Nanosecond),
1123            ts.convert_to(TimeUnit::Nanosecond).unwrap()
1124        );
1125
1126        let ts = Timestamp::new(1_000_100_100, TimeUnit::Nanosecond);
1127        assert_eq!(
1128            Timestamp::new(1_000_100, TimeUnit::Microsecond),
1129            ts.convert_to(TimeUnit::Microsecond).unwrap()
1130        );
1131        assert_eq!(
1132            Timestamp::new(1000, TimeUnit::Millisecond),
1133            ts.convert_to(TimeUnit::Millisecond).unwrap()
1134        );
1135        assert_eq!(
1136            Timestamp::new(1, TimeUnit::Second),
1137            ts.convert_to(TimeUnit::Second).unwrap()
1138        );
1139
1140        let ts = Timestamp::new(1_000_100_100, TimeUnit::Nanosecond);
1141        assert_eq!(ts, ts.convert_to(TimeUnit::Nanosecond).unwrap());
1142        let ts = Timestamp::new(1_000_100_100, TimeUnit::Microsecond);
1143        assert_eq!(ts, ts.convert_to(TimeUnit::Microsecond).unwrap());
1144        let ts = Timestamp::new(1_000_100_100, TimeUnit::Millisecond);
1145        assert_eq!(ts, ts.convert_to(TimeUnit::Millisecond).unwrap());
1146        let ts = Timestamp::new(1_000_100_100, TimeUnit::Second);
1147        assert_eq!(ts, ts.convert_to(TimeUnit::Second).unwrap());
1148
1149        // -9223372036854775808 in milliseconds should be rounded up to -9223372036854776 in seconds
1150        assert_eq!(
1151            Timestamp::new(-9223372036854776, TimeUnit::Second),
1152            Timestamp::new(i64::MIN, TimeUnit::Millisecond)
1153                .convert_to(TimeUnit::Second)
1154                .unwrap()
1155        );
1156
1157        assert!(
1158            Timestamp::new(i64::MAX, TimeUnit::Second)
1159                .convert_to(TimeUnit::Millisecond)
1160                .is_none()
1161        );
1162    }
1163
1164    #[test]
1165    fn test_split() {
1166        assert_eq!((0, 0), Timestamp::new(0, TimeUnit::Second).split());
1167        assert_eq!((1, 0), Timestamp::new(1, TimeUnit::Second).split());
1168        assert_eq!(
1169            (0, 1_000_000),
1170            Timestamp::new(1, TimeUnit::Millisecond).split()
1171        );
1172
1173        assert_eq!((0, 1_000), Timestamp::new(1, TimeUnit::Microsecond).split());
1174        assert_eq!((0, 1), Timestamp::new(1, TimeUnit::Nanosecond).split());
1175
1176        assert_eq!(
1177            (1, 1_000_000),
1178            Timestamp::new(1001, TimeUnit::Millisecond).split()
1179        );
1180
1181        assert_eq!(
1182            (-2, 999_000_000),
1183            Timestamp::new(-1001, TimeUnit::Millisecond).split()
1184        );
1185
1186        // check min value of nanos
1187        let (sec, nsec) = Timestamp::new(i64::MIN, TimeUnit::Nanosecond).split();
1188        assert_eq!(
1189            i64::MIN as i128,
1190            sec as i128 * (TimeUnit::Second.factor() / TimeUnit::Nanosecond.factor()) as i128
1191                + nsec as i128
1192        );
1193
1194        assert_eq!(
1195            (i64::MAX, 0),
1196            Timestamp::new(i64::MAX, TimeUnit::Second).split()
1197        );
1198    }
1199
1200    #[test]
1201    fn test_convert_to_ceil() {
1202        assert_eq!(
1203            Timestamp::new(1, TimeUnit::Second),
1204            Timestamp::new(1000, TimeUnit::Millisecond)
1205                .convert_to_ceil(TimeUnit::Second)
1206                .unwrap()
1207        );
1208
1209        // These two cases shows how `Timestamp::convert_to_ceil` behaves differently
1210        // from `Timestamp::convert_to` when converting larger unit to smaller unit.
1211        assert_eq!(
1212            Timestamp::new(1, TimeUnit::Second),
1213            Timestamp::new(1001, TimeUnit::Millisecond)
1214                .convert_to(TimeUnit::Second)
1215                .unwrap()
1216        );
1217        assert_eq!(
1218            Timestamp::new(2, TimeUnit::Second),
1219            Timestamp::new(1001, TimeUnit::Millisecond)
1220                .convert_to_ceil(TimeUnit::Second)
1221                .unwrap()
1222        );
1223
1224        assert_eq!(
1225            Timestamp::new(-1, TimeUnit::Second),
1226            Timestamp::new(-1, TimeUnit::Millisecond)
1227                .convert_to(TimeUnit::Second)
1228                .unwrap()
1229        );
1230        assert_eq!(
1231            Timestamp::new(0, TimeUnit::Second),
1232            Timestamp::new(-1, TimeUnit::Millisecond)
1233                .convert_to_ceil(TimeUnit::Second)
1234                .unwrap()
1235        );
1236
1237        // When converting large unit to smaller unit, there will be no rounding error,
1238        // so `Timestamp::convert_to_ceil` behaves just like `Timestamp::convert_to`
1239        assert_eq!(
1240            Timestamp::new(-1, TimeUnit::Second).convert_to(TimeUnit::Millisecond),
1241            Timestamp::new(-1, TimeUnit::Second).convert_to_ceil(TimeUnit::Millisecond)
1242        );
1243        assert_eq!(
1244            Timestamp::new(1000, TimeUnit::Second).convert_to(TimeUnit::Millisecond),
1245            Timestamp::new(1000, TimeUnit::Second).convert_to_ceil(TimeUnit::Millisecond)
1246        );
1247        assert_eq!(
1248            Timestamp::new(1, TimeUnit::Second).convert_to(TimeUnit::Millisecond),
1249            Timestamp::new(1, TimeUnit::Second).convert_to_ceil(TimeUnit::Millisecond)
1250        );
1251    }
1252
1253    #[test]
1254    fn test_split_overflow() {
1255        let _ = Timestamp::new(i64::MAX, TimeUnit::Second).split();
1256        let _ = Timestamp::new(i64::MIN, TimeUnit::Second).split();
1257        let _ = Timestamp::new(i64::MAX, TimeUnit::Millisecond).split();
1258        let _ = Timestamp::new(i64::MIN, TimeUnit::Millisecond).split();
1259        let _ = Timestamp::new(i64::MAX, TimeUnit::Microsecond).split();
1260        let _ = Timestamp::new(i64::MIN, TimeUnit::Microsecond).split();
1261        let _ = Timestamp::new(i64::MAX, TimeUnit::Nanosecond).split();
1262        let _ = Timestamp::new(i64::MIN, TimeUnit::Nanosecond).split();
1263        let (sec, nsec) = Timestamp::new(i64::MIN, TimeUnit::Nanosecond).split();
1264        let time = DateTime::from_timestamp(sec, nsec).unwrap().naive_utc();
1265        assert_eq!(sec, time.and_utc().timestamp());
1266        assert_eq!(nsec, time.and_utc().timestamp_subsec_nanos());
1267    }
1268
1269    #[test]
1270    fn test_timestamp_sub() {
1271        let res = Timestamp::new(1, TimeUnit::Second)
1272            .sub_duration(Duration::from_secs(1))
1273            .unwrap();
1274        assert_eq!(0, res.value);
1275        assert_eq!(TimeUnit::Second, res.unit);
1276
1277        let res = Timestamp::new(0, TimeUnit::Second)
1278            .sub_duration(Duration::from_secs(1))
1279            .unwrap();
1280        assert_eq!(-1, res.value);
1281        assert_eq!(TimeUnit::Second, res.unit);
1282
1283        let res = Timestamp::new(1, TimeUnit::Second)
1284            .sub_duration(Duration::from_millis(1))
1285            .unwrap();
1286        assert_eq!(1, res.value);
1287        assert_eq!(TimeUnit::Second, res.unit);
1288    }
1289
1290    #[test]
1291    fn test_timestamp_add() {
1292        let res = Timestamp::new(1, TimeUnit::Second)
1293            .add_duration(Duration::from_secs(1))
1294            .unwrap();
1295        assert_eq!(2, res.value);
1296        assert_eq!(TimeUnit::Second, res.unit);
1297
1298        let res = Timestamp::new(0, TimeUnit::Second)
1299            .add_duration(Duration::from_secs(1))
1300            .unwrap();
1301        assert_eq!(1, res.value);
1302        assert_eq!(TimeUnit::Second, res.unit);
1303
1304        let res = Timestamp::new(1, TimeUnit::Second)
1305            .add_duration(Duration::from_millis(1))
1306            .unwrap();
1307        assert_eq!(1, res.value);
1308        assert_eq!(TimeUnit::Second, res.unit);
1309
1310        let res = Timestamp::new(100, TimeUnit::Second)
1311            .add_duration(Duration::from_millis(1000))
1312            .unwrap();
1313        assert_eq!(101, res.value);
1314        assert_eq!(TimeUnit::Second, res.unit);
1315    }
1316
1317    // $TZ doesn't take effort.
1318    #[test]
1319    fn test_parse_in_timezone() {
1320        unsafe {
1321            std::env::set_var("TZ", "Asia/Shanghai");
1322        }
1323        assert_eq!(
1324            Timestamp::new(28800, TimeUnit::Second),
1325            Timestamp::from_str_utc("1970-01-01 08:00:00.000").unwrap()
1326        );
1327
1328        assert_eq!(
1329            Timestamp::new(28800, TimeUnit::Second),
1330            Timestamp::from_str_utc("1970-01-01 08:00:00").unwrap()
1331        );
1332
1333        assert_eq!(
1334            Timestamp::new(28800, TimeUnit::Second),
1335            Timestamp::from_str_utc("      1970-01-01        08:00:00    ").unwrap()
1336        );
1337    }
1338
1339    #[test]
1340    fn test_to_local_string() {
1341        set_default_timezone(Some("Asia/Shanghai")).unwrap();
1342
1343        assert_eq!(
1344            "1970-01-01 08:00:00.000000001",
1345            Timestamp::new(1, TimeUnit::Nanosecond).to_local_string()
1346        );
1347
1348        assert_eq!(
1349            "1970-01-01 08:00:00.001",
1350            Timestamp::new(1, TimeUnit::Millisecond).to_local_string()
1351        );
1352
1353        assert_eq!(
1354            "1970-01-01 08:00:01",
1355            Timestamp::new(1, TimeUnit::Second).to_local_string()
1356        );
1357    }
1358
1359    #[test]
1360    fn test_subtract_timestamp() {
1361        assert_eq!(
1362            chrono::Duration::try_milliseconds(42),
1363            Timestamp::new_millisecond(100).sub(&Timestamp::new_millisecond(58))
1364        );
1365
1366        assert_eq!(
1367            chrono::Duration::try_milliseconds(-42),
1368            Timestamp::new_millisecond(58).sub(&Timestamp::new_millisecond(100))
1369        );
1370    }
1371
1372    #[test]
1373    fn test_to_timezone_aware_string() {
1374        set_default_timezone(Some("Asia/Shanghai")).unwrap();
1375        unsafe {
1376            std::env::set_var("TZ", "Asia/Shanghai");
1377        }
1378        assert_eq!(
1379            "1970-01-01 08:00:00.001",
1380            Timestamp::new(1, TimeUnit::Millisecond)
1381                .to_timezone_aware_string(Some(&Timezone::from_tz_string("SYSTEM").unwrap()))
1382        );
1383        assert_eq!(
1384            "1970-01-01 08:00:00.001",
1385            Timestamp::new(1, TimeUnit::Millisecond)
1386                .to_timezone_aware_string(Some(&Timezone::from_tz_string("SYSTEM").unwrap()))
1387        );
1388        assert_eq!(
1389            "1970-01-01 08:00:00.001",
1390            Timestamp::new(1, TimeUnit::Millisecond)
1391                .to_timezone_aware_string(Some(&Timezone::from_tz_string("+08:00").unwrap()))
1392        );
1393        assert_eq!(
1394            "1970-01-01 07:00:00.001",
1395            Timestamp::new(1, TimeUnit::Millisecond)
1396                .to_timezone_aware_string(Some(&Timezone::from_tz_string("+07:00").unwrap()))
1397        );
1398        assert_eq!(
1399            "1969-12-31 23:00:00.001",
1400            Timestamp::new(1, TimeUnit::Millisecond)
1401                .to_timezone_aware_string(Some(&Timezone::from_tz_string("-01:00").unwrap()))
1402        );
1403        assert_eq!(
1404            "1970-01-01 08:00:00.001",
1405            Timestamp::new(1, TimeUnit::Millisecond).to_timezone_aware_string(Some(
1406                &Timezone::from_tz_string("Asia/Shanghai").unwrap()
1407            ))
1408        );
1409        assert_eq!(
1410            "1970-01-01 00:00:00.001",
1411            Timestamp::new(1, TimeUnit::Millisecond)
1412                .to_timezone_aware_string(Some(&Timezone::from_tz_string("UTC").unwrap()))
1413        );
1414        assert_eq!(
1415            "1970-01-01 01:00:00.001",
1416            Timestamp::new(1, TimeUnit::Millisecond).to_timezone_aware_string(Some(
1417                &Timezone::from_tz_string("Europe/Berlin").unwrap()
1418            ))
1419        );
1420        assert_eq!(
1421            "1970-01-01 03:00:00.001",
1422            Timestamp::new(1, TimeUnit::Millisecond).to_timezone_aware_string(Some(
1423                &Timezone::from_tz_string("Europe/Moscow").unwrap()
1424            ))
1425        );
1426    }
1427
1428    #[test]
1429    fn test_as_formatted_string() {
1430        let ts = Timestamp::new(1, TimeUnit::Millisecond);
1431
1432        assert_eq!(
1433            "1970-01-01",
1434            ts.as_formatted_string("%Y-%m-%d", None).unwrap()
1435        );
1436        assert_eq!(
1437            "1970-01-01 00:00:00",
1438            ts.as_formatted_string("%Y-%m-%d %H:%M:%S", None).unwrap()
1439        );
1440        assert_eq!(
1441            "1970-01-01T00:00:00:001",
1442            ts.as_formatted_string("%Y-%m-%dT%H:%M:%S:%3f", None)
1443                .unwrap()
1444        );
1445        assert_eq!(
1446            "1970-01-01T08:00:00:001",
1447            ts.as_formatted_string(
1448                "%Y-%m-%dT%H:%M:%S:%3f",
1449                Some(&Timezone::from_tz_string("Asia/Shanghai").unwrap())
1450            )
1451            .unwrap()
1452        );
1453    }
1454
1455    #[test]
1456    fn test_to_chrono_datetime_with_timezone_bounds() {
1457        let positive_offset = Timezone::from_tz_string("+08:00").unwrap();
1458        assert_eq!(
1459            None,
1460            Timestamp::MAX_SECOND.to_chrono_datetime_with_timezone(Some(&positive_offset))
1461        );
1462
1463        let negative_offset = Timezone::from_tz_string("-08:00").unwrap();
1464        assert_eq!(
1465            None,
1466            Timestamp::MIN_SECOND.to_chrono_datetime_with_timezone(Some(&negative_offset))
1467        );
1468    }
1469
1470    #[test]
1471    fn test_to_chrono_datetime_with_named_timezone_summer_offset() {
1472        let timestamp = Timestamp::from_str_utc("2024-07-01 12:00:00Z").unwrap();
1473        let berlin = Timezone::from_tz_string("Europe/Berlin").unwrap();
1474
1475        assert_eq!(
1476            Some(
1477                NaiveDate::from_ymd_opt(2024, 7, 1)
1478                    .unwrap()
1479                    .and_hms_opt(14, 0, 0)
1480                    .unwrap()
1481            ),
1482            timestamp.to_chrono_datetime_with_timezone(Some(&berlin))
1483        );
1484    }
1485
1486    #[test]
1487    fn test_from_arrow_time_unit() {
1488        assert_eq!(TimeUnit::Second, TimeUnit::from(ArrowTimeUnit::Second));
1489        assert_eq!(
1490            TimeUnit::Millisecond,
1491            TimeUnit::from(ArrowTimeUnit::Millisecond)
1492        );
1493        assert_eq!(
1494            TimeUnit::Microsecond,
1495            TimeUnit::from(ArrowTimeUnit::Microsecond)
1496        );
1497        assert_eq!(
1498            TimeUnit::Nanosecond,
1499            TimeUnit::from(ArrowTimeUnit::Nanosecond)
1500        );
1501    }
1502
1503    fn check_conversion(ts: Timestamp, valid: bool) {
1504        let Some(t2) = ts.to_chrono_datetime() else {
1505            if valid {
1506                panic!("Cannot convert {:?} to Chrono NaiveDateTime", ts);
1507            }
1508            return;
1509        };
1510        let Some(t3) = Timestamp::from_chrono_datetime(t2) else {
1511            if valid {
1512                panic!("Cannot convert Chrono NaiveDateTime {:?} to Timestamp", t2);
1513            }
1514            return;
1515        };
1516
1517        assert_eq!(t3, ts);
1518    }
1519
1520    #[test]
1521    fn test_from_naive_date_time() {
1522        let naive_date_time_min = NaiveDateTime::MIN.and_utc();
1523        let naive_date_time_max = NaiveDateTime::MAX.and_utc();
1524
1525        let min_sec = Timestamp::new_second(naive_date_time_min.timestamp());
1526        let max_sec = Timestamp::new_second(naive_date_time_max.timestamp());
1527        check_conversion(min_sec, true);
1528        check_conversion(Timestamp::new_second(min_sec.value - 1), false);
1529        check_conversion(max_sec, true);
1530        check_conversion(Timestamp::new_second(max_sec.value + 1), false);
1531
1532        let min_millis = Timestamp::new_millisecond(naive_date_time_min.timestamp_millis());
1533        let max_millis = Timestamp::new_millisecond(naive_date_time_max.timestamp_millis());
1534        check_conversion(min_millis, true);
1535        check_conversion(Timestamp::new_millisecond(min_millis.value - 1), false);
1536        check_conversion(max_millis, true);
1537        check_conversion(Timestamp::new_millisecond(max_millis.value + 1), false);
1538
1539        let min_micros = Timestamp::new_microsecond(naive_date_time_min.timestamp_micros());
1540        let max_micros = Timestamp::new_microsecond(naive_date_time_max.timestamp_micros());
1541        check_conversion(min_micros, true);
1542        check_conversion(Timestamp::new_microsecond(min_micros.value - 1), false);
1543        check_conversion(max_micros, true);
1544        check_conversion(Timestamp::new_microsecond(max_micros.value + 1), false);
1545
1546        // the min time that can be represented by nanoseconds is: 1677-09-21T00:12:43.145224192
1547        let min_nanos = Timestamp::new_nanosecond(-9223372036854775000);
1548        let max_nanos = Timestamp::new_nanosecond(i64::MAX);
1549        check_conversion(min_nanos, true);
1550        check_conversion(Timestamp::new_nanosecond(min_nanos.value - 1), false);
1551        check_conversion(max_nanos, true);
1552    }
1553
1554    #[test]
1555    fn test_parse_timestamp_range() {
1556        let datetime_min = NaiveDateTime::MIN.format("%Y-%m-%d %H:%M:%SZ").to_string();
1557        assert_eq!("-262143-01-01 00:00:00Z", datetime_min);
1558        let datetime_max = NaiveDateTime::MAX.format("%Y-%m-%d %H:%M:%SZ").to_string();
1559        assert_eq!("+262142-12-31 23:59:59Z", datetime_max);
1560
1561        let valid_strings = vec![
1562            "-262143-01-01 00:00:00Z",
1563            "+262142-12-31 23:59:59Z",
1564            "+262142-12-31 23:59:59.999Z",
1565            "+262142-12-31 23:59:59.999999Z",
1566            "1677-09-21 00:12:43.145224192Z",
1567            "2262-04-11 23:47:16.854775807Z",
1568            "+100000-01-01 00:00:01.5Z",
1569        ];
1570
1571        for s in valid_strings {
1572            Timestamp::from_str_utc(s).unwrap();
1573        }
1574    }
1575
1576    #[test]
1577    fn test_min_nanos_roundtrip() {
1578        let (sec, nsec) = Timestamp::MIN_NANOSECOND.split();
1579        let ts = Timestamp::from_splits(sec, nsec).unwrap();
1580        assert_eq!(Timestamp::MIN_NANOSECOND, ts);
1581    }
1582
1583    #[test]
1584    fn test_timestamp_bound_format() {
1585        assert_eq!(
1586            "1677-09-21 00:12:43.145224192",
1587            Timestamp::MIN_NANOSECOND.to_timezone_aware_string(Some(&Timezone::Named(Tz::UTC)))
1588        );
1589        assert_eq!(
1590            "2262-04-11 23:47:16.854775807",
1591            Timestamp::MAX_NANOSECOND.to_timezone_aware_string(Some(&Timezone::Named(Tz::UTC)))
1592        );
1593        assert_eq!(
1594            "-262143-01-01 00:00:00",
1595            Timestamp::MIN_MICROSECOND.to_timezone_aware_string(Some(&Timezone::Named(Tz::UTC)))
1596        );
1597        assert_eq!(
1598            "+262142-12-31 23:59:59.999999",
1599            Timestamp::MAX_MICROSECOND.to_timezone_aware_string(Some(&Timezone::Named(Tz::UTC)))
1600        );
1601        assert_eq!(
1602            "-262143-01-01 00:00:00",
1603            Timestamp::MIN_MILLISECOND.to_timezone_aware_string(Some(&Timezone::Named(Tz::UTC)))
1604        );
1605        assert_eq!(
1606            "+262142-12-31 23:59:59.999",
1607            Timestamp::MAX_MILLISECOND.to_timezone_aware_string(Some(&Timezone::Named(Tz::UTC)))
1608        );
1609        assert_eq!(
1610            "-262143-01-01 00:00:00",
1611            Timestamp::MIN_SECOND.to_timezone_aware_string(Some(&Timezone::Named(Tz::UTC)))
1612        );
1613        assert_eq!(
1614            "+262142-12-31 23:59:59",
1615            Timestamp::MAX_SECOND.to_timezone_aware_string(Some(&Timezone::Named(Tz::UTC)))
1616        );
1617    }
1618
1619    #[test]
1620    fn test_debug_timestamp() {
1621        assert_eq!(
1622            "1000::Second",
1623            format!("{:?}", Timestamp::new(1000, TimeUnit::Second))
1624        );
1625        assert_eq!(
1626            "1001::Millisecond",
1627            format!("{:?}", Timestamp::new(1001, TimeUnit::Millisecond))
1628        );
1629        assert_eq!(
1630            "1002::Microsecond",
1631            format!("{:?}", Timestamp::new(1002, TimeUnit::Microsecond))
1632        );
1633        assert_eq!(
1634            "1003::Nanosecond",
1635            format!("{:?}", Timestamp::new(1003, TimeUnit::Nanosecond))
1636        );
1637    }
1638}