Skip to main content

locale_settings/
time.rs

1/*!
2Fetch locale-specific date and time formatting settings.
3
4This module provides the details for both time and date formatting
5as well as a set of calendar names used for day and month display.
6
7## Date and Time Formatting
8
9The date and time formatting strings use the field specifiers from
10the C [`strftime`](https://man.openbsd.org/strftime.3) function. These
11strings may also be used with the chrono crate's
12[`format::strftime`](https://docs.rs/chrono/0.4.7/chrono/format/strftime/index.html)
13module.
14*/
15
16use crate::ffi::utils::*;
17use crate::ffi::*;
18use crate::Category;
19use locale_types::{Locale, LocaleResult};
20
21// ------------------------------------------------------------------------------------------------
22// Public Types
23// ------------------------------------------------------------------------------------------------
24
25/// This structure captures a name and its abbreviated form.
26#[derive(Debug, Clone, PartialEq)]
27pub struct Name {
28    /// Full name
29    name: Option<String>,
30    /// Abbreviated name
31    abbreviated: Option<String>,
32}
33
34/// The set of all calendar names.
35#[derive(Debug, Clone, PartialEq)]
36pub struct CalendarNames {
37    /// Names of the days of the week (Sunday .. Saturday)
38    week_day_names: Vec<Name>,
39    /// Names of the months in a year (January .. December)
40    month_names: Vec<Name>,
41    /// The string for AM time
42    am_string: Option<String>,
43    /// The string for PM time
44    pm_string: Option<String>,
45}
46
47/// The complete date and time formatting information.
48#[derive(Debug, Clone, PartialEq)]
49pub struct DateTimeFormat {
50    /// The string to use to format a complete date and time.
51    date_time_format: Option<String>,
52    /// The string to use to format a date.
53    date_format: Option<String>,
54    /// The string to use to format a time, in 24 hours.
55    time_format: Option<String>,
56    /// The string to use to format a time, with am/pm.
57    time_ampm_format: Option<String>,
58    /// Alternate era name
59    era: Option<String>,
60    /// The string to use to format a complete date and time in the alternate era.
61    era_date_time_format: Option<String>,
62    /// The string to use to format a date in the alternate era.
63    era_date_format: Option<String>,
64    /// The string to use to format a time in the alternate era.
65    era_time_format: Option<String>,
66    /// The alternate symbols for digits.
67    alternate_digit_symbol: Option<String>,
68}
69
70// ------------------------------------------------------------------------------------------------
71// Public Functions
72// ------------------------------------------------------------------------------------------------
73
74/// Fetch calendar names for days and months.
75pub fn get_calendar_names() -> CalendarNames {
76    CalendarNames {
77        week_day_names: make_name_vector(7, DAY_1, ABDAY_1),
78        month_names: make_name_vector(12, MON_1, ABMON_1),
79        am_string: get_nl_string(AM_STR),
80        pm_string: get_nl_string(PM_STR),
81    }
82}
83
84/// Fetch the calendar names for a specified `Locale`.
85///
86/// # Arguments
87///
88/// * `locale` - The locale to query.
89/// * `inherit_current` - Whether the specified locale should inherit
90///   from the current locale.
91///
92/// If `inherit_current` is `false` the `locale` specified will be treated
93/// as an entirely new and complete locale when calling the C
94/// [`newlocale`](https://man.openbsd.org/newlocale.3) function. If it is
95/// `true` the `locale` is assumed to be a partially specified one and inherits
96/// any unspecified components from the current locale. For example, if the
97/// current locale is `en_US.UTF-8` and the parameters passed are `_NZ` and
98/// `true` then the resulting locale will be `en_NZ.UTF-8`.
99pub fn get_calendar_names_for_locale(
100    locale: Locale,
101    inherit_current: bool,
102) -> LocaleResult<CalendarNames> {
103    get_format_for_locale(Category::Time, locale, &get_calendar_names, inherit_current)
104}
105
106/// Fetch the date and time formatting settings for the current locale.
107pub fn get_date_time_format() -> DateTimeFormat {
108    DateTimeFormat {
109        date_time_format: get_nl_string(D_T_FMT),
110        date_format: get_nl_string(D_FMT),
111        time_format: get_nl_string(T_FMT),
112        time_ampm_format: get_nl_string(T_FMT_AMPM),
113        era: get_nl_string(ERA),
114        era_date_time_format: get_nl_string(ERA_D_T_FMT),
115        era_date_format: get_nl_string(ERA_D_FMT),
116        era_time_format: get_nl_string(ERA_T_FMT),
117        alternate_digit_symbol: get_nl_string(ALT_DIGITS),
118    }
119}
120
121/// Fetch the date and time formatting rules for a specified `Locale`.
122///
123/// # Arguments
124///
125/// * `locale` - The locale to query.
126/// * `inherit_current` - Whether the specified locale should inherit
127///   from the current locale.
128///
129/// If `inherit_current` is `false` the `locale` specified will be treated
130/// as an entirely new and complete locale when calling the C
131/// [`newlocale`](https://man.openbsd.org/newlocale.3) function. If it is
132/// `true` the `locale` is assumed to be a partially specified one and inherits
133/// any unspecified components from the current locale. For example, if the
134/// current locale is `en_US.UTF-8` and the parameters passed are `_NZ` and
135/// `true` then the resulting locale will be `en_NZ.UTF-8`.
136pub fn get_date_time_format_for_locale(
137    locale: Locale,
138    inherit_current: bool,
139) -> LocaleResult<DateTimeFormat> {
140    get_format_for_locale(
141        Category::Time,
142        locale,
143        &get_date_time_format,
144        inherit_current,
145    )
146}
147
148// ------------------------------------------------------------------------------------------------
149// Private Functions
150// ------------------------------------------------------------------------------------------------
151
152fn make_name_vector(count: u32, n_st: u32, ab_st: u32) -> Vec<Name> {
153    (0..count)
154        .map(|offset| Name {
155            name: get_nl_string(n_st + offset),
156            abbreviated: get_nl_string(ab_st + offset),
157        })
158        .collect()
159}
160
161// ------------------------------------------------------------------------------------------------
162// Unit Tests
163// ------------------------------------------------------------------------------------------------
164
165#[cfg(test)]
166mod tests {
167    use super::{get_calendar_names, get_date_time_format};
168    use crate::locale::{set_locale, Category};
169    use crate::time::Name;
170    use locale_types::Locale;
171
172    // --------------------------------------------------------------------------------------------
173    #[test]
174    fn test_week_day_names() {
175        if set_locale(&Locale::POSIX, &Category::Time) {
176            let names = get_calendar_names();
177            assert_eq!(names.week_day_names.len(), 7);
178            let sunday = names.week_day_names.get(0).unwrap();
179            assert_eq!(
180                sunday,
181                &Name {
182                    name: Some("Sunday".to_string()),
183                    abbreviated: Some("Sun".to_string())
184                }
185            );
186        } else {
187            panic!("set_locale returned false");
188        }
189    }
190
191    #[test]
192    fn test_month_names() {
193        if set_locale(&Locale::POSIX, &Category::Time) {
194            let names = get_calendar_names();
195            assert_eq!(names.month_names.len(), 12);
196            let january = names.month_names.get(0).unwrap();
197            assert_eq!(
198                january,
199                &Name {
200                    name: Some("January".to_string()),
201                    abbreviated: Some("Jan".to_string())
202                }
203            );
204        } else {
205            panic!("set_locale returned false");
206        }
207    }
208
209    #[test]
210    fn test_ampm_names() {
211        if set_locale(&Locale::POSIX, &Category::Time) {
212            let names = get_calendar_names();
213            assert_eq!(names.am_string, Some("AM".to_string()));
214            assert_eq!(names.pm_string, Some("PM".to_string()));
215        } else {
216            panic!("set_locale returned false");
217        }
218    }
219
220    // --------------------------------------------------------------------------------------------
221    #[test]
222    fn test_date_time_formats() {
223        if set_locale(&Locale::POSIX, &Category::Time) {
224            let formats = get_date_time_format();
225            println!("{:#?}", formats);
226            assert_eq!(formats.date_format, Some("%m/%d/%y".to_string()));
227            assert_eq!(formats.era, None);
228        } else {
229            panic!("set_locale returned false");
230        }
231    }
232}