1use cxx::{type_id, ExternType};
7use std::fmt;
8
9#[cxx::bridge]
10mod ffi {
11 #[namespace = "Qt"]
12 unsafe extern "C++" {
13 include!("cxx-qt-lib/qt.h");
14 type DateFormat = crate::DateFormat;
15 }
16
17 unsafe extern "C++" {
18 include!("cxx-qt-lib/qstring.h");
19 type QString = crate::QString;
20
21 include!("cxx-qt-lib/qdate.h");
22 type QDate = super::QDate;
23
24 #[rust_name = "add_months"]
26 fn addMonths(self: &QDate, nmonths: i32) -> QDate;
27
28 #[rust_name = "add_years"]
30 fn addYears(self: &QDate, nyears: i32) -> QDate;
31
32 fn day(self: &QDate) -> i32;
34
35 #[rust_name = "day_of_week"]
37 fn dayOfWeek(self: &QDate) -> i32;
38
39 #[rust_name = "day_of_year"]
41 fn dayOfYear(self: &QDate) -> i32;
42
43 #[rust_name = "days_in_monyth"]
45 fn daysInMonth(self: &QDate) -> i32;
46
47 #[rust_name = "days_in_year"]
49 fn daysInYear(self: &QDate) -> i32;
50
51 #[rust_name = "is_null"]
53 fn isNull(self: &QDate) -> bool;
54
55 #[rust_name = "is_valid"]
57 fn isValid(self: &QDate) -> bool;
58
59 fn month(self: &QDate) -> i32;
63
64 #[rust_name = "set_date"]
67 fn setDate(self: &mut QDate, y: i32, m: i32, d: i32) -> bool;
68
69 #[rust_name = "format_enum"]
71 fn toString(self: &QDate, format: DateFormat) -> QString;
72
73 fn year(self: &QDate) -> i32;
75 }
76
77 #[namespace = "rust::cxxqtlib1"]
78 unsafe extern "C++" {
79 #[doc(hidden)]
80 #[rust_name = "qdate_add_days"]
81 fn qdateAddDays(date: &QDate, ndays: i64) -> QDate;
82
83 #[doc(hidden)]
84 #[rust_name = "qdate_current_date"]
85 fn qdateCurrentDate() -> QDate;
86
87 #[doc(hidden)]
88 #[rust_name = "qdate_days_to"]
89 fn qdateDaysTo(date: &QDate, d: QDate) -> i64;
90
91 #[doc(hidden)]
92 #[rust_name = "qdate_from_string"]
93 fn qdateFromString(string: &QString, format: &QString) -> QDate;
94 #[doc(hidden)]
95 #[rust_name = "qdate_from_string_enum"]
96 fn qdateFromString(string: &QString, format: DateFormat) -> QDate;
97
98 #[doc(hidden)]
99 #[rust_name = "qdate_is_leap_year"]
100 fn qdateIsLeapYear(year: i32) -> bool;
101
102 #[doc(hidden)]
103 #[rust_name = "qdate_to_format"]
104 fn qdateToFormat(date: &QDate, format: &QString) -> QString;
105 }
106
107 #[namespace = "rust::cxxqtlib1"]
108 unsafe extern "C++" {
109 include!("cxx-qt-lib/common.h");
110
111 #[doc(hidden)]
112 #[rust_name = "qdate_init_default"]
113 fn construct() -> QDate;
114 #[doc(hidden)]
115 #[rust_name = "qdate_init"]
116 fn construct(y: i32, m: i32, d: i32) -> QDate;
117 #[doc(hidden)]
118 #[rust_name = "qdate_to_qstring"]
119 fn toQString(value: &QDate) -> QString;
120 }
121}
122
123#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
125#[repr(C)]
126pub struct QDate {
127 jd: i64,
128}
129
130impl Default for QDate {
131 fn default() -> Self {
133 ffi::qdate_init_default()
134 }
135}
136
137impl fmt::Display for QDate {
138 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139 write!(f, "{}", ffi::qdate_to_qstring(self))
140 }
141}
142
143impl fmt::Debug for QDate {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 write!(f, "{self}")
146 }
147}
148
149impl QDate {
150 pub fn add_days(&self, ndays: i64) -> Self {
154 ffi::qdate_add_days(self, ndays)
155 }
156
157 pub fn current_date() -> Self {
159 ffi::qdate_current_date()
160 }
161
162 pub fn days_to(&self, date: Self) -> i64 {
166 ffi::qdate_days_to(self, date)
167 }
168
169 pub fn format(&self, format: &ffi::QString) -> ffi::QString {
171 ffi::qdate_to_format(self, format)
172 }
173
174 pub fn from_julian_day(jd: i64) -> Self {
176 Self { jd }
177 }
178
179 pub fn from_string(string: &ffi::QString, format: &ffi::QString) -> Option<Self> {
181 let date = ffi::qdate_from_string(string, format);
182 if date.is_valid() {
183 Some(date)
184 } else {
185 None
186 }
187 }
188
189 pub fn from_string_enum(string: &ffi::QString, format: ffi::DateFormat) -> Option<Self> {
191 let date = ffi::qdate_from_string_enum(string, format);
192 if date.is_valid() {
193 Some(date)
194 } else {
195 None
196 }
197 }
198
199 pub fn is_leap_year(year: i32) -> bool {
201 ffi::qdate_is_leap_year(year)
202 }
203
204 pub fn new(y: i32, m: i32, d: i32) -> Self {
206 ffi::qdate_init(y, m, d)
207 }
208
209 pub fn to_julian_day(&self) -> i64 {
211 self.jd
212 }
213}
214
215unsafe impl ExternType for QDate {
219 type Id = type_id!("QDate");
220 type Kind = cxx::kind::Trivial;
221}
222
223#[cfg(feature = "chrono")]
224use chrono::Datelike;
225
226#[cfg(feature = "chrono")]
227impl From<chrono::NaiveDate> for QDate {
228 fn from(value: chrono::NaiveDate) -> Self {
229 QDate::new(value.year(), value.month() as i32, value.day() as i32)
230 }
231}
232
233#[cfg(feature = "chrono")]
234impl TryFrom<QDate> for chrono::NaiveDate {
235 type Error = &'static str;
236
237 fn try_from(value: QDate) -> Result<Self, Self::Error> {
238 chrono::NaiveDate::from_ymd_opt(value.year(), value.month() as u32, value.day() as u32)
239 .ok_or("out-of-range date, invalid month and/or day")
240 }
241}
242
243#[cfg(feature = "time")]
244impl From<time::Date> for QDate {
245 fn from(value: time::Date) -> Self {
246 QDate::new(
247 value.year(),
248 Into::<u8>::into(value.month()) as i32,
249 value.day() as i32,
250 )
251 }
252}
253
254#[cfg(feature = "time")]
255impl TryFrom<QDate> for time::Date {
256 type Error = time::error::ComponentRange;
257
258 fn try_from(value: QDate) -> Result<Self, Self::Error> {
259 time::Date::from_calendar_date(
260 value.year(),
261 time::Month::try_from(value.month() as u8)?,
262 value.day() as u8,
263 )
264 }
265}
266
267#[cfg(test)]
268mod test {
269 use super::*;
270
271 #[test]
272 fn qdate_current_date() {
273 let date_a = QDate::current_date();
274 let date_b = date_a.add_days(100);
275 assert_eq!(date_a.days_to(date_b), 100);
276 }
277
278 #[test]
279 fn qdate_julian_day() {
280 let date_a = QDate::from_julian_day(1000);
281 let date_b = QDate::from_julian_day(1010);
282 assert_eq!(date_a.days_to(date_b), 10);
283 }
284
285 #[cfg(feature = "chrono")]
286 #[test]
287 fn qdate_from_chrono_naive() {
288 let naive = chrono::NaiveDate::from_ymd_opt(2023, 1, 1).unwrap();
289 let qdate = QDate::new(2023, 1, 1);
290 assert_eq!(QDate::from(naive), qdate);
291 }
292
293 #[cfg(feature = "chrono")]
294 #[test]
295 fn qdate_to_chrono_naive() {
296 let naive = chrono::NaiveDate::from_ymd_opt(2023, 1, 1).unwrap();
297 let qdate = QDate::new(2023, 1, 1);
298 assert_eq!(chrono::NaiveDate::try_from(qdate).unwrap(), naive);
299 }
300
301 #[cfg(feature = "time")]
302 #[test]
303 fn qdate_from_time_date() {
304 let time_date = time::Date::from_calendar_date(2023, time::Month::January, 1).unwrap();
305 let qdate = QDate::new(2023, 1, 1);
306 assert_eq!(QDate::from(time_date), qdate);
307 }
308
309 #[cfg(feature = "time")]
310 #[test]
311 fn qdate_to_time_date() {
312 let time_date = time::Date::from_calendar_date(2023, time::Month::January, 1).unwrap();
313 let qdate = QDate::new(2023, 1, 1);
314 assert_eq!(time::Date::try_from(qdate).unwrap(), time_date);
315 }
316}