risingwave_expr_impl/scalar/
to_timestamp.rs1use chrono::format::Parsed;
16use risingwave_common::types::{Date, Timestamp, Timestamptz};
17use risingwave_expr::{ExprError, Result, function};
18
19use super::timestamptz::{timestamp_at_time_zone, timestamptz_at_time_zone};
20use super::to_char::ChronoPattern;
21
22#[inline(always)]
24fn parse(s: &str, tmpl: &ChronoPattern) -> Result<Parsed> {
25    let mut parsed = Parsed::new();
26    chrono::format::parse(&mut parsed, s, tmpl.borrow_dependent().iter())?;
27
28    if parsed.year.is_none()
33        && parsed.year_div_100.is_none()
34        && parsed.year_mod_100.is_none()
35        && parsed.isoyear.is_none()
36        && parsed.isoyear_div_100.is_none()
37        && parsed.isoyear_mod_100.is_none()
38    {
39        parsed.set_year(-1).unwrap();
40    }
41
42    if parsed.month.is_none()
44        && parsed.week_from_mon.is_none()
45        && parsed.week_from_sun.is_none()
46        && parsed.isoweek.is_none()
47    {
48        parsed.set_month(1).unwrap();
49    }
50
51    if parsed.day.is_none() && parsed.ordinal.is_none() {
53        parsed.set_day(1).unwrap();
54    }
55
56    parsed.hour_div_12.get_or_insert(0);
58
59    parsed.hour_mod_12.get_or_insert(0);
61    parsed.minute.get_or_insert(0);
62
63    Ok(parsed)
66}
67
68#[function(
69    "char_to_timestamptz(varchar, varchar) -> timestamp",
70    prebuild = "ChronoPattern::compile($1)",
71    deprecated
72)]
73pub fn to_timestamp_legacy(s: &str, tmpl: &ChronoPattern) -> Result<Timestamp> {
74    let parsed = parse(s, tmpl)?;
75    match parsed.offset {
76        None => Ok(parsed.to_naive_datetime_with_offset(0)?.into()),
77        Some(_) => timestamptz_at_time_zone(parsed.to_datetime()?.into(), "UTC"),
80    }
81}
82
83#[function(
84    "char_to_timestamptz(varchar, varchar, varchar) -> timestamptz",
85    prebuild = "ChronoPattern::compile($1)"
86)]
87pub fn to_timestamp(s: &str, timezone: &str, tmpl: &ChronoPattern) -> Result<Timestamptz> {
88    let parsed = parse(s, tmpl)?;
89    Ok(match parsed.offset {
90        Some(_) => parsed.to_datetime()?.into(),
91        None => timestamp_at_time_zone(parsed.to_naive_datetime_with_offset(0)?.into(), timezone)?,
93    })
94}
95
96#[function("char_to_timestamptz(varchar, varchar) -> timestamptz", rewritten)]
97fn _to_timestamp1() {}
98
99#[function(
100    "char_to_date(varchar, varchar) -> date",
101    prebuild = "ChronoPattern::compile($1)"
102)]
103pub fn to_date(s: &str, tmpl: &ChronoPattern) -> Result<Date> {
104    let mut parsed = parse(s, tmpl)?;
105    if let Some(year) = &mut parsed.year
106        && *year < 0
107    {
108        *year += 1;
109    }
110    Ok(parsed.to_naive_date()?.into())
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_to_timestamp_legacy() {
119        for (input, format, expected) in [
122            (
123                "2020-02-03 12:34:56",
124                "yyyy-mm-dd hh24:mi:ss",
125                "2020-02-03 12:34:56",
126            ),
127            (
128                "2020-02-03 12:34:56+03:00",
129                "yyyy-mm-dd hh24:mi:ss tzh:tzm",
130                "2020-02-03 09:34:56",
131            ),
132        ] {
133            let actual = to_timestamp_legacy(input, &ChronoPattern::compile(format)).unwrap();
134            assert_eq!(actual.to_string(), expected);
135        }
136    }
137}