Skip to content
This repository was archived by the owner on Dec 25, 2019. It is now read-only.

Commit f5812b4

Browse files
authored
Merge pull request #21 from ruchirK/timestamptz-2
sqlparser changes to support timestamptz
2 parents d8ae0f0 + 805ca84 commit f5812b4

File tree

7 files changed

+593
-8
lines changed

7 files changed

+593
-8
lines changed

src/ast/value.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ pub enum Value {
5252
Time(String),
5353
/// `TIMESTAMP '...'` literals
5454
Timestamp(String, ParsedTimestamp),
55+
/// `TIMESTAMP WITH TIME ZONE` literals
56+
TimestampTz(String, ParsedTimestamp),
5557
/// INTERVAL literals, roughly in the following format:
5658
///
5759
/// ```text
@@ -80,6 +82,11 @@ impl fmt::Display for Value {
8082
Value::Date(v, _) => write!(f, "DATE '{}'", escape_single_quote_string(v)),
8183
Value::Time(v) => write!(f, "TIME '{}'", escape_single_quote_string(v)),
8284
Value::Timestamp(v, _) => write!(f, "TIMESTAMP '{}'", escape_single_quote_string(v)),
85+
Value::TimestampTz(v, _) => write!(
86+
f,
87+
"TIMESTAMP WITH TIME ZONE '{}'",
88+
escape_single_quote_string(v)
89+
),
8390
Value::Interval(IntervalValue {
8491
parsed: _,
8592
value,

src/ast/value/datetime.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ pub struct ParsedTimestamp {
280280
pub minute: u8,
281281
pub second: u8,
282282
pub nano: u32,
283+
pub timezone_offset_second: i64,
283284
}
284285

285286
/// All of the fields that can appear in a literal `DATE`, `TIMESTAMP` or `INTERVAL` string
@@ -297,6 +298,7 @@ pub struct ParsedDateTime {
297298
pub minute: Option<u64>,
298299
pub second: Option<u64>,
299300
pub nano: Option<u32>,
301+
pub timezone_offset_second: Option<i64>,
300302
}
301303

302304
impl ParsedDateTime {
@@ -320,6 +322,7 @@ impl Default for ParsedDateTime {
320322
minute: None,
321323
second: None,
322324
nano: None,
325+
timezone_offset_second: None,
323326
}
324327
}
325328
}

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ define_keywords!(
386386
TIES,
387387
TIME,
388388
TIMESTAMP,
389+
TIMESTAMPTZ,
389390
TIMEZONE_HOUR,
390391
TIMEZONE_MINUTE,
391392
TO,

src/parser.rs

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ impl Parser {
212212
expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?),
213213
}),
214214
"TIME" => Ok(Expr::Value(Value::Time(self.parse_literal_string()?))),
215-
"TIMESTAMP" => Ok(Expr::Value(self.parse_timestamp()?)),
215+
"TIMESTAMP" => self.parse_timestamp(),
216+
"TIMESTAMPTZ" => self.parse_timestamptz(),
216217
// Here `w` is a word, check if it's a part of a multi-part
217218
// identifier, a function call, or a simple identifier:
218219
_ => match self.peek_token() {
@@ -508,16 +509,46 @@ impl Parser {
508509
}
509510
}
510511

511-
fn parse_timestamp(&mut self) -> Result<Value, ParserError> {
512+
fn parse_timestamp(&mut self) -> Result<Expr, ParserError> {
513+
if self.parse_keyword("WITH") {
514+
self.expect_keywords(&["TIME", "ZONE"])?;
515+
return Ok(Expr::Value(self.parse_timestamp_inner(true)?));
516+
} else if self.parse_keyword("WITHOUT") {
517+
self.expect_keywords(&["TIME", "ZONE"])?;
518+
}
519+
Ok(Expr::Value(self.parse_timestamp_inner(false)?))
520+
}
521+
522+
fn parse_timestamptz(&mut self) -> Result<Expr, ParserError> {
523+
Ok(Expr::Value(self.parse_timestamp_inner(true)?))
524+
}
525+
526+
fn parse_timestamp_inner(&mut self, parse_timezone: bool) -> Result<Value, ParserError> {
512527
use std::convert::TryInto;
513528

514529
let value = self.parse_literal_string()?;
515-
let pdt = Self::parse_interval_string(&value, &DateTimeField::Year)?;
530+
let pdt = Self::parse_timestamp_string(&value, parse_timezone)?;
516531

517532
match (
518-
pdt.year, pdt.month, pdt.day, pdt.hour, pdt.minute, pdt.second, pdt.nano,
533+
pdt.year,
534+
pdt.month,
535+
pdt.day,
536+
pdt.hour,
537+
pdt.minute,
538+
pdt.second,
539+
pdt.nano,
540+
pdt.timezone_offset_second,
519541
) {
520-
(Some(year), Some(month), Some(day), Some(hour), Some(minute), Some(second), nano) => {
542+
(
543+
Some(year),
544+
Some(month),
545+
Some(day),
546+
Some(hour),
547+
Some(minute),
548+
Some(second),
549+
nano,
550+
timezone_offset_second,
551+
) => {
521552
let p_err = |e: std::num::TryFromIntError, field: &str| {
522553
ParserError::ParserError(format!(
523554
"{} in date '{}' is invalid: {}",
@@ -555,6 +586,23 @@ impl Parser {
555586
if second > 60 {
556587
parser_err!("Second in timestamp '{}' cannot be > 60: {}", value, second)?;
557588
}
589+
590+
if parse_timezone {
591+
return Ok(Value::TimestampTz(
592+
value,
593+
ParsedTimestamp {
594+
year,
595+
month,
596+
day,
597+
hour,
598+
minute,
599+
second,
600+
nano: nano.unwrap_or(0),
601+
timezone_offset_second: timezone_offset_second.unwrap_or(0),
602+
},
603+
));
604+
}
605+
558606
Ok(Value::Timestamp(
559607
value,
560608
ParsedTimestamp {
@@ -565,6 +613,7 @@ impl Parser {
565613
minute,
566614
second,
567615
nano: nano.unwrap_or(0),
616+
timezone_offset_second: 0,
568617
},
569618
))
570619
}
@@ -773,6 +822,27 @@ impl Parser {
773822
datetime::build_parsed_datetime(&toks, leading_field, value)
774823
}
775824

825+
pub fn parse_timestamp_string(
826+
value: &str,
827+
parse_timezone: bool,
828+
) -> Result<ParsedDateTime, ParserError> {
829+
if value.is_empty() {
830+
return Err(ParserError::ParserError(
831+
"Timestamp string is empty!".to_string(),
832+
));
833+
}
834+
835+
let (ts_string, tz_string) = datetime::split_timestamp_string(value);
836+
837+
let mut pdt = Self::parse_interval_string(ts_string, &DateTimeField::Year)?;
838+
if !parse_timezone || tz_string.is_empty() {
839+
return Ok(pdt);
840+
}
841+
842+
pdt.timezone_offset_second = Some(datetime::parse_timezone_offset_second(tz_string)?);
843+
Ok(pdt)
844+
}
845+
776846
/// Parses the parens following the `[ NOT ] IN` operator
777847
pub fn parse_in(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParserError> {
778848
self.expect_token(&Token::LParen)?;
@@ -1554,6 +1624,7 @@ impl Parser {
15541624
}
15551625
Ok(DataType::Timestamp)
15561626
}
1627+
"TIMESTAMPTZ" => Ok(DataType::TimestampTz),
15571628
"TIME" => {
15581629
if self.parse_keyword("WITH") {
15591630
self.expect_keywords(&["TIME", "ZONE"])?;

0 commit comments

Comments
 (0)