@@ -53,19 +53,109 @@ cfg_langinfo! {
5353 } )
5454 }
5555
56- /// Retrieves the date/time format string from the system locale
57- fn get_locale_format_string( ) -> Option <String > {
56+ /// Replaces %c, %x, %X with their locale-specific format strings.
57+ ///
58+ /// If a flag like `^` is present (e.g., `%^c`), it is distributed to the
59+ /// sub-specifiers within the locale string.
60+ pub fn expand_locale_format( format: & str ) -> std:: borrow:: Cow <str > {
61+ let mut result = String :: with_capacity( format. len( ) ) ;
62+ let mut chars = format. chars( ) . peekable( ) ;
63+ let mut modified = false ;
64+
65+ while let Some ( c) = chars. next( ) {
66+ if c != '%' {
67+ result. push( c) ;
68+ continue ;
69+ }
70+
71+ // Capture flags
72+ let mut flags = Vec :: new( ) ;
73+ while let Some ( & peek) = chars. peek( ) {
74+ match peek {
75+ '_' | '-' | '0' | '^' | '#' => {
76+ flags. push( peek) ;
77+ chars. next( ) ;
78+ } ,
79+ _ => break ,
80+ }
81+ }
82+
83+ match chars. peek( ) {
84+ Some ( & spec @ ( 'c' | 'x' | 'X' ) ) => {
85+ chars. next( ) ;
86+
87+ let item = match spec {
88+ 'c' => libc:: D_T_FMT ,
89+ 'x' => libc:: D_FMT ,
90+ 'X' => libc:: T_FMT ,
91+ _ => unreachable!( ) ,
92+ } ;
93+
94+ if let Some ( s) = get_langinfo( item) {
95+ // If the user requested uppercase (%^c), distribute that flag
96+ // to the expanded specifiers.
97+ let replacement = if flags. contains( & '^' ) {
98+ distribute_flag( & s, '^' )
99+ } else {
100+ s
101+ } ;
102+ result. push_str( & replacement) ;
103+ modified = true ;
104+ } else {
105+ // Reconstruct original sequence if lookup fails
106+ result. push( '%' ) ;
107+ result. extend( flags) ;
108+ result. push( spec) ;
109+ }
110+ } ,
111+ Some ( _) | None => {
112+ // Not a locale specifier, or end of string.
113+ // Push captured flags and let loop handle the next char.
114+ result. push( '%' ) ;
115+ result. extend( flags) ;
116+ }
117+ }
118+ }
119+
120+ if modified {
121+ std:: borrow:: Cow :: Owned ( result)
122+ } else {
123+ std:: borrow:: Cow :: Borrowed ( format)
124+ }
125+ }
126+
127+ fn distribute_flag( fmt: & str , flag: char ) -> String {
128+ let mut res = String :: with_capacity( fmt. len( ) * 2 ) ;
129+ let mut chars = fmt. chars( ) . peekable( ) ;
130+ while let Some ( c) = chars. next( ) {
131+ res. push( c) ;
132+ if c == '%' {
133+ if let Some ( & n) = chars. peek( ) {
134+ if n == '%' {
135+ chars. next( ) ;
136+ res. push( '%' ) ;
137+ } else {
138+ res. push( flag) ;
139+ }
140+ }
141+ }
142+ }
143+ res
144+ }
145+
146+ /// Retrieves the date/time format string from the system locale (D_T_FMT, D_FMT, T_FMT)
147+ pub fn get_langinfo( item: libc:: nl_item) -> Option <String > {
58148 unsafe {
59149 // Set locale from environment variables
60150 libc:: setlocale( libc:: LC_TIME , c"" . as_ptr( ) ) ;
61151
62152 // Get the date/time format string
63- let d_t_fmt_ptr = libc:: nl_langinfo( libc :: D_T_FMT ) ;
64- if d_t_fmt_ptr . is_null( ) {
153+ let fmt_ptr = libc:: nl_langinfo( item ) ;
154+ if fmt_ptr . is_null( ) {
65155 return None ;
66156 }
67157
68- let format = CStr :: from_ptr( d_t_fmt_ptr ) . to_str( ) . ok( ) ?;
158+ let format = CStr :: from_ptr( fmt_ptr ) . to_str( ) . ok( ) ?;
69159 if format. is_empty( ) {
70160 return None ;
71161 }
@@ -74,6 +164,11 @@ cfg_langinfo! {
74164 }
75165 }
76166
167+ /// Retrieves the date/time format string from the system locale
168+ fn get_locale_format_string( ) -> Option <String > {
169+ get_langinfo( libc:: D_T_FMT )
170+ }
171+
77172 /// Ensures the format string includes timezone (%Z)
78173 fn ensure_timezone_in_format( format: & str ) -> String {
79174 if format. contains( "%Z" ) {
@@ -107,6 +202,18 @@ pub fn get_locale_default_format() -> &'static str {
107202 "%a %b %e %X %Z %Y"
108203}
109204
205+ #[ cfg( not( any(
206+ target_os = "linux" ,
207+ target_vendor = "apple" ,
208+ target_os = "freebsd" ,
209+ target_os = "netbsd" ,
210+ target_os = "openbsd" ,
211+ target_os = "dragonfly"
212+ ) ) ) ]
213+ pub fn expand_locale_format ( format : & str ) -> std:: borrow:: Cow < str > {
214+ std:: borrow:: Cow :: Borrowed ( format)
215+ }
216+
110217#[ cfg( test) ]
111218mod tests {
112219 cfg_langinfo ! {
0 commit comments