@@ -34,70 +34,80 @@ 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+ const INDICATORS : & [ & str ] = & [ "%I" , "%l" , "%r" ] ;
61+ INDICATORS . iter( ) . any( |& indicator| format_str. contains( indicator) )
62+ }
5263
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 ;
64+ // Helper function to check for 24 -hour format indicators
65+ fn has_24_hour_indicators ( format_str : & str ) -> bool {
66+ const INDICATORS : & [ & str ] = & [ "%H" , "%k" , "%R" , "%T" ] ;
67+ INDICATORS . iter ( ) . any ( | & indicator| format_str . contains ( indicator ) )
5768 }
5869
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 ;
70+ // Set locale from environment variables (empty string = use LC_TIME/LANG env vars)
71+ set_time_locale( ) ;
72+
73+ // Get locale format strings using safe wrappers
74+ let d_t_fmt = get_locale_info( nix:: libc:: D_T_FMT ) ;
75+ let t_fmt_opt = get_locale_info( nix:: libc:: T_FMT ) ;
76+ let t_fmt_ampm_opt = get_locale_info( nix:: libc:: T_FMT_AMPM ) ;
77+
78+ // Check D_T_FMT first
79+ if let Some ( ref format) = d_t_fmt {
80+ // Check for 12-hour indicators first (higher priority)
81+ if has_12_hour_indicators( format) {
82+ return true ;
83+ }
84+
85+ // If we find 24-hour indicators, it's definitely not 12-hour
86+ if has_24_hour_indicators( format) {
87+ return false ;
88+ }
6789 }
6890
6991 // 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- }
92+ if let Some ( ref time_format) = t_fmt_opt {
93+ if has_12_hour_indicators( time_format) {
94+ return true ;
8195 }
8296 }
8397
8498 // 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 {
99+ if let Some ( ref ampm_format) = t_fmt_ampm_opt {
100+ // If T_FMT_AMPM is non-empty and different from T_FMT, locale supports 12-hour
101+ if !ampm_format. is_empty( ) {
102+ if let Some ( ref time_format) = t_fmt_opt {
103+ if ampm_format != time_format {
95104 return true ;
96105 }
106+ } else {
107+ return true ;
97108 }
98109 }
99110 }
100- }
101111
102112 // Default to 24-hour format if we can't determine
103113 false
0 commit comments