Skip to content

Commit 1fb3da8

Browse files
committed
add support for DateTime, LocalDateTime & DateTime with zone id bolt
types
1 parent 60bc54a commit 1fb3da8

File tree

7 files changed

+413
-37
lines changed

7 files changed

+413
-37
lines changed

README.md

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -49,39 +49,6 @@ This driver is compatible with neo4j 4.x versions
4949
```
5050

5151

52-
53-
---
54-
55-
# Roadmap
56-
- [x] bolt protocol
57-
- [x] stream abstraction
58-
- [x] query.run() vs query.execute() abstraction
59-
- [x] respect "has_more" flag returned for PULL
60-
- [x] connection pooling
61-
- [x] explicit transactions
62-
- [x] use buffered TCP streams
63-
- [x] improve error messages & logging
64-
- [x] fetch rows in blocks
65-
- [x] configureable fetch size
66-
- [x] multi db support
67-
- [x] multi version support
68-
- [x] support data types
69-
- [x] Float
70-
- [x] Bytes
71-
- [ ] support structures
72-
- [x] Relationship
73-
- [x] Point2D
74-
- [x] Point3D
75-
- [x] UnboundedRelationship
76-
- [x] Path
77-
- [x] Duration
78-
- [x] Date
79-
- [x] Time
80-
- [x] LocalTime
81-
- [ ] DateTime
82-
- [ ] DateTimeZoneId
83-
- [ ] LocalDateTime
84-
8552
## License
8653

8754
Neo4rs is licensed under either of the following, at your option:

lib/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "neo4rs"
3-
version = "0.5.3"
3+
version = "0.5.4"
44
authors = ["John Vincent <yehohanan7@gmail.com>"]
55
edition = "2018"
66
description = "neo4j driver in rust"

lib/src/convert.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,28 @@ impl TryFrom<BoltType> for chrono::NaiveDate {
6969
}
7070
}
7171

72+
impl TryFrom<BoltType> for chrono::DateTime<chrono::FixedOffset> {
73+
type Error = Error;
74+
75+
fn try_from(input: BoltType) -> Result<chrono::DateTime<chrono::FixedOffset>> {
76+
match input {
77+
BoltType::DateTime(d) => d.try_into(),
78+
_ => Err(Error::ConverstionError),
79+
}
80+
}
81+
}
82+
83+
impl TryFrom<BoltType> for chrono::NaiveDateTime {
84+
type Error = Error;
85+
86+
fn try_from(input: BoltType) -> Result<chrono::NaiveDateTime> {
87+
match input {
88+
BoltType::LocalDateTime(d) => d.try_into(),
89+
_ => Err(Error::ConverstionError),
90+
}
91+
}
92+
}
93+
7294
impl TryFrom<BoltType> for (chrono::NaiveTime, Option<chrono::FixedOffset>) {
7395
type Error = Error;
7496

@@ -88,6 +110,17 @@ impl TryFrom<BoltType> for (chrono::NaiveTime, Option<chrono::FixedOffset>) {
88110
}
89111
}
90112

113+
impl TryFrom<BoltType> for (chrono::NaiveDateTime, String) {
114+
type Error = Error;
115+
116+
fn try_from(input: BoltType) -> Result<(chrono::NaiveDateTime, String)> {
117+
match input {
118+
BoltType::DateTimeZoneId(date_time_zone_id) => date_time_zone_id.try_into(),
119+
_ => Err(Error::ConverstionError),
120+
}
121+
}
122+
}
123+
91124
impl TryFrom<BoltType> for Vec<u8> {
92125
type Error = Error;
93126

@@ -202,12 +235,30 @@ impl Into<BoltType> for chrono::NaiveTime {
202235
}
203236
}
204237

238+
impl Into<BoltType> for chrono::NaiveDateTime {
239+
fn into(self) -> BoltType {
240+
BoltType::LocalDateTime(self.into())
241+
}
242+
}
243+
244+
impl Into<BoltType> for chrono::DateTime<chrono::FixedOffset> {
245+
fn into(self) -> BoltType {
246+
BoltType::DateTime(self.into())
247+
}
248+
}
249+
205250
impl Into<BoltType> for (chrono::NaiveTime, chrono::FixedOffset) {
206251
fn into(self) -> BoltType {
207252
BoltType::Time(self.into())
208253
}
209254
}
210255

256+
impl Into<BoltType> for (chrono::NaiveDateTime, &str) {
257+
fn into(self) -> BoltType {
258+
BoltType::DateTimeZoneId(self.into())
259+
}
260+
}
261+
211262
impl Into<BoltType> for Vec<u8> {
212263
fn into(self) -> BoltType {
213264
BoltType::Bytes(BoltBytes::new(self.into()))

lib/src/lib.rs

Lines changed: 137 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,10 @@
467467
//! ```
468468
//! ## Date
469469
//!
470+
//! See [NaiveDate][naive_date] for date abstraction, it captures the date without time component.
471+
//!
472+
//! [naive_date]: https://docs.rs/chrono/0.4.19/chrono/naive/struct.NaiveDate.html
473+
//!
470474
//! ```
471475
//! use neo4rs::*;
472476
//! use futures::stream::*;
@@ -493,14 +497,17 @@
493497
//!
494498
//! ## Time
495499
//!
496-
//! Neo4rs uses [chrono][chrono] crate for time abstractions.
500+
//! * [NaiveTime][naive_time] captures only the time of the day
501+
//! * `tuple`([NaiveTime][naive_time], `Option`<[FixedOffset][fixed_offset]>) captures the time of the day along with the
502+
//! offset
497503
//!
498-
//! [chrono]: https://crates.io/crates/chrono
504+
//! [naive_time]: https://docs.rs/chrono/0.4.19/chrono/naive/struct.NaiveTime.html
505+
//! [fixed_offset]: https://docs.rs/chrono/0.4.19/chrono/offset/struct.FixedOffset.html
499506
//!
500507
//!
501508
//! ### Time as param
502509
//!
503-
//! Pass a time (without offset) as a parameter to the query:
510+
//! Pass a time as a parameter to the query:
504511
//!
505512
//! ```
506513
//! use neo4rs::*;
@@ -583,6 +590,133 @@
583590
//!
584591
//! ```
585592
//!
593+
//!
594+
//! ## DateTime
595+
//!
596+
//!
597+
//! * [DateTime][date_time] captures the date and time with offset
598+
//! * [NaiveDateTime][naive_date_time] captures the date time without offset
599+
//! * `tuple`([NaiveDateTime][naive_date_time], String) captures the date/time and the time zone id
600+
//!
601+
//! [date_time]: https://docs.rs/chrono/0.4.19/chrono/struct.DateTime.html
602+
//! [naive_date_time]: https://docs.rs/chrono/0.4.19/chrono/struct.NaiveDateTime.html
603+
//!
604+
//!
605+
//! ### DateTime as param
606+
//!
607+
//! Pass a DateTime as parameter to the query:
608+
//!
609+
//! ```
610+
//! use neo4rs::*;
611+
//! use futures::stream::*;
612+
//! use uuid::Uuid;
613+
//!
614+
//! #[tokio::main]
615+
//! async fn main() {
616+
//! let uri = "127.0.0.1:7687";
617+
//! let user = "neo4j";
618+
//! let pass = "neo";
619+
//! let graph = Graph::new(uri, user, pass).await.unwrap();
620+
//!
621+
//! //send datetime as parameter in the query
622+
//! let datetime = chrono::DateTime::parse_from_rfc2822("Tue, 01 Jul 2003 10:52:37 +0200").unwrap();
623+
//!
624+
//! let mut result = graph
625+
//! .execute(query("RETURN $d as output").param("d", datetime))
626+
//! .await
627+
//! .unwrap();
628+
//! let row = result.next().await.unwrap().unwrap();
629+
//! let t: chrono::DateTime<chrono::FixedOffset> = row.get("output").unwrap();
630+
//! assert_eq!(t.to_string(), "2003-07-01 10:52:37 +02:00");
631+
//! assert!(result.next().await.unwrap().is_none());
632+
//!
633+
//! //send NaiveDateTime as parameter in the query
634+
//! let localdatetime = chrono::NaiveDateTime::parse_from_str("2015-07-01 08:55:59.123", "%Y-%m-%d %H:%M:%S%.f").unwrap();
635+
//!
636+
//! let mut result = graph
637+
//! .execute(query("RETURN $d as output").param("d", localdatetime))
638+
//! .await
639+
//! .unwrap();
640+
//! let row = result.next().await.unwrap().unwrap();
641+
//! let t: chrono::NaiveDateTime = row.get("output").unwrap();
642+
//! assert_eq!(t.to_string(), "2015-07-01 08:55:59.123");
643+
//! assert!(result.next().await.unwrap().is_none());
644+
//!
645+
//! //send NaiveDateTime with timezone id as parameter in the query
646+
//! let datetime = chrono::NaiveDateTime::parse_from_str("2015-07-03 08:55:59.555", "%Y-%m-%d %H:%M:%S%.f").unwrap();
647+
//! let timezone = "Europe/Paris";
648+
//!
649+
//! let mut result = graph
650+
//! .execute(query("RETURN $d as output").param("d", (datetime, timezone)))
651+
//! .await
652+
//! .unwrap();
653+
//! let row = result.next().await.unwrap().unwrap();
654+
//! let (time, zone): (chrono::NaiveDateTime, String) = row.get("output").unwrap();
655+
//! assert_eq!(time.to_string(), "2015-07-03 08:55:59.555");
656+
//! assert_eq!(zone, "Europe/Paris");
657+
//! assert!(result.next().await.unwrap().is_none());
658+
//!
659+
//! }
660+
//! ```
661+
//!
662+
//! ### Parsing DateTime from result
663+
//!
664+
//! ```
665+
//! use neo4rs::*;
666+
//! use futures::stream::*;
667+
//! use uuid::Uuid;
668+
//!
669+
//! #[tokio::main]
670+
//! async fn main() {
671+
//! let uri = "127.0.0.1:7687";
672+
//! let user = "neo4j";
673+
//! let pass = "neo";
674+
//! let graph = Graph::new(uri, user, pass).await.unwrap();
675+
//!
676+
//! //Parse NaiveDateTime from result
677+
//! let mut result = graph
678+
//! .execute(query(
679+
//! "WITH localdatetime('2015-06-24T12:50:35.556') AS t RETURN t",
680+
//! ))
681+
//! .await
682+
//! .unwrap();
683+
//! let row = result.next().await.unwrap().unwrap();
684+
//! let t: chrono::NaiveDateTime = row.get("t").unwrap();
685+
//! assert_eq!(t.to_string(), "2015-06-24 12:50:35.556");
686+
//! assert!(result.next().await.unwrap().is_none());
687+
//!
688+
//! //Parse DateTime from result
689+
//! let mut result = graph
690+
//! .execute(query(
691+
//! "WITH datetime('2015-06-24T12:50:35.777+0100') AS t RETURN t",
692+
//! ))
693+
//! .await
694+
//! .unwrap();
695+
//! let row = result.next().await.unwrap().unwrap();
696+
//! let t: chrono::DateTime<chrono::FixedOffset> = row.get("t").unwrap();
697+
//! assert_eq!(t.to_string(), "2015-06-24 12:50:35.777 +01:00");
698+
//! assert!(result.next().await.unwrap().is_none());
699+
//!
700+
//!
701+
//! //Parse NaiveDateTime with zone id from result
702+
//! let mut result = graph
703+
//! .execute(query(
704+
//! "WITH datetime({ year:1984, month:11, day:11, hour:12, minute:31, second:14, nanosecond: 645876123, timezone:'Europe/Stockholm' }) AS d return d",
705+
//! ))
706+
//! .await
707+
//! .unwrap();
708+
//! let row = result.next().await.unwrap().unwrap();
709+
//! let (datetime, zone_id): (chrono::NaiveDateTime, String) = row.get("d").unwrap();
710+
//! assert_eq!(datetime.to_string(), "1984-11-11 12:31:14.645876123");
711+
//! assert_eq!(zone_id, "Europe/Stockholm");
712+
//! assert!(result.next().await.unwrap().is_none());
713+
//!
714+
//! }
715+
//!
716+
//! ```
717+
//!
718+
//!
719+
//!
586720
//! ## Path
587721
//!
588722
//! ```

lib/src/types.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod binary;
22
pub mod boolean;
33
pub mod date;
4+
pub mod date_time;
45
pub mod duration;
56
pub mod float;
67
pub mod integer;
@@ -16,6 +17,7 @@ pub mod time;
1617
pub use binary::BoltBytes;
1718
pub use boolean::BoltBoolean;
1819
pub use date::BoltDate;
20+
pub use date_time::{BoltDateTime, BoltDateTimeZoneId, BoltLocalDateTime};
1921
pub use duration::BoltDuration;
2022
pub use float::BoltFloat;
2123
pub use integer::BoltInteger;
@@ -57,6 +59,9 @@ pub enum BoltType {
5759
Date(BoltDate),
5860
Time(BoltTime),
5961
LocalTime(BoltLocalTime),
62+
DateTime(BoltDateTime),
63+
LocalDateTime(BoltLocalDateTime),
64+
DateTimeZoneId(BoltDateTimeZoneId),
6065
}
6166

6267
impl Display for BoltType {
@@ -81,6 +86,9 @@ impl Hash for BoltType {
8186
BoltType::Date(t) => t.hash(state),
8287
BoltType::Time(t) => t.hash(state),
8388
BoltType::LocalTime(t) => t.hash(state),
89+
BoltType::DateTime(t) => t.hash(state),
90+
BoltType::LocalDateTime(t) => t.hash(state),
91+
BoltType::DateTimeZoneId(t) => t.hash(state),
8492
//The below types cannot be hashed
8593
BoltType::Path(_) => panic!("path not hashed"),
8694
BoltType::Bytes(_) => panic!("bytes not hashed"),
@@ -116,6 +124,9 @@ impl BoltType {
116124
BoltType::Date(t) => t.to_bytes(version),
117125
BoltType::Time(t) => t.to_bytes(version),
118126
BoltType::LocalTime(t) => t.to_bytes(version),
127+
BoltType::DateTime(t) => t.to_bytes(version),
128+
BoltType::LocalDateTime(t) => t.to_bytes(version),
129+
BoltType::DateTimeZoneId(t) => t.to_bytes(version),
119130
}
120131
}
121132

@@ -166,6 +177,15 @@ impl BoltType {
166177
input if BoltLocalTime::can_parse(version, input.clone()) => {
167178
BoltType::LocalTime(BoltLocalTime::parse(version, input)?)
168179
}
180+
input if BoltDateTime::can_parse(version, input.clone()) => {
181+
BoltType::DateTime(BoltDateTime::parse(version, input)?)
182+
}
183+
input if BoltLocalDateTime::can_parse(version, input.clone()) => {
184+
BoltType::LocalDateTime(BoltLocalDateTime::parse(version, input)?)
185+
}
186+
input if BoltDateTimeZoneId::can_parse(version, input.clone()) => {
187+
BoltType::DateTimeZoneId(BoltDateTimeZoneId::parse(version, input)?)
188+
}
169189
input if BoltUnboundedRelation::can_parse(version, input.clone()) => {
170190
BoltType::UnboundedRelation(BoltUnboundedRelation::parse(version, input)?)
171191
}

0 commit comments

Comments
 (0)