2019-04-22 11:58:13 +02:00
|
|
|
|
// Copyright (c) 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
|
|
|
|
|
//
|
|
|
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
|
2025-04-03 18:04:42 +02:00
|
|
|
|
use alloc::borrow::Cow;
|
|
|
|
|
|
|
2024-07-09 17:01:42 +02:00
|
|
|
|
use xso::{AsXml, FromXml};
|
2024-06-21 17:54:12 +02:00
|
|
|
|
|
2025-04-03 18:04:42 +02:00
|
|
|
|
use crate::date::Xep0082;
|
2019-04-22 11:58:13 +02:00
|
|
|
|
use crate::iq::{IqGetPayload, IqResultPayload};
|
|
|
|
|
|
use crate::ns;
|
2025-04-03 18:04:42 +02:00
|
|
|
|
use chrono::{DateTime, FixedOffset, Utc};
|
2025-01-25 20:35:47 +01:00
|
|
|
|
use core::str::FromStr;
|
2025-04-03 18:04:42 +02:00
|
|
|
|
use xso::{error::Error, text::TextCodec};
|
|
|
|
|
|
|
|
|
|
|
|
struct ColonSeparatedOffset;
|
|
|
|
|
|
|
|
|
|
|
|
impl TextCodec<FixedOffset> for ColonSeparatedOffset {
|
|
|
|
|
|
fn decode(&self, s: String) -> Result<FixedOffset, Error> {
|
2025-04-07 21:53:22 +02:00
|
|
|
|
FixedOffset::from_str(&s).map_err(Error::text_parse_error)
|
2025-04-03 18:04:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn encode<'x>(&self, value: &'x FixedOffset) -> Result<Option<Cow<'x, str>>, Error> {
|
|
|
|
|
|
let offset = value.local_minus_utc();
|
|
|
|
|
|
let nminutes = offset / 60;
|
|
|
|
|
|
let nseconds = offset % 60;
|
|
|
|
|
|
let nhours = nminutes / 60;
|
|
|
|
|
|
let nminutes = nminutes % 60;
|
|
|
|
|
|
if nseconds == 0 {
|
|
|
|
|
|
Ok(Some(Cow::Owned(format!("{:+03}:{:02}", nhours, nminutes))))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Ok(Some(Cow::Owned(format!(
|
|
|
|
|
|
"{:+03}:{:02}:{:02}",
|
|
|
|
|
|
nhours, nminutes, nseconds
|
|
|
|
|
|
))))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2019-04-22 11:58:13 +02:00
|
|
|
|
|
2024-06-21 17:54:12 +02:00
|
|
|
|
/// An entity time query.
|
2024-07-09 17:01:42 +02:00
|
|
|
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
2024-06-21 17:54:12 +02:00
|
|
|
|
#[xml(namespace = ns::TIME, name = "time")]
|
|
|
|
|
|
pub struct TimeQuery;
|
2019-04-22 11:58:13 +02:00
|
|
|
|
|
|
|
|
|
|
impl IqGetPayload for TimeQuery {}
|
|
|
|
|
|
|
2019-04-22 13:33:29 +02:00
|
|
|
|
/// An entity time result, containing an unique DateTime.
|
2025-04-03 18:04:42 +02:00
|
|
|
|
#[derive(Debug, Clone, Copy, FromXml, AsXml)]
|
|
|
|
|
|
#[xml(namespace = ns::TIME, name = "time")]
|
|
|
|
|
|
pub struct TimeResult {
|
|
|
|
|
|
/// The UTC offset
|
|
|
|
|
|
#[xml(extract(name = "tzo", fields(text(codec = ColonSeparatedOffset))))]
|
|
|
|
|
|
pub tz_offset: FixedOffset,
|
|
|
|
|
|
|
|
|
|
|
|
/// The UTC timestamp
|
|
|
|
|
|
#[xml(extract(name = "utc", fields(text(codec = Xep0082))))]
|
|
|
|
|
|
pub utc: DateTime<Utc>,
|
|
|
|
|
|
}
|
2019-04-22 11:58:13 +02:00
|
|
|
|
|
|
|
|
|
|
impl IqResultPayload for TimeResult {}
|
|
|
|
|
|
|
2025-04-03 18:04:42 +02:00
|
|
|
|
impl From<TimeResult> for DateTime<FixedOffset> {
|
|
|
|
|
|
fn from(time: TimeResult) -> Self {
|
|
|
|
|
|
time.utc.with_timezone(&time.tz_offset)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2019-04-22 11:58:13 +02:00
|
|
|
|
|
2025-04-03 18:04:42 +02:00
|
|
|
|
impl From<TimeResult> for DateTime<Utc> {
|
|
|
|
|
|
fn from(time: TimeResult) -> Self {
|
2025-04-07 21:53:22 +02:00
|
|
|
|
time.utc
|
2025-04-03 18:04:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2019-04-22 11:58:13 +02:00
|
|
|
|
|
2025-04-03 18:04:42 +02:00
|
|
|
|
impl From<DateTime<FixedOffset>> for TimeResult {
|
|
|
|
|
|
fn from(dt: DateTime<FixedOffset>) -> Self {
|
|
|
|
|
|
let tz_offset = *dt.offset();
|
|
|
|
|
|
let utc = dt.with_timezone(&Utc);
|
2025-04-07 21:53:22 +02:00
|
|
|
|
TimeResult { tz_offset, utc }
|
2019-04-22 11:58:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-03 18:04:42 +02:00
|
|
|
|
impl From<DateTime<Utc>> for TimeResult {
|
|
|
|
|
|
fn from(dt: DateTime<Utc>) -> Self {
|
|
|
|
|
|
TimeResult {
|
|
|
|
|
|
tz_offset: FixedOffset::east_opt(0).unwrap(),
|
2025-04-07 21:53:22 +02:00
|
|
|
|
utc: dt,
|
2025-04-03 18:04:42 +02:00
|
|
|
|
}
|
2019-04-22 11:58:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
2025-04-03 18:04:42 +02:00
|
|
|
|
use minidom::Element;
|
|
|
|
|
|
|
2019-04-22 11:58:13 +02:00
|
|
|
|
// DateTime’s size doesn’t depend on the architecture.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_size() {
|
|
|
|
|
|
assert_size!(TimeQuery, 0);
|
|
|
|
|
|
assert_size!(TimeResult, 16);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn parse_response() {
|
|
|
|
|
|
let elem: Element =
|
|
|
|
|
|
"<time xmlns='urn:xmpp:time'><tzo>-06:00</tzo><utc>2006-12-19T17:58:35Z</utc></time>"
|
|
|
|
|
|
.parse()
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
let elem1 = elem.clone();
|
|
|
|
|
|
let time = TimeResult::try_from(elem).unwrap();
|
2025-04-03 18:04:42 +02:00
|
|
|
|
let dt = DateTime::<FixedOffset>::from(time);
|
|
|
|
|
|
assert_eq!(dt.timezone(), FixedOffset::west_opt(6 * 3600).unwrap());
|
2019-10-23 01:32:41 +02:00
|
|
|
|
assert_eq!(
|
2025-04-03 18:04:42 +02:00
|
|
|
|
dt,
|
|
|
|
|
|
DateTime::<FixedOffset>::from_str("2006-12-19T12:58:35-05:00").unwrap()
|
2019-10-23 01:32:41 +02:00
|
|
|
|
);
|
2019-04-22 11:58:13 +02:00
|
|
|
|
let elem2 = Element::from(time);
|
|
|
|
|
|
assert_eq!(elem1, elem2);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|