multisql/data/value/methods/
timestamp.rs1use {
2 crate::{Convert, Result, Value, ValueError},
3 chrono::{Datelike, NaiveDate, NaiveDateTime, Timelike},
4 fstrings::*,
5 std::{
6 cmp::min,
7 convert::TryInto,
8 panic,
9 time::{SystemTime, UNIX_EPOCH},
10 },
11};
12
13macro_rules! protect_null {
14 ($protect: expr) => {
15 match $protect {
16 Value::Null => return Ok(Value::Null),
17 other => other,
18 }
19 };
20}
21
22macro_rules! expect_arguments {
23 ($arguments: expr, $expect: expr) => {
24 match $arguments.len() {
25 $expect => (),
26 found => {
27 return Err(ValueError::NumberOfFunctionParamsNotMatching {
28 expected: $expect,
29 found,
30 }
31 .into())
32 }
33 }
34 };
35}
36
37macro_rules! optional_expect_arguments {
38 ($arguments: expr, $min: expr, $max: expr) => {
39 match $arguments.len() {
40 len if ($min..=$max).contains(&len) => (),
41 found => {
42 return Err(ValueError::NumberOfFunctionParamsNotMatching {
43 expected: $min,
44 found,
45 }
46 .into())
47 }
48 }
49 };
50}
51
52impl Value {
53 pub fn function_now(arguments: Vec<Self>) -> Result<Self> {
54 expect_arguments!(arguments, 0);
55 Value::now()
56 }
57 pub fn function_year(mut arguments: Vec<Self>) -> Result<Self> {
58 expect_arguments!(arguments, 1);
59 protect_null!(arguments.remove(0)).year()
60 }
61 pub fn function_month(mut arguments: Vec<Self>) -> Result<Self> {
62 expect_arguments!(arguments, 1);
63 protect_null!(arguments.remove(0)).month()
64 }
65 pub fn function_day(mut arguments: Vec<Self>) -> Result<Self> {
66 expect_arguments!(arguments, 1);
67 protect_null!(arguments.remove(0)).day()
68 }
69 pub fn function_hour(mut arguments: Vec<Self>) -> Result<Self> {
70 expect_arguments!(arguments, 1);
71 protect_null!(arguments.remove(0)).hour()
72 }
73 pub fn function_minute(mut arguments: Vec<Self>) -> Result<Self> {
74 expect_arguments!(arguments, 1);
75 protect_null!(arguments.remove(0)).minute()
76 }
77 pub fn function_second(mut arguments: Vec<Self>) -> Result<Self> {
78 expect_arguments!(arguments, 1);
79 protect_null!(arguments.remove(0)).second()
80 }
81
82 pub fn function_timestamp_add(mut arguments: Vec<Self>) -> Result<Self> {
83 expect_arguments!(arguments, 3);
84 arguments.remove(0).date_add(
85 protect_null!(arguments.remove(0)),
86 protect_null!(arguments.remove(0)),
87 )
88 }
89 pub fn function_timestamp_from_parts(arguments: Vec<Self>) -> Result<Self> {
90 optional_expect_arguments!(arguments, 1, 6);
91 protect_null!(arguments.get(0).cloned().unwrap_or(Value::I64(1))).date_from_parts(
92 protect_null!(arguments.get(1).cloned().unwrap_or(Value::I64(1))),
93 protect_null!(arguments.get(2).cloned().unwrap_or(Value::I64(1))),
94 protect_null!(arguments.get(3).cloned().unwrap_or(Value::I64(0))),
95 protect_null!(arguments.get(4).cloned().unwrap_or(Value::I64(0))),
96 protect_null!(arguments.get(5).cloned().unwrap_or(Value::I64(0))),
97 )
98 }
99}
100
101impl Value {
103 pub fn now() -> Result<Value> {
104 Ok(Value::I64(
105 NaiveDateTime::from_timestamp(
106 SystemTime::now()
107 .duration_since(UNIX_EPOCH)
108 .unwrap()
109 .as_secs() as i64,
110 0,
111 )
112 .timestamp(),
113 ))
114 }
115}
116
117impl Value {
119 pub fn year(self) -> Result<Value> {
120 let datetime: NaiveDateTime = self.convert()?;
121 Ok(Value::I64(datetime.year() as i64))
122 }
123 pub fn month(self) -> Result<Value> {
124 let datetime: NaiveDateTime = self.convert()?;
125 Ok(Value::I64(datetime.month() as i64))
126 }
127 pub fn day(self) -> Result<Value> {
128 let datetime: NaiveDateTime = self.convert()?;
129 Ok(Value::I64(datetime.day() as i64))
130 }
131 pub fn hour(self) -> Result<Value> {
132 let datetime: NaiveDateTime = self.convert()?;
133 Ok(Value::I64(datetime.hour() as i64))
134 }
135 pub fn minute(self) -> Result<Value> {
136 let datetime: NaiveDateTime = self.convert()?;
137 Ok(Value::I64(datetime.minute() as i64))
138 }
139 pub fn second(self) -> Result<Value> {
140 let datetime: NaiveDateTime = self.convert()?;
141 Ok(Value::I64(datetime.second() as i64))
142 }
143}
144
145impl Value {
147 pub fn date_add(self, amount: Value, datetime: Value) -> Result<Value> {
148 let datetime: NaiveDateTime = datetime.convert()?;
149 let amount: i64 = amount.convert()?;
150 let amount: i32 = amount.try_into().map_err(|_| ValueError::DateError)?;
151 if amount > 100_000 {
152 panic!("Looks like you put the amount and timestamp the wrong way around. This will be fixed in future by using different datatypes");
153 }
154
155 match self {
156 Value::Str(string) if string == "YEAR" => {
157 let years = datetime.year() + amount as i32;
158 let calculated = datetime.with_year(years).unwrap_or_else(
159 || {
160 datetime
161 .with_day(28)
162 .ok_or(ValueError::DateError)
163 .unwrap() .with_year(years)
165 .ok_or(ValueError::DateError)
166 .unwrap()
167 }, );
169 Ok(Value::I64(calculated.timestamp()))
170 }
171 Value::Str(string) if string == "MONTH" => {
172 let month: i32 = datetime
173 .month()
174 .try_into()
175 .map_err(|_| ValueError::DateError)?;
176
177 let months = month + amount;
178 let month = ((months - 1) % 12) + 1;
179
180 let years = (months - month) / 12;
181 let month: u32 = month.try_into().map_err(|_| ValueError::DateError).unwrap(); let (years, month) = if month == 0 { (-1, 12) } else { (years, month) }; let next_month = if datetime.month() == 12 {
186 NaiveDate::from_ymd(datetime.year() + 1, 1, 1)
187 } else {
188 NaiveDate::from_ymd(datetime.year(), datetime.month() + 1, 1)
189 };
190 let this_month = NaiveDate::from_ymd(datetime.year(), datetime.month(), 1);
191
192 let month_days: u32 = NaiveDate::signed_duration_since(next_month, this_month)
193 .num_days()
194 .try_into()
195 .map_err(|_| ValueError::DateError)?;
196
197 let day = min(datetime.day(), month_days);
198 let calculated = datetime
199 .with_day(day)
200 .ok_or(ValueError::DateError)
201 .unwrap() .with_month(month)
203 .ok_or(ValueError::DateError)
204 .unwrap(); let calculated = Value::I64(calculated.timestamp());
207 Value::Str(String::from("YEAR")).date_add(Value::I64(years as i64), calculated)
208 }
209 Value::Str(string) if string == "DAY" => {
210 let day: i32 = datetime
211 .day()
212 .try_into()
213 .map_err(|_| ValueError::DateError)?;
214 let days = day + amount;
215
216 let next_month = if datetime.month() == 12 {
217 NaiveDate::from_ymd(datetime.year() + 1, 1, 1)
218 } else {
219 NaiveDate::from_ymd(datetime.year(), datetime.month() + 1, 1)
220 };
221 let this_month = NaiveDate::from_ymd(datetime.year(), datetime.month(), 1);
222
223 let month_days: i32 = NaiveDate::signed_duration_since(next_month, this_month)
224 .num_days()
225 .try_into()
226 .map_err(|_| ValueError::DateError)?;
227
228 if days > month_days {
229 let first_day = datetime.with_day(1).ok_or(ValueError::DateError)?;
230 let next_month = Value::Str(String::from("MONTH"))
231 .date_add(Value::I64(1), Value::I64(first_day.timestamp()))?;
232 Value::Str(String::from("DAY")).date_add(
233 Value::I64(
234 (days - month_days - 1)
235 .try_into()
236 .map_err(|_| ValueError::DateError)
237 .unwrap(), ),
239 next_month,
240 )
241 } else if days <= 0 {
242 let prev_month = if datetime.month() == 1 {
243 NaiveDate::from_ymd(datetime.year() - 1, 12, 1)
244 } else {
245 NaiveDate::from_ymd(datetime.year(), datetime.month() - 1, 1)
246 };
247
248 let prev_month_days: i32 =
249 NaiveDate::signed_duration_since(this_month, prev_month)
250 .num_days()
251 .try_into()
252 .map_err(|_| ValueError::DateError)?;
253
254 let first_day = datetime.with_day(1).ok_or(ValueError::DateError)?;
255 let prev_month = Value::Str(String::from("MONTH"))
256 .date_add(Value::I64(-1), Value::I64(first_day.timestamp()))?;
257 Value::Str(String::from("DAY")).date_add(
258 Value::I64(
259 (days + prev_month_days - 1)
260 .try_into()
261 .map_err(|_| ValueError::DateError)
262 .unwrap(), ),
264 prev_month,
265 )
266 } else {
267 let day: u32 = days.try_into().map_err(|_| ValueError::DateError)?;
268 Ok(Value::I64(
269 datetime
270 .with_day(day)
271 .ok_or(ValueError::DateError)?
272 .timestamp(),
273 ))
274 }
275 }
276 _ => Err(ValueError::BadInput(self).into()),
277 }
278 }
279 pub fn date_from_parts(
280 self,
281 month: Value,
282 day: Value,
283 hour: Value,
284 minute: Value,
285 second: Value,
286 ) -> Result<Value> {
287 let (year, month, day, hour, minute, second): (i64, i64, i64, i64, i64, i64) = (
288 self.convert()?,
289 month.convert()?,
290 day.convert()?,
291 hour.convert()?,
292 minute.convert()?,
293 second.convert()?,
294 );
295 let (year, month, day, hour, minute, second): (i32, u32, u32, u32, u32, u32) = (
296 year.try_into().map_err(|_| ValueError::DateError)?,
297 month.try_into().map_err(|_| ValueError::DateError)?,
298 day.try_into().map_err(|_| ValueError::DateError)?,
299 hour.try_into().map_err(|_| ValueError::DateError)?,
300 minute.try_into().map_err(|_| ValueError::DateError)?,
301 second.try_into().map_err(|_| ValueError::DateError)?,
302 );
303 let datetime = panic::catch_unwind(|| {
304 NaiveDate::from_ymd(year, month, day).and_hms(hour, minute, second)
305 })
306 .map_err(|panic| {
307 ValueError::SpecifiedTimestampError(f!(
308 "{year=}, {month=}, {day=},\n{hour=}, {minute=}, {second=}\n{panic=:?}"
309 ))
310 })?;
311
312 Ok(Value::I64(datetime.timestamp()))
313 }
314}