Adds basic native support of xsd:dateTime

pull/10/head
Tpt 6 years ago
parent 071b3934b7
commit 15edf0bc8a
  1. 1
      lib/Cargo.toml
  2. 1
      lib/src/lib.rs
  3. 67
      lib/src/model/literal.rs
  4. 63
      lib/src/store/numeric_encoder.rs

@ -26,6 +26,7 @@ quick-xml = "0.13"
ordered-float = "1" ordered-float = "1"
num-traits = "0.2" num-traits = "0.2"
rust_decimal = "0.10" rust_decimal = "0.10"
chrono = "0.4"
[build-dependencies] [build-dependencies]
peg = "0.5" peg = "0.5"

@ -36,6 +36,7 @@ extern crate byteorder;
extern crate error_chain; extern crate error_chain;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate chrono;
extern crate num_traits; extern crate num_traits;
extern crate ordered_float; extern crate ordered_float;
extern crate quick_xml; extern crate quick_xml;

@ -1,3 +1,6 @@
use chrono::DateTime;
use chrono::FixedOffset;
use chrono::NaiveDateTime;
use model::named_node::NamedNode; use model::named_node::NamedNode;
use model::vocab::rdf; use model::vocab::rdf;
use model::vocab::xsd; use model::vocab::xsd;
@ -47,6 +50,8 @@ enum LiteralContent {
Double(OrderedFloat<f64>), Double(OrderedFloat<f64>),
Integer(i128), Integer(i128),
Decimal(Decimal), Decimal(Decimal),
DateTime(DateTime<FixedOffset>),
NaiveDateTime(NaiveDateTime),
TypedLiteral { value: String, datatype: NamedNode }, TypedLiteral { value: String, datatype: NamedNode },
} }
@ -88,6 +93,19 @@ impl Literal {
Ok(value) => LiteralContent::Decimal(value), Ok(value) => LiteralContent::Decimal(value),
Err(_) => LiteralContent::TypedLiteral { value, datatype }, Err(_) => LiteralContent::TypedLiteral { value, datatype },
} }
} else if datatype == *xsd::DATE_TIME {
match DateTime::parse_from_rfc3339(&value) {
Ok(value) => LiteralContent::DateTime(value),
Err(_) => match NaiveDateTime::parse_from_str(&value, "%Y-%m-%dT%H:%M:%S") {
Ok(value) => LiteralContent::NaiveDateTime(value),
Err(_) => LiteralContent::TypedLiteral { value, datatype },
},
}
} else if datatype == *xsd::DATE_TIME_STAMP {
match DateTime::parse_from_rfc3339(&value) {
Ok(value) => LiteralContent::DateTime(value),
Err(_) => LiteralContent::TypedLiteral { value, datatype },
}
} else { } else {
LiteralContent::TypedLiteral { value, datatype } LiteralContent::TypedLiteral { value, datatype }
}) })
@ -116,6 +134,8 @@ impl Literal {
LiteralContent::Double(value) => Cow::Owned(value.to_string()), LiteralContent::Double(value) => Cow::Owned(value.to_string()),
LiteralContent::Integer(value) => Cow::Owned(value.to_string()), LiteralContent::Integer(value) => Cow::Owned(value.to_string()),
LiteralContent::Decimal(value) => Cow::Owned(value.to_string()), LiteralContent::Decimal(value) => Cow::Owned(value.to_string()),
LiteralContent::DateTime(value) => Cow::Owned(value.to_string()),
LiteralContent::NaiveDateTime(value) => Cow::Owned(value.to_string()),
} }
} }
@ -141,6 +161,7 @@ impl Literal {
LiteralContent::Double(_) => &xsd::DOUBLE, LiteralContent::Double(_) => &xsd::DOUBLE,
LiteralContent::Integer(_) => &xsd::INTEGER, LiteralContent::Integer(_) => &xsd::INTEGER,
LiteralContent::Decimal(_) => &xsd::DECIMAL, LiteralContent::Decimal(_) => &xsd::DECIMAL,
LiteralContent::DateTime(_) | LiteralContent::NaiveDateTime(_) => &xsd::DATE_TIME,
LiteralContent::TypedLiteral { ref datatype, .. } => datatype, LiteralContent::TypedLiteral { ref datatype, .. } => datatype,
} }
} }
@ -204,6 +225,22 @@ impl Literal {
} }
} }
/// Checks if the literal has the datatype [xsd:dateTime](http://www.w3.org/2001/XMLSchema#dateTime) or one of its sub datatype and is valid
pub fn is_date_time(&self) -> bool {
match self.0 {
LiteralContent::DateTime(_) | LiteralContent::NaiveDateTime(_) => true,
_ => false,
}
}
/// Checks if the literal has the datatype [xsd:dateTimeStamp](http://www.w3.org/2001/XMLSchema#dateTimeStamp) or [xsd:dateTime](http://www.w3.org/2001/XMLSchema#dateTime) with a fixed timezone and is valid
pub fn is_date_time_stamp(&self) -> bool {
match self.0 {
LiteralContent::DateTime(_) => true,
_ => false,
}
}
/// Returns the [effective boolean value](https://www.w3.org/TR/sparql11-query/#ebv) of the literal if it exists /// Returns the [effective boolean value](https://www.w3.org/TR/sparql11-query/#ebv) of the literal if it exists
pub fn to_bool(&self) -> Option<bool> { pub fn to_bool(&self) -> Option<bool> {
match self.0 { match self.0 {
@ -282,6 +319,24 @@ impl Literal {
_ => None, _ => None,
} }
} }
/// Returns the value of this literal as NaiveDateTime if possible
pub(crate) fn to_date_time(&self) -> Option<NaiveDateTime> {
match self.0 {
LiteralContent::DateTime(value) => Some(value.naive_utc()),
LiteralContent::NaiveDateTime(value) => Some(value),
_ => None,
}
}
/// Returns the value of this literal as DateTime<FixedOffset> if possible
pub(crate) fn to_date_time_stamp(&self) -> Option<DateTime<FixedOffset>> {
if let LiteralContent::DateTime(value) = self.0 {
Some(value)
} else {
None
}
}
} }
impl fmt::Display for Literal { impl fmt::Display for Literal {
@ -373,3 +428,15 @@ impl From<Decimal> for Literal {
Literal(LiteralContent::Decimal(value)) Literal(LiteralContent::Decimal(value))
} }
} }
impl From<DateTime<FixedOffset>> for Literal {
fn from(value: DateTime<FixedOffset>) -> Self {
Literal(LiteralContent::DateTime(value))
}
}
impl From<NaiveDateTime> for Literal {
fn from(value: NaiveDateTime) -> Self {
Literal(LiteralContent::NaiveDateTime(value))
}
}

@ -1,4 +1,7 @@
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
use chrono::DateTime;
use chrono::FixedOffset;
use chrono::NaiveDateTime;
use model::vocab::rdf; use model::vocab::rdf;
use model::vocab::xsd; use model::vocab::xsd;
use model::*; use model::*;
@ -62,6 +65,8 @@ const TYPE_FLOAT_LITERAL: u8 = 9;
const TYPE_DOUBLE_LITERAL: u8 = 10; const TYPE_DOUBLE_LITERAL: u8 = 10;
const TYPE_INTEGER_LITERAL: u8 = 11; const TYPE_INTEGER_LITERAL: u8 = 11;
const TYPE_DECIMAL_LITERAL: u8 = 12; const TYPE_DECIMAL_LITERAL: u8 = 12;
const TYPE_DATE_TIME_LITERAL: u8 = 13;
const TYPE_NAIVE_DATE_TIME_LITERAL: u8 = 14;
pub static ENCODED_DEFAULT_GRAPH: EncodedTerm = EncodedTerm::DefaultGraph {}; pub static ENCODED_DEFAULT_GRAPH: EncodedTerm = EncodedTerm::DefaultGraph {};
pub static ENCODED_EMPTY_SIMPLE_LITERAL: EncodedTerm = EncodedTerm::SimpleLiteral { pub static ENCODED_EMPTY_SIMPLE_LITERAL: EncodedTerm = EncodedTerm::SimpleLiteral {
@ -109,6 +114,8 @@ pub enum EncodedTerm {
DoubleLiteral(OrderedFloat<f64>), DoubleLiteral(OrderedFloat<f64>),
IntegerLiteral(i128), IntegerLiteral(i128),
DecimalLiteral(Decimal), DecimalLiteral(Decimal),
DateTime(DateTime<FixedOffset>),
NaiveDateTime(NaiveDateTime),
} }
impl EncodedTerm { impl EncodedTerm {
@ -136,7 +143,9 @@ impl EncodedTerm {
| EncodedTerm::FloatLiteral(_) | EncodedTerm::FloatLiteral(_)
| EncodedTerm::DoubleLiteral(_) | EncodedTerm::DoubleLiteral(_)
| EncodedTerm::IntegerLiteral(_) | EncodedTerm::IntegerLiteral(_)
| EncodedTerm::DecimalLiteral(_) => true, | EncodedTerm::DecimalLiteral(_)
| EncodedTerm::DateTime(_)
| EncodedTerm::NaiveDateTime(_) => true,
_ => false, _ => false,
} }
} }
@ -155,6 +164,9 @@ impl EncodedTerm {
EncodedTerm::DoubleLiteral(..) => Some(ENCODED_XSD_DOUBLE_NAMED_NODE), EncodedTerm::DoubleLiteral(..) => Some(ENCODED_XSD_DOUBLE_NAMED_NODE),
EncodedTerm::IntegerLiteral(..) => Some(ENCODED_XSD_INTEGER_NAMED_NODE), EncodedTerm::IntegerLiteral(..) => Some(ENCODED_XSD_INTEGER_NAMED_NODE),
EncodedTerm::DecimalLiteral(..) => Some(ENCODED_XSD_DECIMAL_NAMED_NODE), EncodedTerm::DecimalLiteral(..) => Some(ENCODED_XSD_DECIMAL_NAMED_NODE),
EncodedTerm::DateTime(..) | EncodedTerm::NaiveDateTime(..) => {
Some(ENCODED_XSD_DATE_TIME_NAMED_NODE)
}
_ => None, _ => None,
} }
} }
@ -174,6 +186,8 @@ impl EncodedTerm {
EncodedTerm::DoubleLiteral(_) => TYPE_DOUBLE_LITERAL, EncodedTerm::DoubleLiteral(_) => TYPE_DOUBLE_LITERAL,
EncodedTerm::IntegerLiteral(_) => TYPE_INTEGER_LITERAL, EncodedTerm::IntegerLiteral(_) => TYPE_INTEGER_LITERAL,
EncodedTerm::DecimalLiteral(_) => TYPE_DECIMAL_LITERAL, EncodedTerm::DecimalLiteral(_) => TYPE_DECIMAL_LITERAL,
EncodedTerm::DateTime(_) => TYPE_DATE_TIME_LITERAL,
EncodedTerm::NaiveDateTime(_) => TYPE_NAIVE_DATE_TIME_LITERAL,
} }
} }
} }
@ -208,6 +222,18 @@ impl From<Decimal> for EncodedTerm {
} }
} }
impl From<DateTime<FixedOffset>> for EncodedTerm {
fn from(value: DateTime<FixedOffset>) -> Self {
EncodedTerm::DateTime(value)
}
}
impl From<NaiveDateTime> for EncodedTerm {
fn from(value: NaiveDateTime) -> Self {
EncodedTerm::NaiveDateTime(value)
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct EncodedQuad { pub struct EncodedQuad {
pub subject: EncodedTerm, pub subject: EncodedTerm,
@ -281,6 +307,20 @@ impl<R: Read> TermReader for R {
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DecimalLiteral(Decimal::deserialize(buffer))) Ok(EncodedTerm::DecimalLiteral(Decimal::deserialize(buffer)))
} }
TYPE_DATE_TIME_LITERAL => Ok(EncodedTerm::DateTime(DateTime::from_utc(
NaiveDateTime::from_timestamp_opt(
self.read_i64::<NetworkEndian>()?,
self.read_u32::<NetworkEndian>()?,
).ok_or("Invalid date time serialization")?,
FixedOffset::east_opt(self.read_i32::<NetworkEndian>()?)
.ok_or("Invalid timezone offset")?,
))),
TYPE_NAIVE_DATE_TIME_LITERAL => Ok(EncodedTerm::NaiveDateTime(
NaiveDateTime::from_timestamp_opt(
self.read_i64::<NetworkEndian>()?,
self.read_u32::<NetworkEndian>()?,
).ok_or("Invalid date time serialization")?,
)),
_ => Err("the term buffer has an invalid type id".into()), _ => Err("the term buffer has an invalid type id".into()),
} }
} }
@ -361,6 +401,15 @@ impl<R: Write> TermWriter for R {
EncodedTerm::DoubleLiteral(value) => self.write_f64::<NetworkEndian>(*value)?, EncodedTerm::DoubleLiteral(value) => self.write_f64::<NetworkEndian>(*value)?,
EncodedTerm::IntegerLiteral(value) => self.write_i128::<NetworkEndian>(value)?, EncodedTerm::IntegerLiteral(value) => self.write_i128::<NetworkEndian>(value)?,
EncodedTerm::DecimalLiteral(value) => self.write_all(&value.serialize())?, EncodedTerm::DecimalLiteral(value) => self.write_all(&value.serialize())?,
EncodedTerm::DateTime(value) => {
self.write_i64::<NetworkEndian>(value.timestamp())?;
self.write_u32::<NetworkEndian>(value.timestamp_subsec_nanos())?;
self.write_i32::<NetworkEndian>(value.timezone().local_minus_utc())?;
}
EncodedTerm::NaiveDateTime(value) => {
self.write_i64::<NetworkEndian>(value.timestamp())?;
self.write_u32::<NetworkEndian>(value.timestamp_subsec_nanos())?;
}
} }
Ok(()) Ok(())
} }
@ -450,6 +499,16 @@ impl<S: BytesStore> Encoder<S> {
.to_decimal() .to_decimal()
.ok_or_else(|| Error::from("decimal literal without decimal value"))? .ok_or_else(|| Error::from("decimal literal without decimal value"))?
.into() .into()
} else if literal.is_date_time_stamp() {
literal
.to_date_time_stamp()
.ok_or_else(|| Error::from("dateTimeStamp literal without dateTimeStamp value"))?
.into()
} else if literal.is_decimal() {
literal
.to_date_time()
.ok_or_else(|| Error::from("dateTime literal without dateTime value"))?
.into()
} else { } else {
EncodedTerm::TypedLiteral { EncodedTerm::TypedLiteral {
value_id: self.encode_str_value(&literal.value())?, value_id: self.encode_str_value(&literal.value())?,
@ -530,6 +589,8 @@ impl<S: BytesStore> Encoder<S> {
EncodedTerm::DoubleLiteral(value) => Ok(Literal::from(*value).into()), EncodedTerm::DoubleLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::IntegerLiteral(value) => Ok(Literal::from(value).into()), EncodedTerm::IntegerLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::DecimalLiteral(value) => Ok(Literal::from(value).into()), EncodedTerm::DecimalLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::DateTime(value) => Ok(Literal::from(value).into()),
EncodedTerm::NaiveDateTime(value) => Ok(Literal::from(value).into()),
} }
} }

Loading…
Cancel
Save