glib/
date.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{cmp, fmt, hash};
4
5use crate::{ffi, gobject_ffi, translate::*, BoolError, DateDay, DateMonth, DateWeekday, DateYear};
6
7wrapper! {
8    #[doc(alias = "GDate")]
9    pub struct Date(BoxedInline<ffi::GDate>);
10
11    match fn {
12        copy => |ptr| gobject_ffi::g_boxed_copy(ffi::g_date_get_type(), ptr as *const _) as *mut _,
13        free => |ptr| ffi::g_date_free(ptr),
14        type_ => || ffi::g_date_get_type(),
15    }
16}
17
18unsafe impl Send for Date {}
19unsafe impl Sync for Date {}
20
21impl Date {
22    #[doc(alias = "g_date_new_dmy")]
23    pub fn from_dmy(day: DateDay, month: DateMonth, year: DateYear) -> Result<Date, BoolError> {
24        let month = month.into_glib();
25        unsafe {
26            let check: bool = from_glib(ffi::g_date_valid_dmy(day, month, year));
27            if !check {
28                Err(bool_error!("Invalid date"))
29            } else {
30                Ok(from_glib_full(ffi::g_date_new_dmy(day, month, year)))
31            }
32        }
33    }
34
35    #[doc(alias = "g_date_new_julian")]
36    pub fn from_julian(julian_day: u32) -> Result<Date, BoolError> {
37        if !Self::valid_julian(julian_day) {
38            Err(bool_error!("Invalid date"))
39        } else {
40            unsafe { Ok(from_glib_full(ffi::g_date_new_julian(julian_day))) }
41        }
42    }
43
44    #[doc(alias = "g_date_add_days")]
45    pub fn add_days(&mut self, n_days: u32) -> Result<(), BoolError> {
46        let julian_days = self.julian();
47        if julian_days == 0 || n_days > u32::MAX - julian_days {
48            Err(bool_error!("Invalid date"))
49        } else {
50            unsafe {
51                ffi::g_date_add_days(self.to_glib_none_mut().0, n_days);
52            }
53            Ok(())
54        }
55    }
56
57    #[doc(alias = "g_date_add_months")]
58    pub fn add_months(&mut self, n_months: u32) -> Result<(), BoolError> {
59        // The checks for this function are just a mess in the C code, allowing intermediate
60        // unknown state. So for now, nothing can be done...
61        unsafe {
62            ffi::g_date_add_months(self.to_glib_none_mut().0, n_months);
63        }
64        Ok(())
65    }
66
67    #[doc(alias = "g_date_add_years")]
68    pub fn add_years(&mut self, n_years: u16) -> Result<(), BoolError> {
69        let year = self.year();
70        if n_years > u16::MAX - year {
71            Err(bool_error!("Invalid date"))
72        } else {
73            unsafe {
74                ffi::g_date_add_years(self.to_glib_none_mut().0, n_years as _);
75            }
76            Ok(())
77        }
78    }
79
80    #[doc(alias = "g_date_clamp")]
81    pub fn clamp(&mut self, min_date: &Date, max_date: &Date) -> Result<(), BoolError> {
82        if min_date >= max_date {
83            Err(bool_error!("`min_date` must be before `max_date`"))
84        } else {
85            unsafe {
86                ffi::g_date_clamp(
87                    self.to_glib_none_mut().0,
88                    min_date.to_glib_none().0,
89                    max_date.to_glib_none().0,
90                );
91            }
92            Ok(())
93        }
94    }
95
96    #[doc(alias = "g_date_compare")]
97    fn compare(&self, rhs: &Date) -> i32 {
98        unsafe { ffi::g_date_compare(self.to_glib_none().0, rhs.to_glib_none().0) }
99    }
100
101    #[doc(alias = "g_date_days_between")]
102    pub fn days_between(&self, date2: &Date) -> i32 {
103        unsafe { ffi::g_date_days_between(self.to_glib_none().0, date2.to_glib_none().0) }
104    }
105
106    #[doc(alias = "g_date_get_day")]
107    #[doc(alias = "get_day")]
108    pub fn day(&self) -> DateDay {
109        unsafe { ffi::g_date_get_day(self.to_glib_none().0) }
110    }
111
112    #[doc(alias = "g_date_get_day_of_year")]
113    #[doc(alias = "get_day_of_year")]
114    pub fn day_of_year(&self) -> u32 {
115        unsafe { ffi::g_date_get_day_of_year(self.to_glib_none().0) }
116    }
117
118    #[doc(alias = "g_date_get_iso8601_week_of_year")]
119    #[doc(alias = "get_iso8601_week_of_year")]
120    pub fn iso8601_week_of_year(&self) -> u32 {
121        unsafe { ffi::g_date_get_iso8601_week_of_year(self.to_glib_none().0) }
122    }
123
124    #[doc(alias = "g_date_get_julian")]
125    #[doc(alias = "get_julian")]
126    pub fn julian(&self) -> u32 {
127        unsafe { ffi::g_date_get_julian(self.to_glib_none().0) }
128    }
129
130    #[doc(alias = "g_date_get_monday_week_of_year")]
131    #[doc(alias = "get_monday_week_of_year")]
132    pub fn monday_week_of_year(&self) -> u32 {
133        unsafe { ffi::g_date_get_monday_week_of_year(self.to_glib_none().0) }
134    }
135
136    #[doc(alias = "g_date_get_month")]
137    #[doc(alias = "get_month")]
138    pub fn month(&self) -> DateMonth {
139        unsafe { from_glib(ffi::g_date_get_month(self.to_glib_none().0)) }
140    }
141
142    #[doc(alias = "g_date_get_sunday_week_of_year")]
143    #[doc(alias = "get_sunday_week_of_year")]
144    pub fn sunday_week_of_year(&self) -> u32 {
145        unsafe { ffi::g_date_get_sunday_week_of_year(self.to_glib_none().0) }
146    }
147
148    #[doc(alias = "g_date_get_weekday")]
149    #[doc(alias = "get_weekday")]
150    pub fn weekday(&self) -> DateWeekday {
151        unsafe { from_glib(ffi::g_date_get_weekday(self.to_glib_none().0)) }
152    }
153
154    #[doc(alias = "g_date_get_year")]
155    #[doc(alias = "get_year")]
156    pub fn year(&self) -> DateYear {
157        unsafe { ffi::g_date_get_year(self.to_glib_none().0) }
158    }
159
160    #[doc(alias = "g_date_is_first_of_month")]
161    pub fn is_first_of_month(&self) -> bool {
162        unsafe { from_glib(ffi::g_date_is_first_of_month(self.to_glib_none().0)) }
163    }
164
165    #[doc(alias = "g_date_is_last_of_month")]
166    pub fn is_last_of_month(&self) -> bool {
167        unsafe { from_glib(ffi::g_date_is_last_of_month(self.to_glib_none().0)) }
168    }
169
170    #[doc(alias = "g_date_order")]
171    pub fn order(&mut self, date2: &mut Date) {
172        unsafe {
173            ffi::g_date_order(self.to_glib_none_mut().0, date2.to_glib_none_mut().0);
174        }
175    }
176
177    #[doc(alias = "g_date_set_day")]
178    pub fn set_day(&mut self, day: DateDay) -> Result<(), BoolError> {
179        if !Self::valid_dmy(day, self.month(), self.year()) {
180            Err(bool_error!("invalid day"))
181        } else {
182            unsafe {
183                ffi::g_date_set_day(self.to_glib_none_mut().0, day);
184            }
185            Ok(())
186        }
187    }
188
189    #[doc(alias = "g_date_set_dmy")]
190    pub fn set_dmy(
191        &mut self,
192        day: DateDay,
193        month: DateMonth,
194        y: DateYear,
195    ) -> Result<(), BoolError> {
196        if !Self::valid_dmy(day, month, y) {
197            Err(bool_error!("invalid date"))
198        } else {
199            unsafe {
200                ffi::g_date_set_dmy(self.to_glib_none_mut().0, day, month.into_glib(), y);
201            }
202            Ok(())
203        }
204    }
205
206    #[doc(alias = "g_date_set_julian")]
207    pub fn set_julian(&mut self, julian_date: u32) -> Result<(), BoolError> {
208        if !Self::valid_julian(julian_date) {
209            Err(bool_error!("invalid date"))
210        } else {
211            unsafe {
212                ffi::g_date_set_julian(self.to_glib_none_mut().0, julian_date);
213            }
214            Ok(())
215        }
216    }
217
218    #[doc(alias = "g_date_set_month")]
219    pub fn set_month(&mut self, month: DateMonth) -> Result<(), BoolError> {
220        if !Self::valid_dmy(self.day(), month, self.year()) {
221            Err(bool_error!("invalid month"))
222        } else {
223            unsafe {
224                ffi::g_date_set_month(self.to_glib_none_mut().0, month.into_glib());
225            }
226            Ok(())
227        }
228    }
229
230    #[doc(alias = "g_date_set_parse")]
231    pub fn set_parse(&mut self, str: &str) -> Result<(), BoolError> {
232        let mut c = *self;
233        if !unsafe {
234            ffi::g_date_set_parse(c.to_glib_none_mut().0, str.to_glib_none().0);
235            ffi::g_date_valid(c.to_glib_none().0) == 0
236        } {
237            Err(bool_error!("invalid parse string"))
238        } else {
239            *self = c;
240            Ok(())
241        }
242    }
243
244    #[doc(alias = "g_date_set_time_t")]
245    pub fn set_time(&mut self, time_: libc::time_t) -> Result<(), BoolError> {
246        let mut c = *self;
247        unsafe {
248            ffi::g_date_set_time_t(c.to_glib_none_mut().0, time_ as _);
249        }
250        if !Self::valid_dmy(c.day(), c.month(), c.year()) {
251            Err(bool_error!("invalid time"))
252        } else {
253            *self = c;
254            Ok(())
255        }
256    }
257
258    //pub fn set_time_val(&mut self, timeval: /*Ignored*/&mut TimeVal) {
259    //    unsafe { TODO: call ffi::g_date_set_time_val() }
260    //}
261
262    #[doc(alias = "g_date_set_year")]
263    pub fn set_year(&mut self, year: DateYear) -> Result<(), BoolError> {
264        if !Self::valid_dmy(self.day(), self.month(), year) {
265            Err(bool_error!("invalid year"))
266        } else {
267            unsafe {
268                ffi::g_date_set_year(self.to_glib_none_mut().0, year);
269            }
270            Ok(())
271        }
272    }
273
274    #[doc(alias = "g_date_subtract_days")]
275    pub fn subtract_days(&mut self, n_days: u32) -> Result<(), BoolError> {
276        let julian = self.julian();
277        if julian > n_days {
278            Err(bool_error!("invalid number of days"))
279        } else {
280            unsafe {
281                ffi::g_date_subtract_days(self.to_glib_none_mut().0, n_days);
282            }
283            Ok(())
284        }
285    }
286
287    #[doc(alias = "g_date_subtract_months")]
288    pub fn subtract_months(&mut self, n_months: u32) -> Result<(), BoolError> {
289        // The checks for this function are just a mess in the C code, allowing intermediate
290        // unknown state. So for now, nothing can be done...
291        unsafe {
292            ffi::g_date_subtract_months(self.to_glib_none_mut().0, n_months);
293        }
294        Ok(())
295    }
296
297    #[doc(alias = "g_date_subtract_years")]
298    pub fn subtract_years(&mut self, n_years: u16) -> Result<(), BoolError> {
299        if self.year() < n_years {
300            Err(bool_error!("invalid number of years"))
301        } else {
302            unsafe {
303                ffi::g_date_subtract_years(self.to_glib_none_mut().0, n_years as _);
304            }
305            Ok(())
306        }
307    }
308
309    //#[doc(alias="g_date_to_struct_tm")]
310    //pub fn to_struct_tm(&self, tm: /*Unimplemented*/Fundamental: Pointer) {
311    //    unsafe { TODO: call ffi::g_date_to_struct_tm() }
312    //}
313
314    #[doc(alias = "g_date_valid")]
315    pub fn valid(&self) -> bool {
316        unsafe { from_glib(ffi::g_date_valid(self.to_glib_none().0)) }
317    }
318
319    #[doc(alias = "g_date_get_days_in_month")]
320    #[doc(alias = "get_days_in_month")]
321    pub fn days_in_month(month: DateMonth, year: DateYear) -> u8 {
322        unsafe { ffi::g_date_get_days_in_month(month.into_glib(), year) }
323    }
324
325    #[doc(alias = "g_date_get_monday_weeks_in_year")]
326    #[doc(alias = "get_monday_weeks_in_year")]
327    pub fn monday_weeks_in_year(year: DateYear) -> u8 {
328        unsafe { ffi::g_date_get_monday_weeks_in_year(year) }
329    }
330
331    #[doc(alias = "g_date_get_sunday_weeks_in_year")]
332    #[doc(alias = "get_sunday_weeks_in_year")]
333    pub fn sunday_weeks_in_year(year: DateYear) -> u8 {
334        unsafe { ffi::g_date_get_sunday_weeks_in_year(year) }
335    }
336
337    #[doc(alias = "g_date_is_leap_year")]
338    pub fn is_leap_year(year: DateYear) -> bool {
339        unsafe { from_glib(ffi::g_date_is_leap_year(year)) }
340    }
341
342    #[doc(alias = "g_date_strftime")]
343    pub fn strftime(s: &str, format: &str, date: &Date) -> usize {
344        let slen = s.len() as _;
345        unsafe {
346            ffi::g_date_strftime(
347                s.to_glib_none().0,
348                slen,
349                format.to_glib_none().0,
350                date.to_glib_none().0,
351            )
352        }
353    }
354
355    #[doc(alias = "g_date_valid_day")]
356    pub fn valid_day(day: DateDay) -> bool {
357        unsafe { from_glib(ffi::g_date_valid_day(day)) }
358    }
359
360    #[doc(alias = "g_date_valid_dmy")]
361    pub fn valid_dmy(day: DateDay, month: DateMonth, year: DateYear) -> bool {
362        unsafe { from_glib(ffi::g_date_valid_dmy(day, month.into_glib(), year)) }
363    }
364
365    #[doc(alias = "g_date_valid_julian")]
366    pub fn valid_julian(julian_date: u32) -> bool {
367        unsafe { from_glib(ffi::g_date_valid_julian(julian_date)) }
368    }
369
370    #[doc(alias = "g_date_valid_month")]
371    pub fn valid_month(month: DateMonth) -> bool {
372        unsafe { from_glib(ffi::g_date_valid_month(month.into_glib())) }
373    }
374
375    #[doc(alias = "g_date_valid_weekday")]
376    pub fn valid_weekday(weekday: DateWeekday) -> bool {
377        unsafe { from_glib(ffi::g_date_valid_weekday(weekday.into_glib())) }
378    }
379
380    #[doc(alias = "g_date_valid_year")]
381    pub fn valid_year(year: DateYear) -> bool {
382        unsafe { from_glib(ffi::g_date_valid_year(year)) }
383    }
384}
385
386impl PartialEq for Date {
387    #[inline]
388    fn eq(&self, other: &Self) -> bool {
389        self.compare(other) == 0
390    }
391}
392
393impl Eq for Date {}
394
395impl PartialOrd for Date {
396    #[inline]
397    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
398        Some(self.cmp(other))
399    }
400}
401
402impl Ord for Date {
403    #[inline]
404    fn cmp(&self, other: &Self) -> cmp::Ordering {
405        self.compare(other).cmp(&0)
406    }
407}
408
409impl fmt::Debug for Date {
410    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
411        f.debug_struct("Date")
412            .field("year", &self.year())
413            .field("month", &self.month())
414            .field("day", &self.day())
415            .finish()
416    }
417}
418
419impl hash::Hash for Date {
420    fn hash<H>(&self, state: &mut H)
421    where
422        H: hash::Hasher,
423    {
424        self.year().hash(state);
425        self.month().hash(state);
426        self.day().hash(state);
427    }
428}
429
430#[cfg(test)]
431mod test {
432    use super::*;
433    use crate::value::ToValue;
434
435    #[test]
436    fn test_value() {
437        let d1 = Date::from_dmy(20, crate::DateMonth::November, 2021).unwrap();
438        let v = d1.to_value();
439        let d2 = v.get::<&Date>().unwrap();
440
441        assert_eq!(&d1, d2);
442    }
443}