1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use jiff::tz::TimeZone;
use crate::{
Result,
stmt::{Type, Value},
};
impl Type {
/// Casts a [`Value`] to this jiff temporal type, returning the converted value.
///
/// Supports conversions between:
/// - `String` and any jiff type (parsing ISO 8601 format)
/// - Any jiff type and `String` (formatting with fixed 9-digit nanosecond precision)
/// - `Timestamp` and `Zoned` (via UTC timezone)
/// - `Timestamp`/`Zoned` and `DateTime` (via UTC timezone)
///
/// Returns `Ok(None)` if the conversion is not supported for this
/// value/type combination. Returns `Err` if parsing fails.
pub fn cast_jiff(&self, value: &Value) -> Result<Option<Value>> {
Ok(Some(match (value, self) {
// String -> jiff
(Value::String(value), Type::Timestamp) => {
let v = value.clone();
Value::Timestamp(
value.parse().map_err(|_| {
crate::Error::type_conversion(Value::String(v), "Timestamp")
})?,
)
}
(Value::String(value), Type::Zoned) => {
let v = value.clone();
Value::Zoned(
value
.parse()
.map_err(|_| crate::Error::type_conversion(Value::String(v), "Zoned"))?,
)
}
(Value::String(value), Type::Date) => {
let v = value.clone();
Value::Date(
value
.parse()
.map_err(|_| crate::Error::type_conversion(Value::String(v), "Date"))?,
)
}
(Value::String(value), Type::Time) => {
let v = value.clone();
Value::Time(
value
.parse()
.map_err(|_| crate::Error::type_conversion(Value::String(v), "Time"))?,
)
}
(Value::String(value), Type::DateTime) => {
let v = value.clone();
Value::DateTime(
value
.parse()
.map_err(|_| crate::Error::type_conversion(Value::String(v), "DateTime"))?,
)
}
// Identity
(value @ Value::Timestamp(_), Type::Timestamp) => value.clone(),
(value @ Value::Zoned(_), Type::Zoned) => value.clone(),
(value @ Value::Date(_), Type::Date) => value.clone(),
(value @ Value::Time(_), Type::Time) => value.clone(),
(value @ Value::DateTime(_), Type::DateTime) => value.clone(),
// jiff -> String
//
// Types with sub-second precision use fixed 9-digit nanosecond
// formatting so that the resulting strings sort lexicographically
// in chronological order (ISO 8601 guarantees this for
// uniform-precision representations).
(Value::Timestamp(value), Type::String) => Value::String(format!("{value:.9}")),
(Value::Zoned(value), Type::String) => Value::String(format!("{value:.9}")),
(Value::Date(value), Type::String) => Value::String(value.to_string()),
(Value::Time(value), Type::String) => Value::String(format!("{value:.9}")),
(Value::DateTime(value), Type::String) => Value::String(format!("{value:.9}")),
// UTC <-> Zoned
(Value::Timestamp(value), Type::Zoned) => Value::Zoned(value.to_zoned(TimeZone::UTC)),
(Value::Zoned(value), Type::Timestamp) => Value::Timestamp(value.into()),
// UTC <-> Civil
(Value::Timestamp(value), Type::DateTime) => {
Value::DateTime(value.to_zoned(TimeZone::UTC).into())
}
(Value::DateTime(value), Type::Timestamp) => Value::Timestamp(
value
.to_zoned(TimeZone::UTC)
.expect("value was too close to minimum or maximum DateTime")
.into(),
),
// Zoned <-> Civil
(Value::Zoned(value), Type::DateTime) => Value::DateTime(value.into()),
(Value::DateTime(value), Type::Zoned) => Value::Zoned(
value
.to_zoned(TimeZone::UTC)
.expect("value was too close to minimum or maximum DateTime"),
),
_ => return Ok(None),
}))
}
}