Skip to content

Commit

Permalink
Merge pull request #199 from 01mf02/chrono
Browse files Browse the repository at this point in the history
Use `chrono` instead of `time`.
  • Loading branch information
01mf02 authored Jul 30, 2024
2 parents d190391 + 45e0bdb commit 94d1135
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 103 deletions.
53 changes: 25 additions & 28 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion jaq-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ std = []
format = ["aho-corasick", "base64", "urlencoding"]
math = ["libm"]
parse_json = ["hifijson"]
time = ["chrono"]

[dependencies]
jaq-interpret = { version = "1.5.0", path = "../jaq-interpret" }
hifijson = { version = "0.2.0", optional = true }
time = { version = "0.3.20", optional = true, features = ["formatting", "parsing"] }
chrono = { version = "0.4.38", default-features = false, features = ["alloc"], optional = true }
regex = { version = "1.9", optional = true }
log = { version = "0.4.17", optional = true }
libm = { version = "0.2.7", optional = true }
Expand Down
48 changes: 17 additions & 31 deletions jaq-core/src/time.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,36 @@
use crate::{ValR2, ValT};
use alloc::string::{String, ToString};
use chrono::DateTime;
use jaq_interpret::Error;

/// Parse an ISO-8601 timestamp string to a number holding the equivalent UNIX timestamp
/// Parse an ISO 8601 timestamp string to a number holding the equivalent UNIX timestamp
/// (seconds elapsed since 1970/01/01).
///
/// Actually, this parses RFC 3339; see
/// <https://ijmacd.github.io/rfc3339-iso8601/> for differences.
/// jq also only parses a very restricted subset of ISO 8601.
pub fn from_iso8601<V: ValT>(s: &str) -> ValR2<V> {
use time::format_description::well_known::Iso8601;
use time::OffsetDateTime;
let datetime = OffsetDateTime::parse(s, &Iso8601::DEFAULT)
let dt = DateTime::parse_from_rfc3339(s)
.map_err(|e| Error::str(format_args!("cannot parse {s} as ISO-8601 timestamp: {e}")))?;
let epoch_s = datetime.unix_timestamp();
if s.contains('.') {
let seconds = epoch_s as f64 + (f64::from(datetime.nanosecond()) * 1e-9_f64);
Ok(seconds.into())
Ok((dt.timestamp_micros() as f64 * 1e-6_f64).into())
} else {
isize::try_from(epoch_s)
let seconds = dt.timestamp();
isize::try_from(seconds)
.map(V::from)
.or_else(|_| V::from_num(&epoch_s.to_string()))
.or_else(|_| V::from_num(&seconds.to_string()))
}
}

/// Format a number as an ISO-8601 timestamp string.
/// Format a number as an ISO 8601 timestamp string.
pub fn to_iso8601<V: ValT>(v: &V) -> Result<String, Error<V>> {
use time::format_description::well_known::iso8601;
use time::OffsetDateTime;
const SECONDS_CONFIG: iso8601::EncodedConfig = iso8601::Config::DEFAULT
.set_time_precision(iso8601::TimePrecision::Second {
decimal_digits: None,
})
.encode();

let fail1 = |e| Error::str(format_args!("cannot format {v} as ISO-8601 timestamp: {e}"));
let fail2 = |e| Error::str(format_args!("cannot format {v} as ISO-8601 timestamp: {e}"));

let fail = || Error::str(format_args!("cannot format {v} as ISO-8601 timestamp"));
if let Some(i) = v.as_isize() {
let iso8601_fmt_s = iso8601::Iso8601::<SECONDS_CONFIG>;
OffsetDateTime::from_unix_timestamp(i as i64)
.map_err(fail1)?
.format(&iso8601_fmt_s)
.map_err(fail2)
let dt = DateTime::from_timestamp(i as i64, 0).ok_or_else(fail)?;
Ok(dt.format("%Y-%m-%dT%H:%M:%SZ").to_string())
} else {
let f = v.as_f64()?;
let f_ns = (f * 1_000_000_000_f64).round() as i128;
OffsetDateTime::from_unix_timestamp_nanos(f_ns)
.map_err(fail1)?
.format(&iso8601::Iso8601::DEFAULT)
.map_err(fail2)
let dt = DateTime::from_timestamp_micros((f * 1e6_f64) as i64).ok_or_else(fail)?;
Ok(dt.format("%Y-%m-%dT%H:%M:%S%.fZ").to_string())
}
}
35 changes: 16 additions & 19 deletions jaq-core/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,22 @@ fn ascii() {
give(json!("aAaAäの"), "ascii_downcase", json!("aaaaäの"));
}

#[test]
fn dateiso8601() {
give(
json!("1970-01-02T00:00:00Z"),
"fromdateiso8601",
json!(86400),
);
give(
json!("1970-01-02T00:00:00.123456789Z"),
"fromdateiso8601",
json!(86400.123456789),
);
give(json!(86400), "todateiso8601", json!("1970-01-02T00:00:00Z"));
give(
json!(86400.123456789),
"todateiso8601",
json!("1970-01-02T00:00:00.123456789Z"),
);
}
yields!(
fromdate,
r#""1970-01-02T00:00:00Z" | fromdateiso8601"#,
86400
);
yields!(
fromdate_micros,
r#""1970-01-02T00:00:00.123456Z" | fromdateiso8601"#,
86400.123456
);
yields!(todate, r#"86400 | todateiso8601"#, "1970-01-02T00:00:00Z");
yields!(
todate_micros,
r#"86400.123456 | todateiso8601"#,
"1970-01-02T00:00:00.123456Z"
);

#[test]
fn explode_implode() {
Expand Down
47 changes: 23 additions & 24 deletions jaq-std/tests/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,34 +25,33 @@ fn any() {
give(json!({"a": false, "b": true}), "any", json!(true));
}

#[test]
fn date() {
// aliases for fromdateiso8601 and todateiso8601
give(json!("1970-01-02T00:00:00Z"), "fromdate", json!(86400));
give(
json!("1970-01-02T00:00:00.123456789Z"),
"fromdate",
json!(86400.123456789),
);
give(json!(86400), "todate", json!("1970-01-02T00:00:00Z"));
give(
json!(86400.123456789),
"todate",
json!("1970-01-02T00:00:00.123456789Z"),
);
}
// aliases for fromdateiso8601 and todateiso8601
yields!(fromdate, r#""1970-01-02T00:00:00Z" | fromdate"#, 86400);
yields!(
fromdate_mu,
r#""1970-01-02T00:00:00.123456Z" | fromdate"#,
86400.123456
);
yields!(todate, r#"86400 | todate"#, "1970-01-02T00:00:00Z");
yields!(
todate_mu,
r#"86400.123456 | todate"#,
"1970-01-02T00:00:00.123456Z"
);

#[test]
fn date_roundtrip() {
let epoch = 946684800;
give(json!(epoch), "todate|fromdate", json!(epoch));
let epoch_ns = 946684800.123456;
give(json!(epoch_ns), "todate|fromdate", json!(epoch_ns));
yields!(tofromdate, "946684800|todate|fromdate", 946684800);
yields!(
tofromdate_mu,
"946684800.123456|todate|fromdate",
946684800.123456
);

#[test]
fn fromtodate() {
let iso = "2000-01-01T00:00:00Z";
give(json!(iso), "fromdate|todate", json!(iso));
let iso_ns = "2000-01-01T00:00:00.123456000Z";
give(json!(iso_ns), "fromdate|todate", json!(iso_ns));
let iso_mu = "2000-01-01T00:00:00.123456Z";
give(json!(iso_mu), "fromdate|todate", json!(iso_mu));
}

yields!(
Expand Down

0 comments on commit 94d1135

Please sign in to comment.