use crate::common::*;
use crate::error::DateTimeError;
use crate::interval::*;
use crate::token::*;
use crate::{DateOrder, IntervalStyle};
use std::convert::TryFrom;
#[inline]
fn eat_whitespaces(s: &[u8]) -> &[u8] {
let count = s.iter().take_while(|&i| i.is_ascii_whitespace()).count();
&s[count..]
}
#[inline]
fn eat_digits(s: &[u8]) -> (&[u8], &[u8]) {
let count = s.iter().take_while(|&i| i.is_ascii_digit()).count();
(&s[0..count], &s[count..])
}
#[inline]
fn eat_alphabetics(s: &[u8]) -> (&[u8], &[u8]) {
let count = s.iter().take_while(|&i| i.is_ascii_alphabetic()).count();
(&s[0..count], &s[count..])
}
#[inline]
fn eat_digits_and_chars(s: &[u8], c: u8) -> (&[u8], &[u8]) {
let count = s
.iter()
.take_while(|&&i| i == c || i.is_ascii_digit())
.count();
(&s[0..count], &s[count..])
}
#[inline]
fn eat_alphanumerics_and_chars(s: &[u8], c: u8) -> (&[u8], &[u8]) {
let count = s
.iter()
.take_while(|&&i| i == c || i.is_ascii_alphanumeric())
.count();
(&s[0..count], &s[count..])
}
#[inline]
fn eat_special_chars<F>(s: &[u8], f: F) -> (&[u8], &[u8])
where
F: Fn(u8) -> bool,
{
let count = s.iter().take_while(|&&i| f(i)).count();
(&s[0..count], &s[count..])
}
#[inline]
fn eat_time(s: &[u8]) -> (&[u8], &[u8]) {
let count = s
.iter()
.take_while(|&&i| i.is_ascii_digit() || i == b':' || i == b'.')
.count();
(&s[0..count], &s[count..])
}
#[inline]
fn extract_sign(s: &[u8]) -> (i32, &[u8]) {
match s.first() {
Some(b'+') => (1, &s[1..]),
Some(b'-') => (-1, &s[1..]),
_ => (1, s),
}
}
#[inline]
fn parse_date_time_for_digit_delim_begin<'a, 'b>(
s: &'a [u8],
current_buf: &'b mut [u8],
f_types: &mut [TokenField],
buf_pos: &mut usize,
nf: usize,
) -> &'a [u8] {
let delim = s.first().unwrap();
f_types[nf] = if delim == &b'.' {
TokenField::Number
} else {
TokenField::Date
};
current_buf[*buf_pos] = *delim;
let s = &s[1..];
*buf_pos += 1;
match s.first() {
Some(c) => {
if c.is_ascii_digit() {
let (field_str, s) = eat_digits(s);
*buf_pos = copy_slice(current_buf, *buf_pos, field_str);
match s.first() {
Some(c) => {
if c == delim {
f_types[nf] = TokenField::Date;
let (field_str, s) = eat_digits_and_chars(s, *delim);
*buf_pos = copy_slice(current_buf, *buf_pos, field_str);
s
} else {
s
}
}
None => s,
}
} else {
f_types[nf] = TokenField::Date;
let (field_str, s) = eat_alphanumerics_and_chars(s, *delim);
let lower_str = field_str.to_ascii_lowercase();
*buf_pos = copy_slice(current_buf, *buf_pos, lower_str.as_ref());
s
}
}
None => s,
}
}
#[inline]
fn parse_date_time_for_digit_begin<'a, 'b, 'c>(
s: &'a [u8],
current_buf: &'b mut [u8],
fields: &'c mut [&'b [u8]],
f_types: &mut [TokenField],
nf: usize,
) -> (&'a [u8], &'b mut [u8]) {
let (field_part0, s) = eat_digits(s);
let mut buf_pos = copy_slice(current_buf, 0, field_part0);
let s = match s.first() {
Some(&b':') => {
let (field_str, s) = eat_time(s);
buf_pos = copy_slice(current_buf, buf_pos, field_str);
f_types[nf] = TokenField::Time;
s
}
Some(&b'-') | Some(&b'/') | Some(&b'.') => {
parse_date_time_for_digit_delim_begin(s, current_buf, f_types, &mut buf_pos, nf)
}
_ => {
f_types[nf as usize] = TokenField::Number;
s
}
};
let (field, current_buf) = current_buf.split_at_mut(buf_pos);
fields[nf as usize] = field;
(s, current_buf)
}
#[inline]
fn parse_date_time_for_dot_begin<'a, 'b, 'c>(
s: &'a [u8],
current_buf: &'b mut [u8],
fields: &'c mut [&'b [u8]],
f_types: &mut [TokenField],
nf: usize,
) -> (&'a [u8], &'b mut [u8]) {
f_types[nf] = TokenField::Number;
current_buf[0] = s[0];
let s = &s[1..];
let mut buf_pos = 1;
let (field_str, s) = eat_digits(s);
buf_pos = copy_slice(current_buf, buf_pos, field_str);
let (filed, ret_buf) = current_buf.split_at_mut(buf_pos);
fields[nf] = filed;
(s, ret_buf)
}
fn parse_date_time_for_alpha_begin<'a, 'b, 'c>(
s: &'a [u8],
current_buf: &'b mut [u8],
fields: &'c mut [&'b [u8]],
f_types: &mut [TokenField],
nf: usize,
) -> (&'a [u8], &'b mut [u8]) {
f_types[nf] = TokenField::String;
let (field_str, s) = eat_alphabetics(s);
let field_str = field_str.to_ascii_lowercase();
let mut buf_pos = copy_slice(current_buf, 0, field_str.as_ref());
let is_date = match s.first() {
Some(c) => {
if c == &b'-' || c == &b'/' || c == &b'.' {
true
} else if c == &b'+' || c.is_ascii_digit() {
let ret = search_date_token(field_str.as_ref());
ret.is_none()
} else {
false
}
}
None => false,
};
let s = if is_date {
f_types[nf] = TokenField::Date;
let (field_str, s) = eat_special_chars(s, |i| {
i == b'+'
|| i == b'-'
|| i == b'/'
|| i == b'_'
|| i == b'.'
|| i == b':'
|| i.is_ascii_alphanumeric()
});
buf_pos = copy_slice(current_buf, buf_pos, field_str);
s
} else {
s
};
let (filed, ret_buf) = current_buf.split_at_mut(buf_pos);
fields[nf] = filed;
(s, ret_buf)
}
#[inline]
fn parse_date_time_for_sign_begin<'a, 'b, 'c>(
s: &'a [u8],
current_buf: &'b mut [u8],
fields: &'c mut [&'b [u8]],
f_types: &mut [TokenField],
nf: usize,
) -> Result<(&'a [u8], &'b mut [u8]), DateTimeError> {
current_buf[0] = s[0];
let s = &s[1..];
let s = eat_whitespaces(s);
let mut buf_pos = 1;
let s = match s.first() {
Some(c) => {
if c.is_ascii_digit() {
f_types[nf] = TokenField::Tz;
let (field_str, s) = eat_special_chars(s, |i| {
i.is_ascii_digit() || i == b':' || i == b'.' || i == b'-'
});
buf_pos = copy_slice(current_buf, buf_pos, field_str);
s
} else if c.is_ascii_alphabetic() {
f_types[nf] = TokenField::Special;
let (field_str, s) = eat_alphabetics(s);
let save_buf_pos = buf_pos;
buf_pos = copy_slice(current_buf, buf_pos, field_str);
let buf = &mut current_buf[save_buf_pos..save_buf_pos + field_str.len()];
buf.make_ascii_lowercase();
s
} else {
return Err(DateTimeError::invalid(format!(
"date time expect digit or alpha after sign, but it is :{:?}",
c
)));
}
}
None => s,
};
let (filed, ret_buf) = current_buf.split_at_mut(buf_pos);
fields[nf] = filed;
Ok((s, ret_buf))
}
pub fn parse_date_time<'a, 'b>(
time_str: &[u8],
worker_buf: &'a mut [u8],
fields: &'b mut [&'a [u8]],
f_types: &mut [TokenField],
max_fields: usize,
) -> Result<usize, DateTimeError>
where
'a: 'b,
{
let mut field_num: usize = 0;
if time_str.is_empty() {
return Err(DateTimeError::empty(
"date time string is empty".to_string(),
));
}
let mut current_str = time_str;
let mut current_buf = worker_buf;
loop {
current_str = eat_whitespaces(current_str);
let first_char = current_str.first();
match first_char {
Some(char) => {
if field_num >= max_fields {
return Err(DateTimeError::invalid(format!(
"field number: {:?} exceed max field number: {:?} at field: {:?}",
field_num, max_fields, time_str
)));
}
if char.is_ascii_digit() {
let (ret_str, ret_buf) = parse_date_time_for_digit_begin(
current_str,
current_buf,
fields,
f_types,
field_num,
);
current_str = ret_str;
current_buf = ret_buf;
} else if char == &b'.' {
let (ret_str, ret_buf) = parse_date_time_for_dot_begin(
current_str,
current_buf,
fields,
f_types,
field_num,
);
current_str = ret_str;
current_buf = ret_buf;
} else if char.is_ascii_alphabetic() {
let (ret_str, ret_buf) = parse_date_time_for_alpha_begin(
current_str,
current_buf,
fields,
f_types,
field_num,
);
current_str = ret_str;
current_buf = ret_buf;
} else if *char == b'+' || *char == b'-' {
let (ret_str, ret_buf) = parse_date_time_for_sign_begin(
current_str,
current_buf,
fields,
f_types,
field_num,
)?;
current_str = ret_str;
current_buf = ret_buf;
} else if char.is_ascii_punctuation() {
let (_field_str, s) =
eat_special_chars(current_str, |i| i.is_ascii_punctuation());
current_str = s;
continue;
} else {
return Err(DateTimeError::invalid(format!(
"char: {:?} is not expected at str: {:?}",
char, time_str
)));
}
}
None => break,
}
field_num += 1;
}
Ok(field_num)
}
#[inline]
fn str2i32(s: &[u8]) -> Result<(i32, &[u8]), DateTimeError> {
let s = eat_whitespaces(s);
if s.is_empty() {
return Ok((0, s));
}
let (sign, s) = extract_sign(s);
let (integral, s) = eat_digits(s);
if integral.len() > 10 {
return Err(DateTimeError::overflow());
}
let val = integral
.iter()
.fold(0_i64, |product, &i| product * 10 + (i - b'0') as i64);
let val = val * sign as i64;
if val >= i32::min_value() as i64 && val <= i32::max_value() as i64 {
Ok((val as i32, s))
} else {
Err(DateTimeError::overflow())
}
}
#[inline]
fn str2d(s: &[u8]) -> Result<(f64, &[u8]), DateTimeError> {
let s = eat_whitespaces(s);
if s.is_empty() {
return Ok((0.0, s));
}
let (sign, s) = extract_sign(s);
let (integral, s) = eat_digits(s);
if integral.len() > 10 {
return Err(DateTimeError::overflow());
}
let (fractional, s) = match s.first() {
Some(b'.') => {
let (fractional, s) = eat_digits(&s[1..]);
(fractional, s)
}
_ => (b"".as_ref(), s),
};
if fractional.len() > 7 {
return Err(DateTimeError::overflow());
}
let int = integral
.iter()
.fold(0, |val, &i| val * 10 + (i - b'0') as i64);
let val = if !fractional.is_empty() {
let float = fractional
.iter()
.fold(int, |val, &i| val * 10 + (i - b'0') as i64);
let div = 10_i64.pow(fractional.len() as u32);
(float * sign as i64) as f64 / div as f64
} else {
(int * sign as i64) as f64
};
Ok((val, s))
}
fn decode_timezone(_input: &[u8], _tz: &mut Option<i32>) -> Result<(), DateTimeError> {
Err(DateTimeError::invalid(
"timezone is not supported at current".to_string(),
))
}
fn decode_number_field(
str: &[u8],
f_mask: i32,
t_mask: &mut i32,
tm: &mut PgTime,
fsec: &mut i64,
is2digits: &mut bool,
) -> Result<TokenField, DateTimeError> {
let mut len = str.len();
let count = str.iter().take_while(|&i| i != &b'.').count();
if str.len() != count {
let s = &str[count..];
let (v, _left) = str2d(s)?;
let v = (v * 1_000_000_f64).round() as i64;
*fsec = v;
len = count;
}
else if (f_mask & DTK_DATE_M) != DTK_DATE_M && len >= 6 {
*t_mask = DTK_DATE_M;
let (v, _) = str2i32(&str[len - 2..])?;
tm.mday = v;
let (v, _) = str2i32(&str[len - 4..len - 2])?;
tm.mon = v;
let (v, _) = str2i32(&str[0..len - 4])?;
tm.year = v;
if (len - 4) == 2 {
*is2digits = true;
}
return Ok(TokenField::Date);
}
if (f_mask & DTK_TIME_M) != DTK_TIME_M {
if len == 6 {
*t_mask = DTK_TIME_M;
let (v, _) = str2i32(&str[4..])?;
tm.sec = v;
let (v, _) = str2i32(&str[2..4])?;
tm.min = v;
let (v, _) = str2i32(&str[0..2])?;
tm.hour = v;
return Ok(TokenField::Time);
}
else if len == 4 {
*t_mask = DTK_TIME_M;
tm.sec = 0;
let (v, _) = str2i32(&str[2..])?;
tm.min = v;
let (v, _) = str2i32(&str[0..2])?;
tm.hour = v;
return Ok(TokenField::Time);
}
}
Err(DateTimeError::invalid(format!(
"field: {:?} is invalid number field",
str
)))
}
thread_local!(static DATE_CACHE: RefCell<[Option<&'static DateToken>; MAX_DATE_FIELDS]> = RefCell::new([None; MAX_DATE_FIELDS]));
#[inline]
fn decode_special(field: usize, low_token: &[u8]) -> (TokenType, i32) {
let tp = {
let tp = DATE_CACHE.with(|tokens| tokens.borrow_mut()[field]);
let key = if low_token.len() > MAX_TOKEN_LEN {
&low_token[0..MAX_TOKEN_LEN]
} else {
low_token
};
if tp.is_none() || key != tp.unwrap().token {
search_date_token(key)
} else {
tp
}
};
match tp {
None => (TokenType::UnknownField, 0),
Some(t) => {
DATE_CACHE.with(|tokens| tokens.borrow_mut()[field] = tp);
(t.ty, t.value)
}
}
}
#[inline]
fn parse_fractional_second(s: &[u8]) -> Result<i64, DateTimeError> {
debug_assert!(s[0] == b'.');
let (frac, s) = str2d(s)?;
match s.first() {
None => {}
Some(_) => {
return Err(DateTimeError::invalid(format!(
"after double, it also have str: {:?}",
s
)));
}
}
let fsec = (frac * 1_000_000.0).round() as i64;
Ok(fsec)
}
#[allow(clippy::too_many_arguments)]
fn decode_number(
str: &[u8],
have_text_month: bool,
f_mask: i32,
t_mask: &mut i32,
tm: &mut PgTime,
fsec: &mut i64,
is2digits: &mut bool,
date_order: DateOrder,
) -> Result<(), DateTimeError> {
*t_mask = 0;
let (val, s) = str2i32(str)?;
if s == str {
return Err(DateTimeError::invalid(format!(
"str: {:?} expect have digit",
s
)));
}
match s.first() {
Some(&b'.') => {
if str.len() - s.len() > 2 {
let _ = decode_number_field(str, f_mask | DTK_DATE_M, t_mask, tm, fsec, is2digits);
return Ok(());
}
*fsec = parse_fractional_second(s)?;
}
Some(c) => {
return Err(DateTimeError::invalid(format!(
"char: {:?} is not expect after digit",
c
)));
}
None => {}
};
if str.len() == 3 && (f_mask & DTK_DATE_M) == TokenType::YEAR.mask() && val >= 1 && val <= 366 {
*t_mask = TokenType::DOY.mask() | TokenType::MONTH.mask() | TokenType::DAY.mask();
tm.yday = val;
return Ok(());
}
match f_mask & DTK_DATE_M {
0 => {
if str.len() >= 3 || date_order == DateOrder::YMD {
*t_mask = TokenType::YEAR.mask();
tm.year = val;
} else if date_order == DateOrder::DMY {
*t_mask = TokenType::DAY.mask();
tm.mday = val;
} else {
*t_mask = TokenType::MONTH.mask();
tm.mon = val;
}
}
DTK_YEAR_M => {
*t_mask = TokenType::MONTH.mask();
tm.mon = val;
}
DTK_MONTH_M => {
if have_text_month {
if str.len() >= 3 || date_order == DateOrder::YMD {
*t_mask = TokenType::YEAR.mask();
tm.year = val;
} else {
*t_mask = TokenType::DAY.mask();
tm.mday = val;
}
} else {
*t_mask = TokenType::DAY.mask();
tm.mday = val;
}
}
DTK_YEAR_MONTH_M => {
if have_text_month {
if str.len() >= 3 && *is2digits {
*t_mask = TokenType::DAY.mask(); tm.mday = tm.year;
tm.year = val;
*is2digits = false;
} else {
*t_mask = TokenType::DAY.mask();
tm.mday = val;
}
} else {
*t_mask = TokenType::DAY.mask();
tm.mday = val;
}
}
DTK_DAY_M => {
*t_mask = TokenType::MONTH.mask();
tm.mon = val;
}
DTK_MONTH_DAY_M => {
*t_mask = TokenType::YEAR.mask();
tm.year = val;
}
DTK_DATE_M => {
let _ = decode_number_field(str, f_mask, t_mask, tm, fsec, is2digits)?;
return Ok(());
}
_ => {
return Err(DateTimeError::invalid(format!(
"field mask: {:x} is not expect",
f_mask & DTK_DATE_M
)));
}
}
if *t_mask == TokenType::YEAR.mask() {
*is2digits = str.len() <= 2;
}
Ok(())
}
fn decode_date(
str: &[u8],
f_mask: i32,
t_mask: &mut i32,
is2digits: &mut bool,
tm: &mut PgTime,
date_order: DateOrder,
) -> Result<(), DateTimeError> {
let mut have_text_month = false;
let mut f_mask = f_mask;
let mut nf = 0;
let mut fields: [Option<&[u8]>; MAX_DATE_FIELDS] = [None; MAX_DATE_FIELDS];
let mut d_mask = 0;
let mut fsec = 0;
*t_mask = 0;
let mut curr_s = str;
while !curr_s.is_empty() && nf < MAX_DATE_FIELDS {
let (_, s) = eat_special_chars(curr_s, |i| !i.is_ascii_alphanumeric());
curr_s = match s.first() {
None => {
return Err(DateTimeError::invalid(
"date end with special char".to_string(),
));
}
Some(c) => {
if c.is_ascii_digit() {
let (f, p) = eat_digits(s);
fields[nf] = Some(f);
p
} else if c.is_ascii_alphabetic() {
let (f, p) = eat_alphabetics(s);
fields[nf] = Some(f);
p
} else {
continue;
}
}
};
nf += 1;
}
let ret_fields = &mut fields[0..nf];
for (i, field) in ret_fields.iter_mut().enumerate() {
if let Some(f) = field {
if let Some(c) = f.first() {
if c.is_ascii_alphabetic() {
let (ty, val) = decode_special(i, f);
if ty == TokenType::IgnoreDtf {
continue;
}
let d_mask = ty.mask();
match ty {
TokenType::MONTH => {
tm.mon = val;
have_text_month = true;
}
_ => {
return Err(DateTimeError::invalid(format!(
"{:?} is not month in date",
f
)));
}
}
if f_mask & d_mask != 0 {
return Err(DateTimeError::invalid(format!(
"month appear more than one times, current month: {:?}",
f
)));
}
f_mask |= d_mask;
*t_mask |= d_mask;
*field = None;
}
}
}
}
for field in ret_fields.iter() {
match field {
None => continue,
Some(f) => {
if f.is_empty() {
return Err(DateTimeError::invalid("date field is empty".to_string()));
}
decode_number(
f,
have_text_month,
f_mask,
&mut d_mask,
tm,
&mut fsec,
is2digits,
date_order,
)?;
if f_mask & d_mask != 0 {
return Err(DateTimeError::invalid(format!(
"date mask: {:x} appear more than one times",
d_mask
)));
}
f_mask |= d_mask;
*t_mask |= d_mask;
}
}
}
if (f_mask & !(TokenType::DOY.mask() | TokenType::TZ.mask())) != DTK_DATE_M {
return Err(DateTimeError::invalid(format!(
"field mask: {:x} is invalid",
f_mask
)));
}
Ok(())
}
fn decode_time(
field: &[u8],
_f_mask: i32,
range: i32,
t_mask: &mut i32,
tm: &mut PgTime,
f_sec: &mut i64,
) -> Result<(), DateTimeError> {
*t_mask = DTK_TIME_M;
let (v, s) = str2i32(field)?;
tm.hour = v;
match s.first() {
Some(b':') => {}
_ => {
return Err(DateTimeError::invalid(format!(
"field: {:?} is invalid time",
field
)));
}
}
let (v, s) = str2i32(&s[1..])?;
tm.min = v;
match s.first() {
None => {
tm.sec = 0;
*f_sec = 0;
if range == INTERVAL_MASK_MS {
tm.sec = tm.min;
tm.min = tm.hour;
tm.hour = 0;
}
}
Some(b'.') => {
*f_sec = parse_fractional_second(s)?;
tm.sec = tm.min;
tm.min = tm.hour;
tm.hour = 0;
}
Some(b':') => {
let (v, s) = str2i32(&s[1..])?;
tm.sec = v;
match s.first() {
None => {
*f_sec = 0;
}
Some(b'.') => {
*f_sec = parse_fractional_second(s)?;
}
Some(c) => {
return Err(DateTimeError::invalid(format!(
"field:{:?} is invalid time for have unexpected char: {:?}",
field, c
)));
}
}
}
Some(_) => {
return Err(DateTimeError::invalid(format!(
"field: {:?} is invalid at time",
field
)));
}
}
if tm.hour < 0
|| tm.min < 0
|| tm.min > MINS_PER_HOUR - 1
|| tm.sec < 0
|| tm.sec > SECS_PER_MINUTE
|| *f_sec < 0
|| *f_sec > USECS_PER_SEC
{
Err(DateTimeError::invalid(format!("time: {:?} is invalid", tm)))
} else {
Ok(())
}
}
#[inline]
fn date2time(jd: i64, hour: &mut i32, min: &mut i32, sec: &mut i32, fsec: &mut i64) {
let mut time = jd;
*hour = (time / USECS_PER_HOUR) as i32;
time -= (*hour as i64) * USECS_PER_HOUR;
*min = (time / USECS_PER_MINUTE) as i32;
time -= (*min as i64) * USECS_PER_MINUTE;
*sec = (time / USECS_PER_SEC) as i32;
*fsec = time - ((*sec as i64) * USECS_PER_SEC);
}
#[inline]
fn timestamp_modulo(t: i64, per_unit: i64) -> (i64, i64) {
let q = t / per_unit;
let v = if q != 0 { t - q * per_unit } else { t };
(v, q)
}
pub fn timestamp2time(
dt: i64,
tz: &Option<&mut i32>,
tm: &mut PgTime,
fsec: &mut i64,
tzn: &mut Option<&[u8]>,
_at_time_zone: &Option<&mut PgTimezone>,
) -> Result<(), DateTimeError> {
let (mut time, mut date) = timestamp_modulo(dt, USECS_PER_DAY);
if time < 0 {
time += USECS_PER_DAY;
date -= 1;
}
date += POSTGRES_EPOCH_JDATE as i64;
if date < 0 || date > std::i32::MAX as i64 {
return Err(DateTimeError::overflow());
}
let (y, m, d) = julian2date(date as i32);
tm.set_ymd(y, m, d);
date2time(time, &mut tm.hour, &mut tm.min, &mut tm.sec, fsec);
match tz {
None => {
tm.isdst = -1;
tm.gmtoff = 0;
tm.zone = None;
*tzn = None;
}
Some(_) => {}
}
Ok(())
}
const PG_TIME_ZONE: PgTimezone = PgTimezone::new();
#[inline]
fn decode_timezone_abbrev(
_field: usize,
_low_token: &[u8],
_offset: &mut i32,
) -> (TokenType, &'static PgTimezone) {
(TokenType::UnknownField, &PG_TIME_ZONE)
}
#[inline]
fn pg_time_zone_set(_s: &[u8]) -> Result<Option<PgTimezone>, DateTimeError> {
Ok(None)
}
#[inline]
fn check_mask_set(f_mask: i32, t_mask: i32) -> Result<(), DateTimeError> {
if t_mask & f_mask != 0 {
Err(DateTimeError::invalid(format!(
"time mask: {:x} have been set at field mask: {:x}",
t_mask, f_mask
)))
} else {
Ok(())
}
}
#[inline]
fn check_mask_not_empty(f_mask: i32) -> Result<(), DateTimeError> {
if f_mask == 0 {
Err(DateTimeError::invalid(
"field mask of interval is empty".to_string(),
))
} else {
Ok(())
}
}
struct DecodeDateTimeCtx {
d_type: TokenField,
f_sec: i64,
f_mask: i32,
t_mask: i32,
p_type: TokenField,
is_julian: bool,
is2digits: bool,
bc: bool,
mer: i32,
have_text_month: bool,
}
impl DecodeDateTimeCtx {
#[inline]
fn new() -> Self {
Self {
d_type: TokenField::Date,
f_sec: 0,
f_mask: 0,
t_mask: 0,
p_type: TokenField::Number,
is_julian: false,
is2digits: false,
bc: false,
mer: HR24,
have_text_month: false,
}
}
fn decode_date(
&mut self,
str: &[u8],
tm: &mut PgTime,
date_order: DateOrder,
) -> Result<(), DateTimeError> {
let mut have_text_month = false;
let mut f_mask = self.f_mask;
let mut nf = 0;
let mut fields: [Option<&[u8]>; MAX_DATE_FIELDS] = [None; MAX_DATE_FIELDS];
let mut d_mask = 0;
let mut fsec = 0;
self.t_mask = 0;
let mut curr_s = str;
while !curr_s.is_empty() && nf < MAX_DATE_FIELDS {
let (_, s) = eat_special_chars(curr_s, |i| !i.is_ascii_alphanumeric());
curr_s = match s.first() {
None => {
return Err(DateTimeError::invalid(
"date end with special char".to_string(),
));
}
Some(c) => {
if c.is_ascii_digit() {
let (f, p) = eat_digits(s);
fields[nf] = Some(f);
p
} else if c.is_ascii_alphabetic() {
let (f, p) = eat_alphabetics(s);
fields[nf] = Some(f);
p
} else {
continue;
}
}
};
nf += 1;
}
let ret_fields = &mut fields[0..nf];
for (i, field) in ret_fields.iter_mut().enumerate() {
if let Some(f) = field {
if let Some(c) = f.first() {
if c.is_ascii_alphabetic() {
let (ty, val) = decode_special(i, f);
if ty == TokenType::IgnoreDtf {
continue;
}
let d_mask = ty.mask();
match ty {
TokenType::MONTH => {
tm.mon = val;
have_text_month = true;
}
_ => {
return Err(DateTimeError::invalid(format!(
"{:?} is not month in date",
f
)));
}
}
if f_mask & d_mask != 0 {
return Err(DateTimeError::invalid(format!(
"month appear more than one times, current month: {:?}",
f
)));
}
f_mask |= d_mask;
self.t_mask |= d_mask;
*field = None;
}
}
}
}
for field in ret_fields.iter() {
match field {
None => continue,
Some(f) => {
if f.is_empty() {
return Err(DateTimeError::invalid("date field is empty".to_string()));
}
decode_number(
f,
have_text_month,
f_mask,
&mut d_mask,
tm,
&mut fsec,
&mut self.is2digits,
date_order,
)?;
if f_mask & d_mask != 0 {
return Err(DateTimeError::invalid(format!(
"date mask: {:x} appear more than one times",
d_mask
)));
}
f_mask |= d_mask;
self.t_mask |= d_mask;
}
}
}
if (f_mask & !(TokenType::DOY.mask() | TokenType::TZ.mask())) != DTK_DATE_M {
return Err(DateTimeError::invalid(format!(
"field mask: {:x} is invalid",
f_mask
)));
}
Ok(())
}
fn decode_date_token(
&mut self,
field: &[u8],
tm: &mut PgTime,
tz: &mut Option<i32>,
date_order: DateOrder,
) -> Result<(), DateTimeError> {
if self.p_type == TokenField::Julian {
let (val, s) = str2i32(field)?;
let (y, m, d) = julian2date(val);
tm.set_ymd(y, m, d);
self.is_julian = true;
decode_timezone(s, tz)?;
}
else if self.p_type != TokenField::Number
|| (self.f_mask & (TokenType::MONTH.mask() | TokenType::DAY.mask()))
== (TokenType::MONTH.mask() | TokenType::DAY.mask())
{
if let Some(c) = field.first() {
if c.is_ascii_digit() || self.p_type != TokenField::Number {
if self.p_type != TokenField::Number {
if self.p_type != TokenField::Time {
return Err(DateTimeError::invalid(format!(
"prefix type: {:?} is not time",
self.p_type
)));
} else {
self.p_type = TokenField::Number;
}
if (self.f_mask & DTK_TIME_M) == DTK_TIME_M {
return Err(DateTimeError::invalid(format!(
"time have been set as field mask: {:x}",
self.f_mask
)));
}
let count = field.iter().take_while(|&i| i != &b'-').count();
if count == field.len() {
return Err(DateTimeError::invalid(
"can not find '-' flag".to_string(),
));
}
decode_timezone(&field[1..], tz)?;
}
} else {
return Err(DateTimeError::invalid(
"unsupport time zone now".to_string(),
));
}
}
} else {
self.decode_date(field, tm, date_order)?;
};
Ok(())
}
#[allow(clippy::if_same_then_else)]
fn decode_number_token(
&mut self,
str: &[u8],
tm: &mut PgTime,
_tz: &mut Option<i32>,
date_order: DateOrder,
) -> Result<(), DateTimeError> {
if self.p_type != TokenField::Number {
let (val, s) = str2i32(str)?;
match s.first() {
Some(b'.') => match self.p_type {
TokenField::Julian | TokenField::Time | TokenField::Second => {}
_ => {
return Err(DateTimeError::invalid(format!(
"prefix type: {:?} do not expect '.' at field: {:?}",
self.p_type, str
)));
}
},
Some(_) => {
return Err(DateTimeError::invalid(format!(
"field: {:?} and prefix type: {:?} is invalid",
str, self.p_type
)));
}
None => {}
};
match self.p_type {
TokenField::Year => {
tm.year = val;
self.t_mask = TokenType::YEAR.mask();
}
TokenField::Month => {
if (self.f_mask & TokenType::MONTH.mask()) != 0
&& (self.f_mask & TokenType::HOUR.mask()) != 0
{
tm.min = val;
self.t_mask = TokenType::MINUTE.mask();
} else {
tm.mon = val;
self.t_mask = TokenType::MONTH.mask();
}
}
TokenField::Day => {
tm.mday = val;
self.t_mask = TokenType::DAY.mask();
}
TokenField::Hour => {
tm.hour = val;
self.t_mask = TokenType::HOUR.mask();
}
TokenField::Minute => {
tm.min = val;
self.t_mask = TokenType::MINUTE.mask();
}
TokenField::Second => {
tm.sec = val;
self.t_mask = TokenType::SECOND.mask();
if let Some(b'.') = s.first() {
self.f_sec = parse_fractional_second(s)?;
self.t_mask = DTK_ALL_SECS_M;
}
}
TokenField::Julian => {
if val < 0 {
return Err(DateTimeError::overflow());
}
self.t_mask = DTK_DATE_M;
let (y, m, d) = julian2date(val);
tm.set_ymd(y, m, d);
self.is_julian = true;
if let Some(b'.') = s.first() {
let (time, _s) = str2d(s)?;
let mut time = time;
time *= USECS_PER_DAY as f64;
date2time(
time as i64,
&mut tm.hour,
&mut tm.min,
&mut tm.sec,
&mut self.f_sec,
);
self.t_mask |= DTK_TIME_M;
}
}
TokenField::Time => {
let _ = decode_number_field(
str,
self.f_mask | DTK_DATE_M,
&mut self.t_mask,
tm,
&mut self.f_sec,
&mut self.is2digits,
)?;
if self.t_mask != DTK_TIME_M {
return Err(DateTimeError::invalid(format!(
"prexfix time expects field: {:?} is not time mask at field: {:?}",
self.p_type, str
)));
}
}
_ => {
return Err(DateTimeError::invalid(format!(
"prefix type: {:?} is not expected at field: {:?}",
self.p_type, str
)));
}
};
self.p_type = TokenField::Number;
self.d_type = TokenField::Date;
} else {
let count = str.iter().take_while(|&i| i != &b'.').count();
if count != str.len() && ((self.f_mask & DTK_DATE_M) == 0) && str.len() <= 14 {
self.decode_date(str, tm, date_order)?;
}
else if count != str.len() && count > 2 {
let _ = decode_number_field(
str,
self.f_mask,
&mut self.t_mask,
tm,
&mut self.f_sec,
&mut self.is2digits,
)?;
}
else if str.len() >= 6
&& ((self.f_mask & DTK_DATE_M == 0) || (self.f_mask & DTK_TIME_M == 0))
{
let _ = decode_number_field(
str,
self.f_mask,
&mut self.t_mask,
tm,
&mut self.f_sec,
&mut self.is2digits,
)?;
}
else {
decode_number(
str,
false,
self.f_mask,
&mut self.t_mask,
tm,
&mut self.f_sec,
&mut self.is2digits,
date_order,
)?;
}
}
Ok(())
}
fn decode_string_and_special_token(
&mut self,
field_id: usize,
ftypes: &[TokenField],
field: &[u8],
tm: &mut PgTime,
tz: &mut Option<i32>,
) -> Result<(bool, Option<PgTimezone>), DateTimeError> {
let nf = ftypes.len();
let mut named_tz = None;
let mut val = 0;
let (t, _valtz) = decode_timezone_abbrev(field_id, field, &mut val);
let mut ty = t;
if ty == TokenType::UnknownField {
let (t, v) = decode_special(field_id, field);
ty = t;
val = v;
}
if ty == TokenType::IgnoreDtf {
return Ok((true, None)); }
self.t_mask = ty.mask();
match ty {
TokenType::RESERV => {
match TokenField::try_from(val)? {
TokenField::Current
| TokenField::Now
| TokenField::Yesterday
| TokenField::Today
| TokenField::Tomorrow => {
return Err(DateTimeError::invalid(format!(
"{:?} is not supported at current",
val
)));
}
TokenField::Zulu => {
self.t_mask = DTK_TIME_M | TokenType::TZ.mask();
self.d_type = TokenField::Date;
tm.hour = 0;
tm.min = 0;
tm.sec = 0;
match tz {
Some(z) => *z = 0,
None => {}
}
}
_ => self.d_type = TokenField::try_from(val)?,
}
}
TokenType::MONTH => {
if (self.f_mask & TokenType::MONTH.mask() != 0)
&& !(self.have_text_month)
&& (self.f_mask & TokenType::DAY.mask() == 0)
&& tm.mon >= 1
&& tm.mon <= 31
{
tm.mday = tm.mon;
self.t_mask = TokenType::DAY.mask();
}
self.have_text_month = true;
tm.mon = val;
}
TokenType::AMPM => {
self.mer = val;
}
TokenType::ADBC => {
self.bc = val == BC;
}
TokenType::DOW => {
tm.wday = val;
}
TokenType::UNITS => {
self.t_mask = 0;
self.p_type = TokenField::try_from(val)?;
}
TokenType::ISOTIME => {
self.t_mask = 0;
if (self.f_mask & DTK_DATE_M) != DTK_DATE_M {
return Err(DateTimeError::invalid(format!(
"field mask: {:x} do not have time mask",
self.f_mask
)));
}
if field_id >= nf - 1
|| (ftypes[field_id + 1] != TokenField::Number
&& ftypes[field_id + 1] != TokenField::Time
&& ftypes[field_id + 1] != TokenField::Date)
{
return Err(DateTimeError::invalid(format!(
"the {:?} field's type: {:?} is unexpected",
field_id,
ftypes[field_id + 1]
)));
}
self.p_type = TokenField::try_from(val)?;
}
TokenType::UnknownField => {
named_tz = pg_time_zone_set(field)?;
match named_tz {
Some(ref _tz) => {}
None => return Err(DateTimeError::invalid("timezone is null".to_string())),
}
self.t_mask = TokenType::TZ.mask();
}
_ => {
return Err(DateTimeError::invalid(format!(
"type: {:?} is unexpected",
ty
)));
}
}
Ok((false, named_tz))
}
}
pub fn decode_date_time(
fields: &[&[u8]],
f_types: &[TokenField],
tm: &mut PgTime,
tz: &mut Option<i32>,
date_order: DateOrder,
) -> Result<(i32, TokenField, i64), DateTimeError> {
let mut ctx = DecodeDateTimeCtx::new();
match tz {
Some(z) => *z = 0,
None => {}
}
ctx.f_sec = 0;
for (i, f_type) in f_types.iter().enumerate() {
match f_type {
TokenField::Date => {
ctx.decode_date_token(fields[i], tm, tz, date_order)?;
}
TokenField::Time => {
if ctx.p_type != TokenField::Number {
if ctx.p_type != TokenField::Time {
return Err(DateTimeError::invalid(format!(
"prefix type: {:?} is not time",
ctx.p_type
)));
}
ctx.p_type = TokenField::Number;
}
decode_time(
fields[i],
ctx.f_mask,
INTERVAL_FULL_RANGE,
&mut ctx.t_mask,
tm,
&mut ctx.f_sec,
)?;
if tm.hour > HOURS_PER_DAY
|| (tm.hour == HOURS_PER_DAY && (tm.min > 0 || tm.sec > 0 || ctx.f_sec > 0))
{
return Err(DateTimeError::overflow());
}
}
TokenField::Number => {
ctx.decode_number_token(fields[i], tm, tz, date_order)?;
}
TokenField::String | TokenField::Special => {
let (continue_flag, _) =
ctx.decode_string_and_special_token(i, f_types, fields[i], tm, tz)?;
if continue_flag {
continue;
}
}
_ => {
return Err(DateTimeError::invalid(format!(
"unsupported field type: {:?}",
f_type
)));
}
}
check_mask_set(ctx.f_mask, ctx.t_mask)?;
ctx.f_mask |= ctx.t_mask;
}
tm.validate_date(ctx.f_mask, ctx.is_julian, ctx.is2digits, ctx.bc)?;
if ctx.mer != HR24 && tm.hour > HOURS_PER_DAY / 2 {
return Err(DateTimeError::overflow());
}
if ctx.mer == AM && tm.hour == HOURS_PER_DAY / 2 {
tm.hour = 0;
} else if ctx.mer == PM && tm.hour != HOURS_PER_DAY / 2 {
tm.hour += HOURS_PER_DAY / 2;
}
if ctx.d_type == TokenField::Date && (ctx.f_mask & DTK_DATE_M) != DTK_DATE_M {
if (ctx.f_mask & DTK_TIME_M) == DTK_TIME_M {
return Ok((1, ctx.d_type, ctx.f_sec));
}
return Err(DateTimeError::invalid(format!(
"date type's field mask: {:x} is invalid",
ctx.f_mask
)));
}
Ok((0, ctx.d_type, ctx.f_sec))
}
struct DecodeTimeOnlyCtx {
d_type: TokenField,
f_sec: i64,
f_mask: i32,
t_mask: i32,
p_type: TokenField,
is_julian: bool,
is2digits: bool,
bc: bool,
mer: i32,
}
impl DecodeTimeOnlyCtx {
#[inline]
fn new() -> Self {
Self {
d_type: TokenField::Date,
f_sec: 0,
f_mask: 0,
t_mask: 0,
p_type: TokenField::Number,
is_julian: false,
is2digits: false,
bc: false,
mer: HR24,
}
}
#[allow(clippy::too_many_arguments)]
fn decode_date_token(
&mut self,
field_id: usize,
f_types: &mut [TokenField],
field: &[u8],
tm: &mut PgTime,
tz: &mut Option<i32>,
date_order: DateOrder,
) -> Result<(), DateTimeError> {
if tz.is_none() {
return Err(DateTimeError::invalid(
"only time should have timezone".to_string(),
));
}
let field_num = f_types.len();
if field_id == 0
&& field_num >= 2
&& (f_types[field_num - 1] == TokenField::Date || f_types[1] == TokenField::Time)
{
decode_date(
field,
self.f_mask,
&mut self.t_mask,
&mut self.is2digits,
tm,
date_order,
)?;
}
else {
match field.first() {
Some(_c) => {
return Err(DateTimeError::invalid(
"timezone is not supported at current".to_string(),
));
}
None => {
return Err(DateTimeError::invalid(
"timezone is not supported at current".to_string(),
));
}
}
}
Ok(())
}
fn decode_number_token(
&mut self,
field_id: usize,
f_types: &mut [TokenField],
field: &[u8],
tm: &mut PgTime,
tz: &mut Option<i32>,
date_order: DateOrder,
) -> Result<(), DateTimeError> {
if self.p_type != TokenField::Number {
match self.p_type {
TokenField::Julian | TokenField::Year | TokenField::Month | TokenField::Day => {
if tz.is_none() {
return Err(DateTimeError::invalid(format!(
"prefix type: {:?} is not expected at time only field: {:?}",
self.p_type, field
)));
}
}
_ => {}
}
let (val, s) = str2i32(field)?;
match s.first() {
Some(b'.') => match self.p_type {
TokenField::Julian | TokenField::Time | TokenField::Second => {}
_ => {
return Err(DateTimeError::invalid(format!(
"prefix type: {:?} do not expect '.'",
self.p_type
)));
}
},
Some(_) => {
return Err(DateTimeError::invalid(format!(
"field: {:?} is invalid",
field
)));
}
None => {}
};
match self.p_type {
TokenField::Year => {
tm.year = val;
self.t_mask = TokenType::YEAR.mask();
}
TokenField::Month => {
if (self.f_mask & TokenType::MONTH.mask()) != 0
&& (self.f_mask & TokenType::HOUR.mask()) != 0
{
tm.min = val;
self.t_mask = TokenType::MINUTE.mask();
} else {
tm.mon = val;
self.t_mask = TokenType::MONTH.mask();
}
}
TokenField::Day => {
tm.mday = val;
self.t_mask = TokenType::DAY.mask();
}
TokenField::Hour => {
tm.hour = val;
self.t_mask = TokenType::HOUR.mask();
}
TokenField::Minute => {
tm.min = val;
self.t_mask = TokenType::MINUTE.mask();
}
TokenField::Second => {
tm.sec = val;
self.t_mask = TokenType::SECOND.mask();
if let Some(b'.') = s.first() {
self.f_sec = parse_fractional_second(s)?;
self.t_mask = DTK_ALL_SECS_M;
}
}
TokenField::Tz => {
self.t_mask = TokenType::TZ.mask();
decode_timezone(field, tz)?;
}
TokenField::Julian => {
if val < 0 {
return Err(DateTimeError::overflow());
}
self.t_mask = DTK_DATE_M;
let (y, m, d) = julian2date(val);
tm.set_ymd(y, m, d);
self.is_julian = true;
if let Some(b'.') = s.first() {
let (time, _s) = str2d(s)?;
let mut time = time;
time *= USECS_PER_DAY as f64;
date2time(
time as i64,
&mut tm.hour,
&mut tm.min,
&mut tm.sec,
&mut self.f_sec,
);
self.t_mask |= DTK_TIME_M;
}
}
TokenField::Time => {
let ret = decode_number_field(
field,
self.f_mask | DTK_DATE_M,
&mut self.t_mask,
tm,
&mut self.f_sec,
&mut self.is2digits,
)?;
if self.t_mask != DTK_TIME_M {
return Err(DateTimeError::invalid(format!(
"field: {:?} is invalid for time mask is not set at field mask: {:x}",
self.t_mask, self.f_mask
)));
}
f_types[field_id] = ret;
}
_ => {
return Err(DateTimeError::invalid(format!(
"prefix type: {:?} is invalid",
self.p_type
)));
}
};
self.p_type = TokenField::Number;
self.d_type = TokenField::Date;
} else {
let count = field.iter().take_while(|&i| i != &b'.').count();
let field_num = f_types.len();
if count != field.len() {
if field_id == 0 && field_num >= 2 && f_types[field_num - 1] == TokenField::Date {
decode_date(
field,
self.f_mask,
&mut self.t_mask,
&mut self.is2digits,
tm,
date_order,
)?;
}
else if count != field.len() && count > 2 {
let ret = decode_number_field(
field,
self.f_mask | DTK_DATE_M,
&mut self.t_mask,
tm,
&mut self.f_sec,
&mut self.is2digits,
)?;
f_types[field_id] = ret as TokenField;
} else {
return Err(DateTimeError::invalid(format!(
"field: {:?} is invalid",
field
)));
}
} else if field.len() > 4 {
let ret = decode_number_field(
field,
self.f_mask | DTK_DATE_M,
&mut self.t_mask,
tm,
&mut self.f_sec,
&mut self.is2digits,
)?;
f_types[field_id] = ret;
}
else {
decode_number(
field,
false,
self.f_mask | DTK_DATE_M,
&mut self.t_mask,
tm,
&mut self.f_sec,
&mut self.is2digits,
date_order,
)?;
}
}
Ok(())
}
fn decode_string_and_special_token(
&mut self,
field_id: usize,
f_types: &mut [TokenField],
str: &[u8],
tm: &mut PgTime,
_tz: &mut Option<i32>,
) -> Result<(bool, Option<PgTimezone>), DateTimeError> {
let field_num = f_types.len();
let mut named_tz = None;
let mut val = 0;
let (t, _valtz) = decode_timezone_abbrev(field_id, str, &mut val);
let mut ty = t;
if ty == TokenType::UnknownField {
let (t, v) = decode_special(field_id, str);
ty = t;
val = v;
}
if ty == TokenType::IgnoreDtf {
return Ok((true, None)); }
self.t_mask = ty.mask();
match ty {
TokenType::RESERV => {
match TokenField::try_from(val)? {
TokenField::Current | TokenField::Now => {
return Err(DateTimeError::invalid(format!(
"type: {:?} is not support as time",
val
)));
}
TokenField::Zulu => {
self.t_mask = DTK_TIME_M | TokenType::TZ.mask();
self.d_type = TokenField::Time;
tm.hour = 0;
tm.min = 0;
tm.sec = 0;
tm.isdst = 0;
}
_ => {
return Err(DateTimeError::invalid(format!(
"type: {:?} is not support at time",
val
)));
}
}
}
TokenType::AMPM => {
self.mer = val;
}
TokenType::ADBC => {
self.bc = val == BC;
}
TokenType::UNITS => {
self.t_mask = 0;
self.p_type = TokenField::try_from(val)?;
}
TokenType::ISOTIME => {
self.t_mask = 0;
if field_id >= field_num - 1
|| (f_types[field_id + 1] != TokenField::Number
&& f_types[field_id + 1] != TokenField::Time
&& f_types[field_id + 1] != TokenField::Date)
{
return Err(DateTimeError::invalid(format!(
"the {:?} type :{:?} is unexpected",
field_id, f_types[field_id]
)));
}
self.p_type = TokenField::try_from(val)?;
}
TokenType::UnknownField => {
named_tz = pg_time_zone_set(str)?;
match named_tz {
Some(ref _tz) => {}
None => return Err(DateTimeError::invalid("timezone is none".to_string())),
}
self.t_mask = TokenType::TZ.mask();
}
_ => {
return Err(DateTimeError::invalid(format!(
"type: {:?} is not support",
val
)));
}
}
Ok((false, named_tz))
}
}
pub fn decode_time_only(
fields: &[&[u8]],
f_types: &mut [TokenField],
tm: &mut PgTime,
tz: &mut Option<i32>,
date_order: DateOrder,
) -> Result<i64, DateTimeError> {
let mut ctx = DecodeTimeOnlyCtx::new();
match tz {
Some(z) => *z = 0,
None => {}
}
ctx.f_sec = 0;
for (i, &field) in fields.iter().enumerate() {
let f_type = f_types[i];
match f_type {
TokenField::Date => {
ctx.decode_date_token(i, f_types, field, tm, tz, date_order)?;
}
TokenField::Time => {
decode_time(
field,
ctx.f_mask | DTK_DATE_M,
INTERVAL_FULL_RANGE,
&mut ctx.t_mask,
tm,
&mut ctx.f_sec,
)?;
}
TokenField::Tz => {
if tz.is_none() {
return Err(DateTimeError::invalid("timezone is null".to_string()));
}
decode_timezone(field, tz)?;
ctx.t_mask = TokenType::TZ.mask();
}
TokenField::Number => {
ctx.decode_number_token(i, f_types, field, tm, tz, date_order)?;
}
TokenField::String | TokenField::Special => {
let (continue_flag, _) =
ctx.decode_string_and_special_token(i, f_types, field, tm, tz)?;
if continue_flag {
continue;
}
}
_ => {
return Err(DateTimeError::invalid(format!(
"field type: {:?} is invalid for field: {:?}",
f_type, field
)));
}
}
check_mask_set(ctx.f_mask, ctx.t_mask)?;
ctx.f_mask |= ctx.t_mask;
}
tm.validate_date(ctx.f_mask, ctx.is_julian, ctx.is2digits, ctx.bc)?;
if ctx.mer != HR24 && tm.hour > HOURS_PER_DAY / 2 {
return Err(DateTimeError::overflow());
}
if ctx.mer == AM && tm.hour == HOURS_PER_DAY / 2 {
tm.hour = 0;
} else if ctx.mer == PM && tm.hour != HOURS_PER_DAY / 2 {
tm.hour += HOURS_PER_DAY / 2;
}
if tm.hour < 0
|| tm.min < 0
|| tm.min > MINS_PER_HOUR - 1
|| tm.sec < 0
|| tm.sec > SECS_PER_MINUTE
|| tm.hour > HOURS_PER_DAY
|| (tm.hour == HOURS_PER_DAY && (tm.min > 0 || tm.sec > 0 || ctx.f_sec > 0))
|| ctx.f_sec < 0
|| ctx.f_sec > USECS_PER_SEC
{
return Err(DateTimeError::overflow());
}
Ok(ctx.f_sec)
}
#[inline]
fn adjust_fract_seconds(
frac: f64,
tm: &mut PgTime,
fsec: &mut i64,
scale: i32,
) -> Result<(), DateTimeError> {
if frac == 0.0 {
return Ok(());
}
let mut frac = frac * scale as f64;
let sec = frac as i32;
let ret = tm.sec.checked_add(sec);
match ret {
Some(s) => tm.sec = s,
None => return Err(DateTimeError::overflow()),
}
frac -= sec as f64;
*fsec += (frac * 1_000_000_f64).round() as i64;
Ok(())
}
#[inline]
fn adjust_fract_days(
frac: f64,
tm: &mut PgTime,
fsec: &mut i64,
scale: i32,
) -> Result<(), DateTimeError> {
if frac == 0.0 {
return Ok(());
}
let mut frac = frac * scale as f64;
let extra_days = frac as i32;
let ret = tm.mday.checked_add(extra_days);
match ret {
Some(d) => tm.mday = d,
None => return Err(DateTimeError::overflow()),
}
frac -= extra_days as f64;
adjust_fract_seconds(frac, tm, fsec, SECS_PER_DAY)?;
Ok(())
}
fn decode_interval_number_field(
field: &[u8],
tm: &mut PgTime,
ty: &mut i32,
range: i32,
f_val: &mut f64,
f_sec: &mut i64,
t_mask: &mut i32,
) -> Result<(), DateTimeError> {
if *ty == TokenType::IgnoreDtf as i32 {
match range {
INTERVAL_MASK_Y => {
*ty = TokenField::Year.value();
}
INTERVAL_MASK_M | INTERVAL_MASK_YM => {
*ty = TokenField::Month.value();
}
INTERVAL_MASK_D => {
*ty = TokenField::Day.value();
}
INTERVAL_MASK_H | INTERVAL_MASK_DH => {
*ty = TokenField::Hour.value();
}
INTERVAL_MASK_MIN | INTERVAL_MASK_HM | INTERVAL_MASK_DHM => {
*ty = TokenField::Minute.value();
}
INTERVAL_MASK_S | INTERVAL_MASK_MS | INTERVAL_MASK_HMS | INTERVAL_MASK_DHMS => {
*ty = TokenField::Second.value();
}
_ => {
*ty = TokenField::Second.value();
}
}
}
let (mut val, s) = str2i32(field)?;
match s.first() {
Some(b'-') => {
let (mut val2, s) = str2i32(&s[1..])?;
if val2 < 0 || val2 >= MONTHS_PER_YEAR {
return Err(DateTimeError::overflow());
}
if !s.is_empty() {
return Err(DateTimeError::invalid(
"after '-' and digit, str is not expect empty".to_string(),
));
}
*ty = TokenField::Month.value();
if field[0] == b'-' {
val2 = -val2
}
if (val as f64 * MONTHS_PER_YEAR as f64 + val2 as f64) > std::i32::MAX as f64
|| (val as f64 * MONTHS_PER_YEAR as f64 + val2 as f64) < std::i32::MIN as f64
{
return Err(DateTimeError::overflow());
}
val = val * MONTHS_PER_YEAR + val2;
*f_val = 0.0;
}
Some(b'.') => {
let (v, s) = str2d(s)?;
if !s.is_empty() {
return Err(DateTimeError::invalid(
"after double, str is not expect empty".to_string(),
));
}
*f_val = if field[0] == b'-' { -v } else { v };
}
None => *f_val = 0.0,
_ => {
return Err(DateTimeError::invalid(format!(
"char: {:?} is not expected after digit",
s.first()
)));
}
}
*t_mask = 0;
match TokenField::try_from(*ty)? {
TokenField::Microsec => {
*f_sec += (val as f64 + *f_val).round() as i64;
*t_mask = TokenType::MICROSECOND.mask();
}
TokenField::Millisec => {
tm.sec += val / 1000;
val -= (val / 1000) * 1000;
*f_sec += ((val as f64 + *f_val) * 1000_f64).round() as i64;
*t_mask = TokenType::MILLISECOND.mask();
}
TokenField::Second => {
tm.sec += val;
*f_sec += (*f_val * 1_000_000_f64).round() as i64;
if *f_val == 0.0 {
*t_mask = TokenType::SECOND.mask();
} else {
*t_mask = DTK_ALL_SECS_M;
}
}
TokenField::Minute => {
tm.min += val;
adjust_fract_seconds(*f_val, tm, f_sec, SECS_PER_MINUTE)?;
*t_mask = TokenType::MINUTE.mask();
}
TokenField::Hour => {
tm.hour += val;
adjust_fract_seconds(*f_val, tm, f_sec, SECS_PER_HOUR)?;
*t_mask = TokenType::HOUR.mask();
*ty = TokenField::Day.value();
}
TokenField::Day => {
tm.mday += val;
adjust_fract_seconds(*f_val, tm, f_sec, SECS_PER_DAY)?;
*t_mask = TokenType::DAY.mask();
}
TokenField::Week => {
tm.mday += val * 7;
adjust_fract_days(*f_val, tm, f_sec, 7)?;
*t_mask = TokenType::WEEK.mask();
}
TokenField::Month => {
tm.mon += val;
adjust_fract_days(*f_val, tm, f_sec, DAYS_PER_MONTH)?;
*t_mask = TokenType::MONTH.mask();
}
TokenField::Year => {
tm.year += val;
if *f_val != 0.0 {
tm.mon += (*f_val * MONTHS_PER_YEAR as f64) as i32;
}
*t_mask = TokenType::YEAR.mask();
}
TokenField::Decade => {
tm.year += val * 10;
if *f_val != 0.0 {
tm.mon += (*f_val * MONTHS_PER_YEAR as f64 * 10_f64) as i32;
}
*t_mask = TokenType::DECADE.mask();
}
TokenField::Century => {
tm.year += val * 100;
if *f_val != 0.0 {
tm.mon += (*f_val * MONTHS_PER_YEAR as f64 * 100_f64) as i32;
}
*t_mask = TokenType::CENTURY.mask();
}
TokenField::Millennium => {
tm.year += val * 1000;
if *f_val != 0.0 {
tm.mon += (*f_val * MONTHS_PER_YEAR as f64 * 1000_f64) as i32;
}
*t_mask = TokenType::MILLENNIUM.mask();
}
_ => {
return Err(DateTimeError::invalid(format!(
"type: {:?} is not expected at interval",
*ty
)));
}
}
Ok(())
}
use crate::common::POSTGRES_EPOCH_JDATE;
use crate::timezone::PgTimezone;
use std::cell::RefCell;
thread_local!(static DELTA_CACHE: RefCell<[Option<&'static DateToken>; MAX_DATE_FIELDS]> = RefCell::new([None; MAX_DATE_FIELDS]));
#[inline]
fn decode_units(field: usize, low_token: &[u8]) -> (TokenType, i32) {
let token = {
let t: Option<&DateToken> = DELTA_CACHE.with(|tokens| tokens.borrow_mut()[field]);
let key = if low_token.len() > MAX_TOKEN_LEN {
&low_token[0..MAX_TOKEN_LEN]
} else {
low_token
};
if t.is_none() || t.unwrap().token != key {
search_delta_token(key)
} else {
t
}
};
match token {
None => (TokenType::UnknownField, 0),
Some(t) => {
DELTA_CACHE.with(|tokens| tokens.borrow_mut()[field] = token);
(t.ty, t.value)
}
}
}
pub fn decode_interval(
fields: &[&[u8]],
f_types: &[TokenField],
range: i32,
d_type: &mut TokenField,
tm: &mut PgTime,
f_sec: &mut i64,
interval_style: IntervalStyle,
) -> Result<(), DateTimeError> {
let mut is_before = false;
let mut f_mask = 0;
let mut t_mask = 0;
*d_type = TokenField::Delta;
let mut ty = TokenType::IgnoreDtf as i32;
*f_sec = 0;
let mut fval = 0.0;
let field_num = fields.len();
for i in 0..field_num {
let field_idx = field_num - i - 1;
let f_type = f_types[field_idx];
let field = fields[field_idx];
match f_type {
TokenField::Time => {
decode_time(field, f_mask, range, &mut t_mask, tm, f_sec)?;
ty = TokenField::Day.value();
}
TokenField::Tz => {
debug_assert!(field[0] == b'-' || field[0] == b'+');
let find = field.iter().find(|&&i| i == b':');
match find {
Some(_) => {
decode_time(&field[1..], f_mask, range, &mut t_mask, tm, f_sec)?;
if field[0] == b'-' {
tm.hour = -tm.hour;
tm.min = -tm.min;
tm.sec = -tm.sec;
*f_sec = -(*f_sec);
}
ty = TokenField::Day.value();
}
None => {
decode_interval_number_field(
field,
tm,
&mut ty,
range,
&mut fval,
f_sec,
&mut t_mask,
)?;
}
}
}
TokenField::Date | TokenField::Number => {
decode_interval_number_field(
field,
tm,
&mut ty,
range,
&mut fval,
f_sec,
&mut t_mask,
)?;
}
TokenField::String | TokenField::Special => {
let (token_ty, val) = decode_units(field_idx, field);
if token_ty == TokenType::IgnoreDtf {
continue;
}
t_mask = 0; match token_ty {
TokenType::UNITS => {
ty = val;
}
TokenType::AGO => {
is_before = true;
ty = val;
}
TokenType::RESERV => {
t_mask = DTK_DATE_M | DTK_TIME_M;
*d_type = TokenField::try_from(val)?;
}
_ => {
return Err(DateTimeError::invalid(format!(
"the type: {:?} of field: {:?} is invalid",
ty, field
)));
}
}
}
_ => {
return Err(DateTimeError::invalid(format!(
"filed type: {:?} is invaid for interval",
f_type
)));
}
}
check_mask_set(f_mask, t_mask)?;
f_mask |= t_mask;
}
check_mask_not_empty(f_mask)?;
if *f_sec != 0 {
let sec = *f_sec / USECS_PER_SEC;
*f_sec -= sec * USECS_PER_SEC;
tm.sec += sec as i32;
}
let field = fields[0];
if interval_style == IntervalStyle::SQLStandard && field[0] == b'-' {
let mut more_signs = false;
for field in fields.iter().skip(1) {
if !field.is_empty() && (field[0] == b'-' || field[0] == b'+') {
more_signs = true;
break;
}
}
if !more_signs {
if *f_sec > 0 {
*f_sec = -(*f_sec);
}
if tm.sec > 0 {
tm.sec = -tm.sec;
}
if tm.min > 0 {
tm.min = -tm.min;
}
if tm.hour > 0 {
tm.hour = -tm.hour;
}
if tm.mday > 0 {
tm.mday = -tm.mday;
}
if tm.mon > 0 {
tm.mon = -tm.mon;
}
if tm.year > 0 {
tm.year = -tm.year;
}
}
}
if is_before {
*f_sec = -(*f_sec);
tm.sec = -tm.sec;
tm.min = -tm.min;
tm.hour = -tm.hour;
tm.mday = -tm.mday;
tm.mon = -tm.mon;
tm.year = -tm.year;
}
Ok(())
}
#[inline]
fn parse_iso8601_number<'a, 'b, 'c>(
s: &'a [u8],
ipart: &'b mut i32,
fpart: &'c mut f64,
) -> Result<&'a [u8], DateTimeError> {
match s.first() {
None => {
return Err(DateTimeError::invalid(
"str is empty for iso number".to_string(),
));
}
Some(c) => {
if !(c.is_ascii_digit() || c == &b'c' || c == &b'.') {
return Err(DateTimeError::invalid(format!(
"str: {:?} is invalid for iso number",
s
)));
}
}
}
let (val, end) = str2d(s)?;
if end.len() == s.len() {
return Err(DateTimeError::invalid(format!("str: {:?} is not digit", s)));
}
if val < std::i32::MIN as f64 || val > std::i32::MAX as f64 {
return Err(DateTimeError::overflow());
}
if val >= 0.0 {
*ipart = val.floor() as i32;
} else {
*ipart = -val.floor() as i32;
}
*fpart = val - *ipart as f64;
Ok(end)
}
#[inline]
fn iso8601_integer_width(field_start: &[u8]) -> i32 {
let s = if !field_start.is_empty() && field_start[0] == b'-' {
&field_start[1..]
} else {
field_start
};
s.iter().take_while(|&&i| i >= b'0' && i <= b'9').count() as i32
}
#[allow(clippy::too_many_arguments)]
fn decode_date_part_left<'a, 'b, 'c, 'd>(
s: &'a [u8],
unit: u8,
tm: &'b mut PgTime,
datepart: &'c mut bool,
havefield: &'c mut bool,
val: &'d mut i32,
fval: &'d mut f64,
fsec: &'b mut i64,
) -> Result<(bool, &'a [u8]), DateTimeError> {
if *havefield {
return Err(DateTimeError::invalid(
"decode date part left foutn have field.".to_string(),
));
}
let mut s = s;
tm.year += *val;
tm.mon += (*fval * MONTHS_PER_YEAR as f64) as i32;
if unit == b'\0' {
return Ok((true, s));
}
if unit == b'T' {
*datepart = false;
*havefield = false;
return Ok((false, s));
}
s = parse_iso8601_number(s, val, fval)?;
tm.mon += *val;
adjust_fract_days(*fval, tm, fsec, DAYS_PER_MONTH)?;
match s.first() {
None => return Ok((true, s)),
Some(c) => {
if c == &b'T' {
*datepart = false;
*havefield = false;
return Ok((false, s));
}
if c != &b'-' {
return Err(DateTimeError::invalid(format!(
"expect '-', but it is: {:?}",
c
)));
}
}
}
s = &s[1..];
s = parse_iso8601_number(s, val, fval)?;
tm.mday += *val;
adjust_fract_seconds(*fval, tm, fsec, SECS_PER_DAY)?;
match s.first() {
None => Ok((true, s)),
Some(c) => {
if c == &b'T' {
*datepart = false;
*havefield = false;
Ok((false, s))
} else {
Err(DateTimeError::invalid(format!(
"expect 'T', but it is: {:?}",
c
)))
}
}
}
}
#[inline]
fn decode_time_part_left(
s: &[u8],
unit: u8,
tm: &mut PgTime,
havefield: bool,
val: &mut i32,
fval: &mut f64,
fsec: &mut i64,
) -> Result<(), DateTimeError> {
if havefield {
return Err(DateTimeError::invalid("have field is set".to_string()));
}
tm.hour += *val;
adjust_fract_seconds(*fval, tm, fsec, SECS_PER_HOUR)?;
if unit == b'\0' {
return Ok(());
}
let mut s = parse_iso8601_number(s, val, fval)?;
tm.min += *val;
adjust_fract_seconds(*fval, tm, fsec, SECS_PER_MINUTE)?;
match s.first() {
None => return Ok(()),
Some(c) => {
if c != &b':' {
return Err(DateTimeError::invalid(format!(
"char: {:?} is unexpected at str: {:?}",
s, c
)));
}
}
}
s = &s[1..];
s = parse_iso8601_number(s, val, fval)?;
tm.sec += *val;
adjust_fract_seconds(*fval, tm, fsec, 1)?;
match s.first() {
None => Ok(()),
Some(_) => Err(DateTimeError::invalid(format!(
"str: {:?} is after double",
s
))),
}
}
pub fn decode_iso8601_interval(
s: &[u8],
dtype: &mut TokenField,
tm: &mut PgTime,
fsec: &mut i64,
) -> Result<(), DateTimeError> {
let mut datepart = true;
let mut havefield = false;
*dtype = TokenField::Delta;
*fsec = 0;
if s.len() < 2 || s[0] != b'P' {
return Err(DateTimeError::invalid(format!(
"str: {:?} is not expect at field",
s
)));
}
let mut s = &s[1..];
while !s.is_empty() {
let mut fval = 0.0;
let mut val = 0;
if s[0] == b'T'
{
datepart = false;
havefield = false;
s = &s[1..];
continue;
}
let field_start = s;
s = parse_iso8601_number(s, &mut val, &mut fval)?;
let unit = if !s.is_empty() {
let c = s[0];
s = &s[1..];
c
} else {
b'\0'
};
if datepart {
match unit {
b'Y' => {
tm.year += val;
tm.mon += (fval * MONTHS_PER_YEAR as f64) as i32;
}
b'M' => {
tm.mon += val;
adjust_fract_days(fval, tm, fsec, DAYS_PER_MONTH)?;
}
b'W' => {
tm.mday += val * 7;
adjust_fract_days(fval, tm, fsec, 7)?;
}
b'D' => {
tm.mday += val;
adjust_fract_seconds(fval, tm, fsec, SECS_PER_DAY)?;
}
b'T' | b'\0' => {
let width = iso8601_integer_width(field_start);
if width == 8 && !havefield
{
tm.year += val / 10000;
tm.mon += (val / 100) % 100;
tm.mday += val % 100;
adjust_fract_seconds(fval, tm, fsec, SECS_PER_DAY)?;
if unit == b'\0' {
return Ok(());
}
datepart = false;
havefield = false;
continue;
}
let (is_finish, left) = decode_date_part_left(s, unit, tm,
&mut datepart, &mut havefield,
&mut val, &mut fval, fsec)?;
if !is_finish {
s = left;
continue;
} else {
return Ok(());
}
}
b'-' => {
let (is_finish, left) = decode_date_part_left(s, unit, tm,
&mut datepart, &mut havefield,
&mut val, &mut fval, fsec)?;
if !is_finish {
s = left;
continue;
} else {
return Ok(());
}
}
_ => {
return Err(DateTimeError::invalid(format!("char: {:?} is not expect at iso format interval", unit)));
}
}
} else {
match unit
{
b'H' => {
tm.hour += val;
adjust_fract_seconds(fval, tm, fsec, SECS_PER_HOUR)?;
}
b'M' => {
tm.min += val;
adjust_fract_seconds(fval, tm, fsec, SECS_PER_MINUTE)?;
}
b'S' => {
tm.sec += val;
adjust_fract_seconds(fval, tm, fsec, 1)?;
}
b'\0' => { let len = iso8601_integer_width(field_start);
if len == 6 && !havefield
{
tm.hour += val / 10000;
tm.min += (val / 100) % 100;
tm.sec += val % 100;
adjust_fract_seconds(fval, tm, fsec, 1)?;
return Ok(());
}
return decode_time_part_left(
s,
unit,
tm,
havefield,
&mut val,
&mut fval,
fsec);
}
b':' => { return decode_time_part_left(
s,
unit,
tm,
havefield,
&mut val,
&mut fval,
fsec);
}
_ => return Err(DateTimeError::invalid(format!("char: {:?} is not expect at iso format interval", unit))),
}
}
havefield = true;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::DateTimeError;
use crate::parse::{
check_mask_not_empty, check_mask_set, decode_time_part_left, eat_digits, eat_whitespaces,
parse_date_time, str2i32, TokenField,
};
use std::mem::MaybeUninit;
#[test]
fn test_eat_whitespace() {
let test_str = " 1 23 ".as_bytes();
let test_str = eat_whitespaces(test_str);
assert!(test_str == b"1 23 ");
let test_str = eat_whitespaces(&test_str[1..]);
assert!(test_str == b"23 ");
let test_str = eat_whitespaces(&test_str[2..]);
assert!(test_str.is_empty())
}
#[test]
fn test_eat_digit() {
let test_str = "1234".as_bytes();
let (digit_str, test_str) = eat_digits(test_str);
assert!(test_str.is_empty());
assert!(digit_str == b"1234");
let test_str = "1234abd".as_bytes();
let (digit_str, test_str) = eat_digits(test_str);
assert!(digit_str == b"1234");
assert!(test_str == b"abd");
let test_str = "abd123".as_bytes();
let (digit_str, test_str) = eat_digits(test_str);
assert!(digit_str.is_empty());
assert!(test_str == b"abd123");
}
fn test_pare_date_time_common(time_str: &[u8], ex_fields: &[&[u8]], ex_f_types: &[TokenField]) {
let mut worker_buf: [u8; 128] = unsafe { MaybeUninit::uninit().assume_init() };
let mut fields: [&[u8]; 18] = unsafe { MaybeUninit::uninit().assume_init() };
let mut f_types: [TokenField; 18] = unsafe { MaybeUninit::uninit().assume_init() };
let ret = parse_date_time(time_str, &mut worker_buf, &mut fields, &mut f_types, 18);
assert!(ret.is_ok());
let nf = ret.unwrap();
assert_eq!(nf as usize, ex_fields.len());
for (i, field) in ex_fields.iter().enumerate() {
assert_eq!(f_types[i], ex_f_types[i]);
assert_eq!(*field, ex_fields[i]);
}
}
#[test]
fn test_pare_date_time_digit_begin() {
test_pare_date_time_common(b"2000", &[b"2000"], &[TokenField::Number]);
test_pare_date_time_common(b"14:20", &[b"14:20"], &[TokenField::Time]);
test_pare_date_time_common(b"2000-12", &[b"2000-12"], &[TokenField::Date]);
test_pare_date_time_common(b"2000/12", &[b"2000/12"], &[TokenField::Date]);
test_pare_date_time_common(b"2000.12", &[b"2000.12"], &[TokenField::Number]);
test_pare_date_time_common(b"2000-12-12", &[b"2000-12-12"], &[TokenField::Date]);
test_pare_date_time_common(b"2000.12.12", &[b"2000.12.12"], &[TokenField::Date]);
test_pare_date_time_common(b"2000.am12", &[b"2000.am12"], &[TokenField::Date]);
}
#[test]
fn test_to_i32() -> Result<(), DateTimeError> {
let s = "123".as_bytes();
let ret = str2i32(s);
assert!(ret.is_ok());
let (v, _s) = ret.unwrap();
assert_eq!(v, 123);
let s = "+123".as_bytes();
let ret = str2i32(s);
assert!(ret.is_ok());
let (v, _s) = ret.unwrap();
assert_eq!(v, 123);
let s = "-123".as_bytes();
let ret = str2i32(s);
assert!(ret.is_ok());
let (v, _s) = ret.unwrap();
assert_eq!(v, -123);
let s = "abc".as_bytes();
let ret = str2i32(s);
assert!(ret.is_ok());
let (v, _s) = ret.unwrap();
assert_eq!(v, 0);
let s = "100abc123".as_bytes();
let ret = str2i32(s);
assert!(ret.is_ok());
let (v, _s) = ret.unwrap();
assert_eq!(v, 100);
let s = "2147483647".as_bytes();
let ret = str2i32(s);
assert!(ret.is_ok());
let (v, _s) = ret.unwrap();
assert_eq!(v, 2147483647);
let s = "2147483648".as_bytes();
let ret = str2i32(s);
assert!(ret.is_err());
let s = "2147483648000000".as_bytes();
let ret = str2i32(s);
assert!(ret.is_err());
let s = "123".as_bytes();
let b = &s[3..];
assert_eq!(b.len(), 0);
let (v, _) = str2i32("+".as_bytes())?;
assert_eq!(v, 0);
Ok(())
}
#[test]
fn test_decode_time_part_left() -> Result<(), DateTimeError> {
let mut tm = PgTime::new();
let mut fval = 0.0;
let mut fsec = 56;
let mut val = 40;
let ret = decode_time_part_left(
b"10.23n", b'S', &mut tm, false, &mut val, &mut fval, &mut fsec,
);
assert!(ret.is_err());
let ret = decode_time_part_left(
b"10.23.45.0.45n",
b'S',
&mut tm,
false,
&mut val,
&mut fval,
&mut fsec,
);
assert!(ret.is_err());
fval = 0.0;
fsec = 0;
val = 40;
let mut tm = PgTime::new();
let _ret = decode_time_part_left(
b"10:45", b'S', &mut tm, false, &mut val, &mut fval, &mut fsec,
);
assert_eq!(tm.sec, 45);
let ret = decode_time_part_left(
b"-10:3333377777777777777778888845",
b'S',
&mut tm,
false,
&mut val,
&mut fval,
&mut fsec,
);
assert!(ret.is_err());
Ok(())
}
#[test]
fn test_check_mask_set() -> Result<(), DateTimeError> {
let ret = check_mask_set(1i32 << 10, 1 << 10);
assert!(ret.is_err());
check_mask_set(1i32 << 12 | 1i32 << 11, 1 << 10)?;
let ret = check_mask_not_empty(0);
assert!(ret.is_err());
check_mask_not_empty(1)?;
Ok(())
}
#[test]
fn test_binary_search() -> Result<(), DateTimeError> {
let ret = search_delta_token(b"microseconds");
assert!(ret.is_some());
let ret = search_delta_token(b"microsecon");
assert!(ret.is_some());
let ret = search_delta_token(b"microsec");
assert!(ret.is_none());
let (t, v) = decode_units(1, b"microsecon");
assert_eq!(t, TokenType::UNITS);
assert_eq!(v, 30);
let (t, v) = decode_units(1, b"microseconds");
assert_eq!(t, TokenType::UNITS);
assert_eq!(v, 30);
Ok(())
}
#[test]
fn test_to_double() -> Result<(), DateTimeError> {
let (v, s) = str2d(".23456".as_bytes())?;
assert!(s.len() == 0);
assert_eq!(v, 0.23456);
let (v, s) = str2d("+".as_bytes())?;
assert!(s.len() == 0);
assert_eq!(v, 0.0);
let (v, s) = str2d("23.23456".as_bytes())?;
assert!(s.len() == 0);
assert_eq!(v, 23.23456);
let (v, s) = str2d("+23.23456".as_bytes())?;
assert!(s.len() == 0);
assert_eq!(v, 23.23456);
let (v, s) = str2d("-23.23456".as_bytes())?;
assert!(s.len() == 0);
assert_eq!(v, -23.23456);
let (v, s) = str2d("-23.23456s3".as_bytes())?;
assert!(s.len() == 2);
assert_eq!(v, -23.23456);
let (v, s) = str2d("23.23456s3".as_bytes())?;
assert!(s.len() == 2);
assert_eq!(v, 23.23456);
let (v, _s) = str2d("".as_bytes())?;
assert_eq!(v, 0.0);
Ok(())
}
#[test]
fn test_parse_frac_second() -> Result<(), DateTimeError> {
let ret = parse_fractional_second(b".2345")?;
assert_eq!(ret, 234500);
let ret = parse_fractional_second(b".1234567")?;
assert_eq!(ret, 123457);
let ret = parse_fractional_second(b".1")?;
assert_eq!(ret, 100000);
let ret = parse_fractional_second(b".1b");
assert!(ret.is_err());
Ok(())
}
#[test]
fn test_decode_number() -> Result<(), DateTimeError> {
let mut tm = PgTime::new();
let mut fsec = 0;
let mut is2digits = false;
let mut t_mask = 0;
let date_order = DateOrder::YMD;
let ret = decode_number(
b"b222222",
false,
0,
&mut t_mask,
&mut tm,
&mut fsec,
&mut is2digits,
date_order,
);
assert!(ret.is_err());
let ret = decode_number(
b"222f",
false,
0,
&mut t_mask,
&mut tm,
&mut fsec,
&mut is2digits,
date_order,
);
assert!(ret.is_err());
let ret = decode_number(
b"22222222222222222222222222222222",
false,
0,
&mut t_mask,
&mut tm,
&mut fsec,
&mut is2digits,
date_order,
);
assert!(ret.is_err());
decode_number(
b"19881216",
false,
0,
&mut t_mask,
&mut tm,
&mut fsec,
&mut is2digits,
date_order,
)?;
assert_eq!(tm.year, 19881216);
decode_number(
b"123022.23",
false,
0,
&mut t_mask,
&mut tm,
&mut fsec,
&mut is2digits,
date_order,
)?;
assert_eq!(tm.hour, 12);
assert_eq!(tm.min, 30);
assert_eq!(tm.sec, 22);
assert_eq!(fsec, 230000);
decode_number(
b"12.23",
false,
0,
&mut t_mask,
&mut tm,
&mut fsec,
&mut is2digits,
date_order,
)?;
assert_eq!(fsec, 230000);
Ok(())
}
#[test]
fn test_decode_time() -> Result<(), DateTimeError> {
let mut t_mask = 0;
let mut tm = PgTime::new();
let mut f_sec = 0;
let ret = decode_time(b"12", 0, 0, &mut t_mask, &mut tm, &mut f_sec);
assert!(ret.is_err());
let ret = decode_time(b"12y", 0, 0, &mut t_mask, &mut tm, &mut f_sec);
assert!(ret.is_err());
let ret = decode_time(b"12:-12:-34.345", 0, 0, &mut t_mask, &mut tm, &mut f_sec);
assert!(ret.is_err());
let ret = decode_time(b"12:12y", 0, 0, &mut t_mask, &mut tm, &mut f_sec);
assert!(ret.is_err());
let ret = decode_time(b"12:12:34y", 0, 0, &mut t_mask, &mut tm, &mut f_sec);
assert!(ret.is_err());
decode_time(
b"12:34",
0,
INTERVAL_MASK_MS,
&mut t_mask,
&mut tm,
&mut f_sec,
)?;
assert_eq!(tm.hour, 0);
assert_eq!(tm.min, 12);
assert_eq!(tm.sec, 34);
assert_eq!(f_sec, 0);
decode_time(
b"12:34",
0,
INTERVAL_MASK_MS,
&mut t_mask,
&mut tm,
&mut f_sec,
)?;
assert_eq!(tm.hour, 0);
assert_eq!(tm.min, 12);
assert_eq!(tm.sec, 34);
assert_eq!(f_sec, 0);
decode_time(
b"12:23.34",
0,
INTERVAL_MASK_MS,
&mut t_mask,
&mut tm,
&mut f_sec,
)?;
assert_eq!(tm.hour, 0);
assert_eq!(tm.min, 12);
assert_eq!(tm.sec, 23);
assert_eq!(f_sec, 340000);
Ok(())
}
#[test]
fn test_decode_date() -> Result<(), DateTimeError> {
let fields: [&[u8]; 1] = [&("34".as_bytes()); 1];
let f_types: [TokenField; 1] = [TokenField::Ago; 1];
let mut tm = PgTime::new();
let ret = decode_date_time(&fields, &f_types, &mut tm, &mut None, DateOrder::YMD);
assert!(ret.is_err());
Ok(())
}
}