icu_capi/
timezone.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use ffi::TimeZoneInfo;
6
7#[diplomat::bridge]
8#[diplomat::abi_rename = "icu4x_{0}_mv1"]
9#[diplomat::attr(auto, namespace = "icu4x")]
10pub mod ffi {
11    use alloc::boxed::Box;
12
13    use crate::unstable::{
14        date::ffi::IsoDate,
15        datetime::ffi::IsoDateTime,
16        time::ffi::Time,
17        variant_offset::ffi::{UtcOffset, VariantOffsetsCalculator},
18    };
19
20    #[diplomat::opaque]
21    #[diplomat::rust_link(icu::time::TimeZone, Struct)]
22    pub struct TimeZone(pub(crate) icu_time::TimeZone);
23
24    impl TimeZone {
25        /// The unknown time zone.
26        #[diplomat::rust_link(icu::time::TimeZoneInfo::unknown, FnInStruct)]
27        #[diplomat::rust_link(icu::time::TimeZone::unknown, FnInStruct, hidden)]
28        #[diplomat::attr(auto, named_constructor)]
29        pub fn unknown() -> Box<TimeZone> {
30            Box::new(TimeZone(icu_time::TimeZone::UNKNOWN))
31        }
32
33        /// Whether the time zone is the unknown zone.
34        #[diplomat::rust_link(icu::time::TimeZone::is_unknown, FnInStruct)]
35        pub fn is_unknown(&self) -> bool {
36            self.0.is_unknown()
37        }
38
39        /// Creates a time zone from a BCP-47 string.
40        ///
41        /// Returns the unknown time zone if the string is not a valid BCP-47 subtag.
42        #[diplomat::rust_link(icu::time::TimeZone, Struct, compact)]
43        #[diplomat::attr(auto, named_constructor = "from_bcp47")]
44        #[diplomat::demo(default_constructor)]
45        pub fn create_from_bcp47(id: &DiplomatStr) -> Box<Self> {
46            icu_locale_core::subtags::Subtag::try_from_utf8(id)
47                .map(icu_time::TimeZone)
48                .map(TimeZone)
49                .map(Box::new)
50                .unwrap_or(Self::unknown())
51        }
52
53        #[diplomat::rust_link(icu::time::TimeZone::with_offset, FnInStruct)]
54        pub fn with_offset(&self, offset: &UtcOffset) -> Box<TimeZoneInfo> {
55            Box::new(self.0.with_offset(Some(offset.0)).into())
56        }
57
58        #[diplomat::rust_link(icu::time::TimeZone::without_offset, FnInStruct)]
59        pub fn without_offset(&self) -> Box<TimeZoneInfo> {
60            Box::new(self.0.without_offset().into())
61        }
62    }
63
64    #[diplomat::enum_convert(icu_time::zone::TimeZoneVariant, needs_wildcard)]
65    pub enum TimeZoneVariant {
66        Standard,
67        Daylight,
68    }
69
70    impl TimeZoneVariant {
71        #[diplomat::rust_link(icu::time::zone::TimeZoneVariant::from_rearguard_isdst, FnInEnum)]
72        #[diplomat::rust_link(icu::time::TimeZoneInfo::with_variant, FnInStruct)]
73        #[diplomat::rust_link(icu::time::zone::TimeZoneVariant, Enum, compact)]
74        pub fn from_rearguard_isdst(isdst: bool) -> Self {
75            icu_time::zone::TimeZoneVariant::from_rearguard_isdst(isdst).into()
76        }
77    }
78
79    #[diplomat::opaque]
80    #[diplomat::rust_link(icu::time::TimeZoneInfo, Struct)]
81    #[diplomat::rust_link(icu::time::zone::models::AtTime, Struct, hidden)]
82    #[diplomat::rust_link(icu::time::zone::models::Base, Struct, hidden)]
83    #[diplomat::rust_link(icu::time::zone::models::Full, Struct, hidden)]
84    pub struct TimeZoneInfo {
85        pub(crate) id: icu_time::TimeZone,
86        pub(crate) offset: Option<icu_time::zone::UtcOffset>,
87        pub(crate) variant: Option<icu_time::zone::TimeZoneVariant>,
88        pub(crate) zone_name_timestamp: Option<icu_time::zone::ZoneNameTimestamp>,
89    }
90
91    impl TimeZoneInfo {
92        /// Creates a time zone for UTC (Coordinated Universal Time).
93        #[diplomat::rust_link(icu::time::TimeZoneInfo::utc, FnInStruct)]
94        #[diplomat::rust_link(icu::time::zone::UtcOffset::zero, FnInStruct, hidden)]
95        #[diplomat::attr(auto, named_constructor)]
96        pub fn utc() -> Box<TimeZoneInfo> {
97            Box::new(icu_time::TimeZoneInfo::utc().into())
98        }
99
100        /// Creates a time zone info from parts.
101        #[diplomat::attr(auto, constructor)]
102        pub fn from_parts(
103            id: &TimeZone,
104            offset: Option<&UtcOffset>,
105            variant: Option<TimeZoneVariant>,
106        ) -> Box<TimeZoneInfo> {
107            Box::new(Self {
108                id: id.0,
109                offset: offset.map(|o| o.0),
110                variant: variant.map(Into::into),
111                zone_name_timestamp: None,
112            })
113        }
114
115        #[diplomat::rust_link(icu::time::TimeZoneInfo::id, FnInStruct)]
116        #[diplomat::attr(demo_gen, disable)] // this just returns a constructor argument
117        pub fn id(&self) -> Box<TimeZone> {
118            Box::new(TimeZone(self.id))
119        }
120
121        /// Sets the datetime at which to interpret the time zone
122        /// for display name lookup.
123        ///
124        /// Notes:
125        ///
126        /// - If not set, the formatting datetime is used if possible.
127        /// - The constraints are the same as with `ZoneNameTimestamp` in Rust.
128        /// - Set to year 1000 or 9999 for a reference far in the past or future.
129        #[diplomat::rust_link(icu::time::TimeZoneInfo::at_date_time_iso, FnInStruct)]
130        #[diplomat::rust_link(icu::time::zone::ZoneNameTimestamp, Struct, compact)]
131        #[diplomat::rust_link(
132            icu::time::TimeZoneInfo::with_zone_name_timestamp,
133            FnInStruct,
134            hidden
135        )]
136        #[diplomat::rust_link(
137            icu::time::zone::ZoneNameTimestamp::from_date_time_iso,
138            FnInStruct,
139            hidden
140        )]
141        #[diplomat::rust_link(
142            icu::time::zone::ZoneNameTimestamp::far_in_future,
143            FnInStruct,
144            hidden
145        )] // documented
146        #[diplomat::rust_link(icu::time::zone::ZoneNameTimestamp::far_in_past, FnInStruct, hidden)] // documented
147        pub fn at_date_time_iso(&self, date: &IsoDate, time: &Time) -> Box<Self> {
148            Box::new(Self {
149                zone_name_timestamp: Some(icu_time::zone::ZoneNameTimestamp::from_date_time_iso(
150                    icu_time::DateTime {
151                        date: date.0,
152                        time: time.0,
153                    },
154                )),
155                ..*self
156            })
157        }
158
159        #[diplomat::rust_link(icu::time::TimeZoneInfo::zone_name_timestamp, FnInStruct)]
160        #[diplomat::rust_link(
161            icu::time::zone::ZoneNameTimestamp::to_date_time_iso,
162            FnInStruct,
163            hidden
164        )]
165        pub fn zone_name_date_time(&self) -> Option<IsoDateTime> {
166            let datetime = self.zone_name_timestamp?.to_date_time_iso();
167            Some(IsoDateTime {
168                date: Box::new(IsoDate(datetime.date)),
169                time: Box::new(Time(datetime.time)),
170            })
171        }
172
173        #[diplomat::rust_link(icu::time::TimeZoneInfo::with_variant, FnInStruct)]
174        pub fn with_variant(&self, time_variant: TimeZoneVariant) -> Box<Self> {
175            Box::new(Self {
176                variant: Some(time_variant.into()),
177                ..*self
178            })
179        }
180
181        /// Infers the zone variant.
182        ///
183        /// Requires the offset and local time to be set.
184        #[diplomat::rust_link(icu::time::TimeZoneInfo::infer_variant, FnInStruct)]
185        #[diplomat::rust_link(icu::time::zone::TimeZoneVariant, Enum, compact)]
186        pub fn infer_variant(
187            &mut self,
188            offset_calculator: &VariantOffsetsCalculator,
189        ) -> Option<()> {
190            let info = self
191                .id
192                .with_offset(self.offset)
193                .with_zone_name_timestamp(self.zone_name_timestamp?)
194                .infer_variant(offset_calculator.0.as_borrowed());
195
196            self.id = info.id();
197            self.variant = Some(info.variant());
198            Some(())
199        }
200
201        #[diplomat::rust_link(icu::time::TimeZoneInfo::variant, FnInStruct)]
202        #[diplomat::attr(demo_gen, disable)] // this just returns a constructor argument
203        pub fn variant(&self) -> Option<TimeZoneVariant> {
204            self.variant.map(Into::into)
205        }
206    }
207}
208
209impl From<icu_time::zone::UtcOffset> for TimeZoneInfo {
210    fn from(other: icu_time::zone::UtcOffset) -> Self {
211        Self {
212            id: icu_time::TimeZone::UNKNOWN,
213            offset: Some(other),
214            variant: None,
215            zone_name_timestamp: None,
216        }
217    }
218}
219
220impl From<icu_time::TimeZoneInfo<icu_time::zone::models::Base>> for TimeZoneInfo {
221    fn from(other: icu_time::TimeZoneInfo<icu_time::zone::models::Base>) -> Self {
222        Self {
223            id: other.id(),
224            offset: other.offset(),
225            variant: None,
226            zone_name_timestamp: None,
227        }
228    }
229}
230
231impl From<icu_time::TimeZoneInfo<icu_time::zone::models::AtTime>> for TimeZoneInfo {
232    fn from(other: icu_time::TimeZoneInfo<icu_time::zone::models::AtTime>) -> Self {
233        Self {
234            id: other.id(),
235            offset: other.offset(),
236            variant: None,
237            zone_name_timestamp: Some(other.zone_name_timestamp()),
238        }
239    }
240}
241
242impl From<icu_time::TimeZoneInfo<icu_time::zone::models::Full>> for TimeZoneInfo {
243    fn from(other: icu_time::TimeZoneInfo<icu_time::zone::models::Full>) -> Self {
244        Self {
245            id: other.id(),
246            offset: other.offset(),
247            variant: Some(other.variant()),
248            zone_name_timestamp: Some(other.zone_name_timestamp()),
249        }
250    }
251}