1use e_utils::chrono::{
2 china_now, now, parse_datetime_offset, parse_timestamp, DateTime, FixedOffset, TimeZone, Utc,
3};
4
5use crate::Packet;
6use std::fmt::Display;
7use std::io::Cursor;
8use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs, UdpSocket};
9use std::time::Duration;
10
11#[derive(Clone, Debug)]
12pub struct Client {
13 pub packet: Packet,
14 pub timestamp: i64,
15 pub src: String,
16 pub read_timeout: Duration,
17 pub write_timeout: Duration,
18 pub target: SocketAddr,
19 pub offset: Option<FixedOffset>,
20 pub format: Option<String>,
21}
22impl Default for Client {
23 fn default() -> Self {
24 Self {
25 target: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 123)),
26 packet: Packet::new_client(),
27 timestamp: 0,
28 src: String::from("0.0.0.0:0"),
29 write_timeout: Duration::from_secs(5),
30 read_timeout: Duration::from_secs(5),
31 offset: None,
32 format: None,
33 }
34 }
35}
36impl Client {
37 pub fn new() -> Self {
53 Self::default()
54 }
55 pub fn offset(mut self, offset: Option<FixedOffset>) -> Self {
56 self.offset = offset;
57 self
58 }
59 pub fn format(mut self, format: Option<impl Into<String>>) -> Self {
60 self.format = format.map(|v| v.into());
61 self
62 }
63 pub fn target<A: ToSocketAddrs>(mut self, addr: A) -> e_utils::AnyResult<Self> {
64 self.target = addr
65 .to_socket_addrs()?
66 .next()
67 .ok_or("to_socket_addrs next")?;
68 Ok(self)
69 }
70 pub fn src_ip(mut self, addr: impl Into<String>) -> Self {
71 self.src = addr.into();
72 self
73 }
74 pub fn packet(mut self, packet: Packet) -> Self {
75 self.packet = packet;
76 self
77 }
78
79 pub fn request(mut self) -> e_utils::AnyResult<Self> {
80 let data: Vec<u8> = self.packet.clone().into();
81 let sock = UdpSocket::bind(&self.src)?;
82 sock.set_read_timeout(Some(self.read_timeout))?;
83 sock.set_write_timeout(Some(self.write_timeout))?;
84 let _sz = sock.send_to(&data, self.target)?;
85 let mut buf = vec![0; 48];
86 let _res = sock.recv(&mut buf)?;
87 let rdr = Cursor::new(&buf);
88 self.packet = Packet::try_from(rdr)?;
89 self.timestamp = DateTime::<Utc>::from(self.packet.transmit_time).timestamp();
90 Ok(self)
91 }
92 pub fn get_datetime_utc(&self) -> Option<DateTime<Utc>> {
94 DateTime::from_timestamp(self.timestamp, 0)
95 }
96 pub fn get_datetime_shanghai(&self) -> Option<DateTime<FixedOffset>> {
98 parse_timestamp(self.get_datetime_utc()?.timestamp())
99 }
100 fn format_datetime<T: TimeZone>(dt: &DateTime<T>, format: &Option<String>) -> String
101 where
102 T::Offset: Display,
103 {
104 match format {
105 Some(fmt) => dt.format(fmt).to_string(),
106 None => dt.to_string(),
107 }
108 }
109 pub fn get_datetime_str(&self) -> Option<String> {
111 Some(match self.offset {
112 Some(offset) => Self::format_datetime(
113 &parse_datetime_offset(self.get_datetime_utc()?.naive_utc(), offset)?,
114 &self.format,
115 ),
116 None => Self::format_datetime(&self.get_datetime_utc()?, &self.format),
117 })
118 }
119 pub fn now_zh() -> Option<DateTime<FixedOffset>> {
120 china_now()
121 }
122 pub fn now() -> DateTime<Utc> {
123 now()
124 }
125}
126
127#[test]
128fn test_request_ntp_org() {
129 let res = Client::new()
130 .target("0.pool.ntp.org:123")
131 .unwrap()
132 .request()
133 .unwrap();
134 res.get_datetime_str().unwrap();
135}