@@ -34,70 +34,81 @@ cfg_langinfo! {
3434 /// Cached result of locale time format detection
3535 static TIME_FORMAT_CACHE : OnceLock <bool > = OnceLock :: new( ) ;
3636
37- /// Internal function that performs the actual locale detection
38- fn detect_12_hour_format ( ) -> bool {
37+ /// Safe wrapper around libc setlocale
38+ fn set_time_locale ( ) {
3939 unsafe {
40- // Set locale from environment variables (empty string = use LC_TIME/LANG env vars)
41- libc:: setlocale( libc:: LC_TIME , c"" . as_ptr( ) ) ;
40+ nix:: libc:: setlocale( nix:: libc:: LC_TIME , c"" . as_ptr( ) ) ;
41+ }
42+ }
4243
43- // Get the date/time format string from locale
44- let d_t_fmt_ptr = libc:: nl_langinfo( libc:: D_T_FMT ) ;
45- if d_t_fmt_ptr. is_null( ) {
46- return false ;
44+ /// Safe wrapper around libc nl_langinfo that returns Option<String>
45+ fn get_locale_info( item: nix:: libc:: nl_item) -> Option <String > {
46+ unsafe {
47+ let ptr = nix:: libc:: nl_langinfo( item) ;
48+ if ptr. is_null( ) {
49+ None
50+ } else {
51+ CStr :: from_ptr( ptr) . to_str( ) . ok( ) . map( String :: from)
52+ }
4753 }
54+ }
4855
49- let Ok ( format) = CStr :: from_ptr( d_t_fmt_ptr) . to_str( ) else {
50- return false ;
51- } ;
56+ /// Internal function that performs the actual locale detection
57+ fn detect_12_hour_format( ) -> bool {
58+ // Helper function to check for 12-hour format indicators
59+ fn has_12_hour_indicators( format_str: & str ) -> bool {
60+ format_str. contains( "%I" ) || format_str. contains( "%l" ) || format_str. contains( "%r" )
61+ }
5262
53- // Check for 12-hour indicators first (higher priority)
54- // %I = hour (01-12), %l = hour (1-12) space-padded, %r = 12-hour time with AM/PM
55- if format. contains( "%I" ) || format. contains( "%l" ) || format. contains( "%r" ) {
56- return true ;
63+ // Helper function to check for 24-hour format indicators
64+ fn has_24_hour_indicators( format_str: & str ) -> bool {
65+ format_str. contains( "%H" )
66+ || format_str. contains( "%k" )
67+ || format_str. contains( "%R" )
68+ || format_str. contains( "%T" )
5769 }
5870
59- // If we find 24-hour indicators, it's definitely not 12-hour
60- // %H = hour (00-23), %k = hour (0-23) space-padded, %R = %H:%M, %T = %H:%M:%S
61- if format. contains( "%H" )
62- || format. contains( "%k" )
63- || format. contains( "%R" )
64- || format. contains( "%T" )
65- {
66- return false ;
71+ // Set locale from environment variables (empty string = use LC_TIME/LANG env vars)
72+ set_time_locale( ) ;
73+
74+ // Get locale format strings using safe wrappers
75+ let d_t_fmt = get_locale_info( nix:: libc:: D_T_FMT ) ;
76+ let t_fmt_opt = get_locale_info( nix:: libc:: T_FMT ) ;
77+ let t_fmt_ampm_opt = get_locale_info( nix:: libc:: T_FMT_AMPM ) ;
78+
79+ // Check D_T_FMT first
80+ if let Some ( ref format) = d_t_fmt {
81+ // Check for 12-hour indicators first (higher priority)
82+ if has_12_hour_indicators( format) {
83+ return true ;
84+ }
85+
86+ // If we find 24-hour indicators, it's definitely not 12-hour
87+ if has_24_hour_indicators( format) {
88+ return false ;
89+ }
6790 }
6891
6992 // Also check the time-only format as a fallback
70- let t_fmt_ptr = libc:: nl_langinfo( libc:: T_FMT ) ;
71- let mut time_fmt_opt = None ;
72- if !t_fmt_ptr. is_null( ) {
73- if let Ok ( time_format) = CStr :: from_ptr( t_fmt_ptr) . to_str( ) {
74- time_fmt_opt = Some ( time_format) ;
75- if time_format. contains( "%I" )
76- || time_format. contains( "%l" )
77- || time_format. contains( "%r" )
78- {
79- return true ;
80- }
93+ if let Some ( ref time_format) = t_fmt_opt {
94+ if has_12_hour_indicators( time_format) {
95+ return true ;
8196 }
8297 }
8398
8499 // Check if there's a specific 12-hour format defined
85- let t_fmt_ampm_ptr = libc:: nl_langinfo( libc:: T_FMT_AMPM ) ;
86- if !t_fmt_ampm_ptr. is_null( ) {
87- if let Ok ( ampm_format) = CStr :: from_ptr( t_fmt_ampm_ptr) . to_str( ) {
88- // If T_FMT_AMPM is non-empty and different from T_FMT, locale supports 12-hour
89- if !ampm_format. is_empty( ) {
90- if let Some ( time_format) = time_fmt_opt {
91- if ampm_format != time_format {
92- return true ;
93- }
94- } else {
100+ if let Some ( ref ampm_format) = t_fmt_ampm_opt {
101+ // If T_FMT_AMPM is non-empty and different from T_FMT, locale supports 12-hour
102+ if !ampm_format. is_empty( ) {
103+ if let Some ( ref time_format) = t_fmt_opt {
104+ if ampm_format != time_format {
95105 return true ;
96106 }
107+ } else {
108+ return true ;
97109 }
98110 }
99111 }
100- }
101112
102113 // Default to 24-hour format if we can't determine
103114 false
0 commit comments