timezone_converter/lib.rs
1//! # Timezone Converter
2//!
3//! A Rust library for converting times between different timezones and getting timezone information.
4//! This library provides functionality to:
5//!
6//! - Convert times between any two timezones
7//! - Get current time in different timezones
8//! - Get timezone information including offset and DST status
9//! - Calculate time differences between timezones
10//!
11//! ## Example
12//! ```rust
13//! use timezone_converter::TimeZoneConverter;
14//!
15//! let converter = TimeZoneConverter::new("America/New_York", "Europe/London").unwrap();
16//! let current_time = converter.get_current_time_source().unwrap();
17//! let converted_time = converter.convert(current_time).unwrap();
18//! ```
19
20use chrono::{DateTime, TimeZone as ChronoTimeZone, Utc, Duration, Offset};
21use chrono_tz::{OffsetName, Tz};
22
23/// A struct that handles timezone conversions between a source and target timezone
24#[derive(Debug)]
25pub struct TimeZoneConverter {
26 /// The source timezone to convert from
27 source_tz: Tz,
28 /// The target timezone to convert to
29 target_tz: Tz,
30}
31
32/// Represents detailed information about a timezone
33#[derive(Debug)]
34pub struct TimeZoneInfo {
35 /// The name of the timezone (e.g., "America/New_York")
36 name: String,
37 /// The offset from UTC
38 offset: Duration,
39 /// Whether Daylight Saving Time is currently in effect
40 is_dst: bool,
41}
42
43/// Possible errors that can occur during timezone operations
44#[derive(Debug)]
45pub enum Errors {
46 /// Error when an invalid timezone identifier is provided
47 InvalidTimeZone(String),
48 /// Error when parsing datetime strings
49 ParseError(String),
50 /// Error during timezone conversion
51 ConversionError(String),
52}
53
54impl TimeZoneConverter {
55 /// Creates a new TimeZoneConverter instance
56 ///
57 /// # Arguments
58 ///
59 /// * `source` - The source timezone identifier (e.g., "America/New_York")
60 /// * `target` - The target timezone identifier (e.g., "Europe/London")
61 ///
62 /// # Returns
63 ///
64 /// * `Result<TimeZoneConverter, Errors>` - A new TimeZoneConverter instance or an error
65 ///
66 /// # Example
67 ///
68 /// ```rust
69 /// use timezone_converter::TimeZoneConverter;
70 ///
71 /// let converter = TimeZoneConverter::new("America/New_York", "Europe/London").unwrap();
72 /// ```
73 pub fn new(source: &str, target: &str) -> Result<Self, Errors> {
74 let source_tz = source.parse::<Tz>().map_err(|_| Errors::InvalidTimeZone(source.to_string()))?;
75 let target_tz = target.parse::<Tz>().map_err(|_| Errors::InvalidTimeZone(target.to_string()))?;
76
77 Ok(Self {
78 source_tz,
79 target_tz
80 })
81 }
82
83 /// Converts a datetime from the source timezone to the target timezone
84 ///
85 /// # Arguments
86 ///
87 /// * `datetime` - The datetime to convert
88 ///
89 /// # Returns
90 ///
91 /// * `Result<DateTime<Tz>, Errors>` - The converted datetime or an error
92 pub fn convert<T: ChronoTimeZone>(&self, datetime: DateTime<T>) -> Result<DateTime<Tz>, Errors> {
93 Ok(
94 datetime.with_timezone(&self.target_tz)
95 )
96 }
97
98 /// Gets the current time in the source timezone
99 ///
100 /// # Returns
101 ///
102 /// * `Result<DateTime<Tz>, Errors>` - The current time in the source timezone
103 pub fn get_current_time_source(&self) -> Result<DateTime<Tz>, Errors> {
104 Ok(
105 Utc::now().with_timezone(&self.source_tz)
106 )
107 }
108
109 /// Gets the current time in the target timezone
110 ///
111 /// # Returns
112 ///
113 /// * `Result<DateTime<Tz>, Errors>` - The current time in the target timezone
114 pub fn get_current_time_target(&self) -> Result<DateTime<Tz>, Errors> {
115 Ok(
116 Utc::now().with_timezone(&self.target_tz)
117 )
118 }
119
120 /// Gets detailed information about the source timezone
121 ///
122 /// # Returns
123 ///
124 /// * `Result<TimeZoneInfo, Errors>` - Information about the timezone including name, offset, and DST status
125 pub fn get_timezone_info(&self) -> Result<TimeZoneInfo, Errors> {
126 let now = Utc::now().with_timezone(&self.source_tz);
127 let offset = now.offset();
128
129 // Calculate the total offset in seconds
130 let total_offset_seconds = offset.fix().local_minus_utc();
131 // Determine if DST is in effect by checking the offset abbreviation
132 let is_dst = match offset.abbreviation() {
133 Some(abbr) => abbr.ends_with("DT"),
134 None => false,
135 };
136
137 Ok(TimeZoneInfo {
138 name: self.source_tz.name().to_string(),
139 offset: Duration::seconds(total_offset_seconds as i64),
140 is_dst,
141 })
142 }
143
144 /// Gets the time difference between source and target timezones in hours
145 ///
146 /// # Returns
147 ///
148 /// * `Result<f64, Errors>` - The time difference in hours (positive if source is ahead)
149 ///
150 /// # Example
151 ///
152 /// ```rust
153 /// use timezone_converter::TimeZoneConverter;
154 ///
155 /// let converter = TimeZoneConverter::new("America/New_York", "Europe/London").unwrap();
156 /// let difference = converter.get_time_difference().unwrap();
157 /// println!("Time difference: {} hours", difference);
158 /// ```
159 pub fn get_time_difference(&self) -> Result<f64, Errors> {
160 let now = Utc::now();
161
162 // Get the offsets for both timezones
163 let source_time = now.with_timezone(&self.source_tz);
164 let target_time = now.with_timezone(&self.target_tz);
165
166 let source_offset = source_time.offset().fix().local_minus_utc();
167 let target_offset = target_time.offset().fix().local_minus_utc();
168
169 // Convert seconds to hours (f64 for decimal hours)
170 Ok((source_offset - target_offset) as f64 / 3600.0)
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use chrono_tz::America::New_York;
178 use chrono_tz::Africa::Kampala;
179
180 #[test]
181 fn NewYorktoKampala() {
182 let timezone = TimeZoneConverter::new("America/New_York", "Africa/Kampala").unwrap();
183 // Create a specific time in New York
184 let ny_time = New_York.with_ymd_and_hms(2024, 11, 4, 10, 0, 0).unwrap();
185 let time = timezone.convert(ny_time).unwrap();
186 // Print both times to verify the conversion
187 println!("New York: {}", ny_time);
188 println!("Kampala: {}", time);
189 }
190
191 #[test]
192 fn NewYorkToKampala_current() {
193 let timezone = TimeZoneConverter::new("America/New_York", "Africa/Kampala").unwrap();
194 let ny_time = Utc::now().with_timezone(&New_York);
195 let time = timezone.convert(ny_time).unwrap();
196 println!("New York: {}", ny_time);
197 println!("Kampala: {}", time);
198 }
199
200 #[test]
201 fn get_timezone_info() {
202 let timezone = TimeZoneConverter::new("America/New_York", "Africa/Kampala").unwrap();
203 let info = timezone.get_timezone_info().unwrap();
204 println!("{:?}", info);
205 }
206
207 #[test]
208 fn get_time_difference() {
209 let timezone = TimeZoneConverter::new("America/New_York", "Africa/Kampala").unwrap();
210 let difference = timezone.get_time_difference().unwrap();
211 println!("Time difference: {} hours", difference);
212 }
213}