|
| 1 | +extern crate chrono; |
| 2 | + |
| 3 | +use std::io::prelude::*; |
| 4 | +use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; |
| 5 | +use self::chrono::{Timelike, Datelike, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC}; |
| 6 | + |
| 7 | +use Result; |
| 8 | +use types::{FromSql, ToSql, IsNull, Type}; |
| 9 | + |
| 10 | +const USEC_PER_SEC: i64 = 1_000_000; |
| 11 | +const NSEC_PER_USEC: i64 = 1_000; |
| 12 | + |
| 13 | +// Number of seconds from 1970-01-01 to 2000-01-01 |
| 14 | +const TIME_SEC_CONVERSION: i64 = 946684800; |
| 15 | + |
| 16 | +const POSTGRES_EPOCH_JDATE: i32 = 2451545; |
| 17 | + |
| 18 | +fn base() -> NaiveDateTime { |
| 19 | + NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0) |
| 20 | +} |
| 21 | + |
| 22 | +impl FromSql for NaiveDateTime { |
| 23 | + fn from_sql<R: Read>(_: &Type, raw: &mut R) -> Result<NaiveDateTime> { |
| 24 | + // FIXME should be this, blocked on lifthrasiir/rust-chrono#37 |
| 25 | + /* |
| 26 | + let t = try!(raw.read_i64::<BigEndian>()); |
| 27 | + Ok(base() + Duration::microseconds(t)) |
| 28 | + */ |
| 29 | + |
| 30 | + let t = try!(raw.read_i64::<BigEndian>()); |
| 31 | + let mut sec = t / USEC_PER_SEC + TIME_SEC_CONVERSION; |
| 32 | + let mut usec = t % USEC_PER_SEC; |
| 33 | + |
| 34 | + if usec < 0 { |
| 35 | + sec -= 1; |
| 36 | + usec = USEC_PER_SEC + usec; |
| 37 | + } |
| 38 | + |
| 39 | + Ok(NaiveDateTime::from_timestamp(sec, (usec * NSEC_PER_USEC) as u32)) |
| 40 | + } |
| 41 | + |
| 42 | + accepts!(Type::Timestamp); |
| 43 | +} |
| 44 | + |
| 45 | +impl ToSql for NaiveDateTime { |
| 46 | + fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W) -> Result<IsNull> { |
| 47 | + let t = (*self - base()).num_microseconds().unwrap(); |
| 48 | + try!(w.write_i64::<BigEndian>(t)); |
| 49 | + Ok(IsNull::No) |
| 50 | + } |
| 51 | + |
| 52 | + accepts!(Type::Timestamp); |
| 53 | + to_sql_checked!(); |
| 54 | +} |
| 55 | + |
| 56 | +impl FromSql for DateTime<UTC> { |
| 57 | + fn from_sql<R: Read>(type_: &Type, raw: &mut R) -> Result<DateTime<UTC>> { |
| 58 | + let naive = try!(NaiveDateTime::from_sql(type_, raw)); |
| 59 | + Ok(DateTime::from_utc(naive, UTC)) |
| 60 | + } |
| 61 | + |
| 62 | + accepts!(Type::TimestampTZ); |
| 63 | +} |
| 64 | + |
| 65 | +impl ToSql for DateTime<UTC> { |
| 66 | + fn to_sql<W: Write+?Sized>(&self, type_: &Type, mut w: &mut W) -> Result<IsNull> { |
| 67 | + self.naive_utc().to_sql(type_, w) |
| 68 | + } |
| 69 | + |
| 70 | + accepts!(Type::TimestampTZ); |
| 71 | + to_sql_checked!(); |
| 72 | +} |
| 73 | + |
| 74 | +impl FromSql for NaiveDate { |
| 75 | + // adapted from j2date in src/interfaces/ecpg/pgtypeslib/dt_common.c |
| 76 | + fn from_sql<R: Read>(_: &Type, raw: &mut R) -> Result<NaiveDate> { |
| 77 | + let jd = try!(raw.read_i32::<BigEndian>()); |
| 78 | + |
| 79 | + let mut julian: u32 = (jd + POSTGRES_EPOCH_JDATE) as u32; |
| 80 | + julian += 32044; |
| 81 | + let mut quad: u32 = julian / 146097; |
| 82 | + let extra: u32 = (julian - quad * 146097) * 4 + 3; |
| 83 | + julian += 60 + quad * 3 + extra / 146097; |
| 84 | + quad = julian / 1461; |
| 85 | + julian -= quad * 1461; |
| 86 | + let mut y: i32 = (julian * 4 / 1461) as i32; |
| 87 | + julian = if y != 0 { (julian + 305) % 365 } else { (julian + 306) % 366 } + 123; |
| 88 | + y += (quad * 4) as i32; |
| 89 | + let year = y - 4800; |
| 90 | + quad = julian * 2141 / 65536; |
| 91 | + let day = julian - 7834 * quad / 256; |
| 92 | + let month = (quad + 10) % 12 + 1; |
| 93 | + |
| 94 | + Ok(NaiveDate::from_ymd(year, month as u32, day as u32)) |
| 95 | + } |
| 96 | + |
| 97 | + accepts!(Type::Date); |
| 98 | +} |
| 99 | + |
| 100 | +impl ToSql for NaiveDate { |
| 101 | + // adapted from date2j in src/interfaces/ecpg/pgtypeslib/dt_common.c |
| 102 | + fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W) -> Result<IsNull> { |
| 103 | + let mut y = self.year(); |
| 104 | + let mut m = self.month() as i32; |
| 105 | + let d = self.day() as i32; |
| 106 | + |
| 107 | + if m > 2 { |
| 108 | + m += 1; |
| 109 | + y += 4800; |
| 110 | + } else { |
| 111 | + m += 13; |
| 112 | + y += 4799; |
| 113 | + } |
| 114 | + |
| 115 | + let century = y / 100; |
| 116 | + let mut julian = y * 365 - 32167; |
| 117 | + julian += y / 4 - century + century / 4; |
| 118 | + julian += 7834 * m / 256 + d; |
| 119 | + |
| 120 | + try!(w.write_i32::<BigEndian>(julian - POSTGRES_EPOCH_JDATE)); |
| 121 | + Ok(IsNull::No) |
| 122 | + } |
| 123 | + |
| 124 | + accepts!(Type::Date); |
| 125 | + to_sql_checked!(); |
| 126 | +} |
| 127 | + |
| 128 | +impl FromSql for NaiveTime { |
| 129 | + fn from_sql<R: Read>(_: &Type, raw: &mut R) -> Result<NaiveTime> { |
| 130 | + let mut usec = try!(raw.read_i64::<BigEndian>()); |
| 131 | + let mut sec = usec / USEC_PER_SEC; |
| 132 | + usec -= sec * USEC_PER_SEC; |
| 133 | + let mut min = sec / 60; |
| 134 | + sec -= min * 60; |
| 135 | + let hr = min / 60; |
| 136 | + min -= hr * 60; |
| 137 | + |
| 138 | + Ok(NaiveTime::from_hms_micro(hr as u32, min as u32, sec as u32, usec as u32)) |
| 139 | + } |
| 140 | + |
| 141 | + accepts!(Type::Time); |
| 142 | +} |
| 143 | + |
| 144 | +impl ToSql for NaiveTime { |
| 145 | + fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W) -> Result<IsNull> { |
| 146 | + let hr = self.hour() as i64; |
| 147 | + let min = hr * 60 + self.minute() as i64; |
| 148 | + let sec = min * 60 + self.second() as i64; |
| 149 | + let usec = sec * USEC_PER_SEC + self.nanosecond() as i64 / NSEC_PER_USEC; |
| 150 | + |
| 151 | + try!(w.write_i64::<BigEndian>(usec)); |
| 152 | + Ok(IsNull::No) |
| 153 | + } |
| 154 | + |
| 155 | + accepts!(Type::Time); |
| 156 | + to_sql_checked!(); |
| 157 | +} |
0 commit comments