mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-05-25 17:30:41 +00:00
feat: adds date_format function (#3167)
* feat: adds date_format function * fix: compile error * chore: use system timezone for FunctionContext and EvalContext * test: as_formatted_string * test: sqlness test * chore: rename function
This commit is contained in:
@@ -12,16 +12,18 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fmt::{Display, Formatter, Write};
|
||||
use std::str::FromStr;
|
||||
|
||||
use chrono::{Datelike, Days, Months, NaiveDate};
|
||||
use chrono::{Datelike, Days, Months, NaiveDate, NaiveTime, TimeZone};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use snafu::ResultExt;
|
||||
|
||||
use crate::error::{Error, ParseDateStrSnafu, Result};
|
||||
use crate::interval::Interval;
|
||||
use crate::timezone::get_timezone;
|
||||
use crate::Timezone;
|
||||
|
||||
const UNIX_EPOCH_FROM_CE: i32 = 719_163;
|
||||
|
||||
@@ -84,6 +86,40 @@ impl Date {
|
||||
NaiveDate::from_num_days_from_ce_opt(UNIX_EPOCH_FROM_CE + self.0)
|
||||
}
|
||||
|
||||
/// Format Date for given format and timezone.
|
||||
/// If `tz==None`, the server default timezone will used.
|
||||
pub fn as_formatted_string(
|
||||
self,
|
||||
pattern: &str,
|
||||
timezone: Option<&Timezone>,
|
||||
) -> Result<Option<String>> {
|
||||
if let Some(v) = self.to_chrono_date() {
|
||||
// Safety: always success
|
||||
let time = NaiveTime::from_hms_nano_opt(0, 0, 0, 0).unwrap();
|
||||
let v = v.and_time(time);
|
||||
let mut formatted = String::new();
|
||||
|
||||
match get_timezone(timezone) {
|
||||
Timezone::Offset(offset) => {
|
||||
write!(
|
||||
formatted,
|
||||
"{}",
|
||||
offset.from_utc_datetime(&v).format(pattern)
|
||||
)
|
||||
.context(crate::error::FormatSnafu { pattern })?;
|
||||
}
|
||||
Timezone::Named(tz) => {
|
||||
write!(formatted, "{}", tz.from_utc_datetime(&v).format(pattern))
|
||||
.context(crate::error::FormatSnafu { pattern })?;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Some(formatted));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn to_secs(&self) -> i64 {
|
||||
(self.0 as i64) * 24 * 3600
|
||||
}
|
||||
@@ -170,6 +206,37 @@ mod tests {
|
||||
assert_eq!(date, Date::from_str(&date.to_string()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_formatted_string() {
|
||||
let d: Date = 42.into();
|
||||
|
||||
assert_eq!(
|
||||
"1970-02-12",
|
||||
d.as_formatted_string("%Y-%m-%d", None).unwrap().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-02-12 00:00:00",
|
||||
d.as_formatted_string("%Y-%m-%d %H:%M:%S", None)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-02-12T00:00:00:000",
|
||||
d.as_formatted_string("%Y-%m-%dT%H:%M:%S:%3f", None)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-02-12T08:00:00:000",
|
||||
d.as_formatted_string(
|
||||
"%Y-%m-%dT%H:%M:%S:%3f",
|
||||
Some(&Timezone::from_tz_string("Asia/Shanghai").unwrap())
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_from() {
|
||||
let d: Date = 42.into();
|
||||
|
||||
@@ -12,15 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fmt::{Display, Formatter, Write};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::{Days, LocalResult, Months, NaiveDateTime, TimeZone as ChronoTimeZone, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use snafu::ResultExt;
|
||||
|
||||
use crate::error::{Error, InvalidDateStrSnafu, Result};
|
||||
use crate::timezone::Timezone;
|
||||
use crate::timezone::{get_timezone, Timezone};
|
||||
use crate::util::{format_utc_datetime, local_datetime_to_utc};
|
||||
use crate::{Date, Interval};
|
||||
|
||||
@@ -110,7 +111,38 @@ impl DateTime {
|
||||
NaiveDateTime::from_timestamp_millis(self.0)
|
||||
}
|
||||
|
||||
pub fn to_chrono_datetime_with_timezone(&self, tz: Option<Timezone>) -> Option<NaiveDateTime> {
|
||||
/// Format DateTime for given format and timezone.
|
||||
/// If `tz==None`, the server default timezone will used.
|
||||
pub fn as_formatted_string(
|
||||
self,
|
||||
pattern: &str,
|
||||
timezone: Option<&Timezone>,
|
||||
) -> Result<Option<String>> {
|
||||
if let Some(v) = self.to_chrono_datetime() {
|
||||
let mut formatted = String::new();
|
||||
|
||||
match get_timezone(timezone) {
|
||||
Timezone::Offset(offset) => {
|
||||
write!(
|
||||
formatted,
|
||||
"{}",
|
||||
offset.from_utc_datetime(&v).format(pattern)
|
||||
)
|
||||
.context(crate::error::FormatSnafu { pattern })?;
|
||||
}
|
||||
Timezone::Named(tz) => {
|
||||
write!(formatted, "{}", tz.from_utc_datetime(&v).format(pattern))
|
||||
.context(crate::error::FormatSnafu { pattern })?;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Some(formatted));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn to_chrono_datetime_with_timezone(&self, tz: Option<&Timezone>) -> Option<NaiveDateTime> {
|
||||
let datetime = self.to_chrono_datetime();
|
||||
datetime.map(|v| match tz {
|
||||
Some(Timezone::Offset(offset)) => offset.from_utc_datetime(&v).naive_local(),
|
||||
@@ -211,6 +243,38 @@ mod tests {
|
||||
assert_eq!(28800000, ts);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_formatted_string() {
|
||||
let d: DateTime = DateTime::new(1000);
|
||||
|
||||
assert_eq!(
|
||||
"1970-01-01",
|
||||
d.as_formatted_string("%Y-%m-%d", None).unwrap().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01 00:00:01",
|
||||
d.as_formatted_string("%Y-%m-%d %H:%M:%S", None)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01T00:00:01:000",
|
||||
d.as_formatted_string("%Y-%m-%dT%H:%M:%S:%3f", None)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"1970-01-01T08:00:01:000",
|
||||
d.as_formatted_string(
|
||||
"%Y-%m-%dT%H:%M:%S:%3f",
|
||||
Some(&Timezone::from_tz_string("Asia/Shanghai").unwrap())
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_max_date() {
|
||||
let date = Date::new(i32::MAX);
|
||||
|
||||
@@ -68,6 +68,14 @@ pub enum Error {
|
||||
|
||||
#[snafu(display("Invalid timezone string {raw}"))]
|
||||
ParseTimezoneName { raw: String, location: Location },
|
||||
|
||||
#[snafu(display("Failed to format, pattern: {}", pattern))]
|
||||
Format {
|
||||
pattern: String,
|
||||
#[snafu(source)]
|
||||
error: std::fmt::Error,
|
||||
location: Location,
|
||||
},
|
||||
}
|
||||
|
||||
impl ErrorExt for Error {
|
||||
@@ -76,6 +84,7 @@ impl ErrorExt for Error {
|
||||
Error::ParseDateStr { .. }
|
||||
| Error::ParseTimestamp { .. }
|
||||
| Error::InvalidTimezoneOffset { .. }
|
||||
| Error::Format { .. }
|
||||
| Error::ParseOffsetStr { .. }
|
||||
| Error::ParseTimezoneName { .. } => StatusCode::InvalidArguments,
|
||||
Error::TimestampOverflow { .. } => StatusCode::Internal,
|
||||
@@ -93,6 +102,7 @@ impl ErrorExt for Error {
|
||||
fn location_opt(&self) -> Option<common_error::snafu::Location> {
|
||||
match self {
|
||||
Error::ParseTimestamp { location, .. }
|
||||
| Error::Format { location, .. }
|
||||
| Error::TimestampOverflow { location, .. }
|
||||
| Error::ArithmeticOverflow { location, .. } => Some(*location),
|
||||
Error::ParseDateStr { .. }
|
||||
|
||||
@@ -115,11 +115,11 @@ impl Time {
|
||||
|
||||
/// Format Time for given timezone.
|
||||
/// When timezone is None, using system timezone by default.
|
||||
pub fn to_timezone_aware_string(&self, tz: Option<Timezone>) -> String {
|
||||
pub fn to_timezone_aware_string(&self, tz: Option<&Timezone>) -> String {
|
||||
self.as_formatted_string("%H:%M:%S%.f", tz)
|
||||
}
|
||||
|
||||
fn as_formatted_string(self, pattern: &str, timezone: Option<Timezone>) -> String {
|
||||
fn as_formatted_string(self, pattern: &str, timezone: Option<&Timezone>) -> String {
|
||||
if let Some(time) = self.to_chrono_time() {
|
||||
let date = Utc::now().date_naive();
|
||||
let datetime = NaiveDateTime::new(date, time);
|
||||
@@ -380,37 +380,39 @@ mod tests {
|
||||
assert_eq!(
|
||||
"08:00:00.001",
|
||||
Time::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("SYSTEM").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("SYSTEM").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"08:00:00.001",
|
||||
Time::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("+08:00").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("+08:00").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"07:00:00.001",
|
||||
Time::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("+07:00").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("+07:00").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"23:00:00.001",
|
||||
Time::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("-01:00").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("-01:00").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"08:00:00.001",
|
||||
Time::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("Asia/Shanghai").unwrap()))
|
||||
Time::new(1, TimeUnit::Millisecond).to_timezone_aware_string(Some(
|
||||
&Timezone::from_tz_string("Asia/Shanghai").unwrap()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
"00:00:00.001",
|
||||
Time::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("UTC").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("UTC").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"03:00:00.001",
|
||||
Time::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("Europe/Moscow").unwrap()))
|
||||
Time::new(1, TimeUnit::Millisecond).to_timezone_aware_string(Some(
|
||||
&Timezone::from_tz_string("Europe/Moscow").unwrap()
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
use core::default::Default;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fmt::{Display, Formatter, Write};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
@@ -26,7 +26,9 @@ use chrono::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use snafu::{OptionExt, ResultExt};
|
||||
|
||||
use crate::error::{ArithmeticOverflowSnafu, Error, ParseTimestampSnafu, TimestampOverflowSnafu};
|
||||
use crate::error::{
|
||||
ArithmeticOverflowSnafu, Error, ParseTimestampSnafu, Result, TimestampOverflowSnafu,
|
||||
};
|
||||
use crate::timezone::{get_timezone, Timezone};
|
||||
use crate::util::div_ceil;
|
||||
use crate::{error, Interval};
|
||||
@@ -290,32 +292,50 @@ impl Timestamp {
|
||||
/// Format timestamp to ISO8601 string. If the timestamp exceeds what chrono timestamp can
|
||||
/// represent, this function simply print the timestamp unit and value in plain string.
|
||||
pub fn to_iso8601_string(&self) -> String {
|
||||
// Safety: the format is valid
|
||||
self.as_formatted_string("%Y-%m-%d %H:%M:%S%.f%z", None)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Format timestamp use **system timezone**.
|
||||
pub fn to_local_string(&self) -> String {
|
||||
// Safety: the format is valid
|
||||
self.as_formatted_string("%Y-%m-%d %H:%M:%S%.f", None)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Format timestamp for given timezone.
|
||||
/// If `tz==None`, the server default timezone will used.
|
||||
pub fn to_timezone_aware_string(&self, tz: Option<Timezone>) -> String {
|
||||
pub fn to_timezone_aware_string(&self, tz: Option<&Timezone>) -> String {
|
||||
// Safety: the format is valid
|
||||
self.as_formatted_string("%Y-%m-%d %H:%M:%S%.f", tz)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn as_formatted_string(self, pattern: &str, timezone: Option<Timezone>) -> String {
|
||||
/// Format timestamp for given format and timezone.
|
||||
/// If `tz==None`, the server default timezone will used.
|
||||
pub fn as_formatted_string(self, pattern: &str, timezone: Option<&Timezone>) -> Result<String> {
|
||||
if let Some(v) = self.to_chrono_datetime() {
|
||||
let mut formatted = String::new();
|
||||
|
||||
match get_timezone(timezone) {
|
||||
Timezone::Offset(offset) => {
|
||||
format!("{}", offset.from_utc_datetime(&v).format(pattern))
|
||||
write!(
|
||||
formatted,
|
||||
"{}",
|
||||
offset.from_utc_datetime(&v).format(pattern)
|
||||
)
|
||||
.context(crate::error::FormatSnafu { pattern })?;
|
||||
}
|
||||
Timezone::Named(tz) => {
|
||||
format!("{}", tz.from_utc_datetime(&v).format(pattern))
|
||||
write!(formatted, "{}", tz.from_utc_datetime(&v).format(pattern))
|
||||
.context(crate::error::FormatSnafu { pattern })?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(formatted)
|
||||
} else {
|
||||
format!("[Timestamp{}: {}]", self.unit, self.value)
|
||||
Ok(format!("[Timestamp{}: {}]", self.unit, self.value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,7 +344,7 @@ impl Timestamp {
|
||||
NaiveDateTime::from_timestamp_opt(sec, nsec)
|
||||
}
|
||||
|
||||
pub fn to_chrono_datetime_with_timezone(&self, tz: Option<Timezone>) -> Option<NaiveDateTime> {
|
||||
pub fn to_chrono_datetime_with_timezone(&self, tz: Option<&Timezone>) -> Option<NaiveDateTime> {
|
||||
let datetime = self.to_chrono_datetime();
|
||||
datetime.map(|v| match tz {
|
||||
Some(Timezone::Offset(offset)) => offset.from_utc_datetime(&v).naive_local(),
|
||||
@@ -369,7 +389,7 @@ impl FromStr for Timestamp {
|
||||
/// - `2022-09-20 14:16:43` (Zulu timezone, without T)
|
||||
/// - `2022-09-20 14:16:43.012345` (Zulu timezone, without T)
|
||||
#[allow(deprecated)]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
// RFC3339 timestamp (with a T)
|
||||
let s = s.trim();
|
||||
if let Ok(ts) = DateTime::parse_from_rfc3339(s) {
|
||||
@@ -1113,47 +1133,77 @@ mod tests {
|
||||
assert_eq!(
|
||||
"1970-01-01 08:00:00.001",
|
||||
Timestamp::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("SYSTEM").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("SYSTEM").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01 08:00:00.001",
|
||||
Timestamp::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("SYSTEM").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("SYSTEM").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01 08:00:00.001",
|
||||
Timestamp::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("+08:00").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("+08:00").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01 07:00:00.001",
|
||||
Timestamp::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("+07:00").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("+07:00").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"1969-12-31 23:00:00.001",
|
||||
Timestamp::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("-01:00").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("-01:00").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01 08:00:00.001",
|
||||
Timestamp::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("Asia/Shanghai").unwrap()))
|
||||
Timestamp::new(1, TimeUnit::Millisecond).to_timezone_aware_string(Some(
|
||||
&Timezone::from_tz_string("Asia/Shanghai").unwrap()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01 00:00:00.001",
|
||||
Timestamp::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("UTC").unwrap()))
|
||||
.to_timezone_aware_string(Some(&Timezone::from_tz_string("UTC").unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01 01:00:00.001",
|
||||
Timestamp::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("Europe/Berlin").unwrap()))
|
||||
Timestamp::new(1, TimeUnit::Millisecond).to_timezone_aware_string(Some(
|
||||
&Timezone::from_tz_string("Europe/Berlin").unwrap()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01 03:00:00.001",
|
||||
Timestamp::new(1, TimeUnit::Millisecond)
|
||||
.to_timezone_aware_string(Some(Timezone::from_tz_string("Europe/Moscow").unwrap()))
|
||||
Timestamp::new(1, TimeUnit::Millisecond).to_timezone_aware_string(Some(
|
||||
&Timezone::from_tz_string("Europe/Moscow").unwrap()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_formatted_string() {
|
||||
let ts = Timestamp::new(1, TimeUnit::Millisecond);
|
||||
|
||||
assert_eq!(
|
||||
"1970-01-01",
|
||||
ts.as_formatted_string("%Y-%m-%d", None).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01 00:00:00",
|
||||
ts.as_formatted_string("%Y-%m-%d %H:%M:%S", None).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01T00:00:00:001",
|
||||
ts.as_formatted_string("%Y-%m-%dT%H:%M:%S:%3f", None)
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"1970-01-01T08:00:00:001",
|
||||
ts.as_formatted_string(
|
||||
"%Y-%m-%dT%H:%M:%S:%3f",
|
||||
Some(&Timezone::from_tz_string("Asia/Shanghai").unwrap())
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,8 @@ pub fn set_default_timezone(tz_str: Option<&str>) -> Result<()> {
|
||||
#[inline(always)]
|
||||
/// If the `tz=Some(timezone)`, return `timezone` directly,
|
||||
/// or return current system timezone.
|
||||
pub fn get_timezone(tz: Option<Timezone>) -> Timezone {
|
||||
tz.unwrap_or_else(|| {
|
||||
DEFAULT_TIMEZONE
|
||||
.get()
|
||||
.cloned()
|
||||
.unwrap_or(Timezone::Named(Tz::UTC))
|
||||
})
|
||||
pub fn get_timezone(tz: Option<&Timezone>) -> &Timezone {
|
||||
tz.unwrap_or_else(|| DEFAULT_TIMEZONE.get().unwrap_or(&Timezone::Named(Tz::UTC)))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
Reference in New Issue
Block a user