@@ -5,7 +5,14 @@ use std::{env, mem, ptr};
55fn main ( ) {
66 test_clocks ( ) ;
77 test_posix_gettimeofday ( ) ;
8- test_localtime_r ( ) ;
8+ test_localtime_r_gmt ( ) ;
9+ test_localtime_r_pst ( ) ;
10+ test_localtime_r_epoch ( ) ;
11+ // Architecture-specific tests.
12+ #[ cfg( target_pointer_width = "32" ) ]
13+ test_localtime_r_future_32b ( ) ;
14+ #[ cfg( target_pointer_width = "64" ) ]
15+ test_localtime_r_future_64b ( ) ;
916}
1017
1118/// Tests whether clock support exists at all
@@ -46,14 +53,9 @@ fn test_posix_gettimeofday() {
4653 assert_eq ! ( is_error, -1 ) ;
4754}
4855
49- fn test_localtime_r ( ) {
50- // Set timezone to GMT.
51- let key = "TZ" ;
52- env:: set_var ( key, "GMT" ) ;
53-
54- const TIME_SINCE_EPOCH : libc:: time_t = 1712475836 ;
55- let custom_time_ptr = & TIME_SINCE_EPOCH ;
56- let mut tm = libc:: tm {
56+ // Helper function to create an empty tm struct.
57+ fn create_empty_tm ( ) -> libc:: tm {
58+ libc:: tm {
5759 tm_sec : 0 ,
5860 tm_min : 0 ,
5961 tm_hour : 0 ,
@@ -77,7 +79,17 @@ fn test_localtime_r() {
7779 target_os = "android"
7880 ) ) ]
7981 tm_zone : std:: ptr:: null_mut :: < libc:: c_char > ( ) ,
80- } ;
82+ }
83+ }
84+
85+ // Original GMT test
86+ fn test_localtime_r_gmt ( ) {
87+ // Set timezone to GMT.
88+ let key = "TZ" ;
89+ env:: set_var ( key, "GMT" ) ;
90+ const TIME_SINCE_EPOCH : libc:: time_t = 1712475836 ; // 2024-04-07 07:43:56 GMT
91+ let custom_time_ptr = & TIME_SINCE_EPOCH ;
92+ let mut tm = create_empty_tm ( ) ;
8193 let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
8294
8395 assert_eq ! ( tm. tm_sec, 56 ) ;
@@ -95,20 +107,172 @@ fn test_localtime_r() {
95107 target_os = "freebsd" ,
96108 target_os = "android"
97109 ) ) ]
98- assert_eq ! ( tm. tm_gmtoff, 0 ) ;
110+ {
111+ assert_eq ! ( tm. tm_gmtoff, 0 ) ;
112+ unsafe {
113+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "+00" ) ;
114+ }
115+ }
116+
117+ // The returned value is the pointer passed in.
118+ assert ! ( ptr:: eq( res, & mut tm) ) ;
119+
120+ // Remove timezone setting.
121+ env:: remove_var ( key) ;
122+ }
123+
124+ // PST timezone test (testing different timezone handling).
125+ fn test_localtime_r_pst ( ) {
126+ let key = "TZ" ;
127+ env:: set_var ( key, "PST8PDT" ) ;
128+ const TIME_SINCE_EPOCH : libc:: time_t = 1712475836 ;
129+ let custom_time_ptr = & TIME_SINCE_EPOCH ;
130+ let mut tm = create_empty_tm ( ) ;
131+
132+ let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
133+
134+ assert_eq ! ( tm. tm_sec, 56 ) ;
135+ assert_eq ! ( tm. tm_min, 43 ) ;
136+ assert_eq ! ( tm. tm_hour, 0 ) ; // 7 - 7 = 0 (PDT offset)
137+ assert_eq ! ( tm. tm_mday, 7 ) ;
138+ assert_eq ! ( tm. tm_mon, 3 ) ;
139+ assert_eq ! ( tm. tm_year, 124 ) ;
140+ assert_eq ! ( tm. tm_wday, 0 ) ;
141+ assert_eq ! ( tm. tm_yday, 97 ) ;
142+ assert_eq ! ( tm. tm_isdst, -1 ) ; // DST information unavailable
143+
99144 #[ cfg( any(
100145 target_os = "linux" ,
101146 target_os = "macos" ,
102147 target_os = "freebsd" ,
103148 target_os = "android"
104149 ) ) ]
105- unsafe {
106- assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "+00" )
107- } ;
150+ {
151+ assert_eq ! ( tm. tm_gmtoff, -25200 ) ; // -7 hours in seconds
152+ unsafe {
153+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "-07" ) ;
154+ }
155+ }
108156
109- // The returned value is the pointer passed in.
110157 assert ! ( ptr:: eq( res, & mut tm) ) ;
158+ env:: remove_var ( key) ;
159+ }
111160
112- // Remove timezone setting.
161+ // Unix epoch test (edge case testing).
162+ fn test_localtime_r_epoch ( ) {
163+ let key = "TZ" ;
164+ env:: set_var ( key, "GMT" ) ;
165+ const TIME_SINCE_EPOCH : libc:: time_t = 0 ; // 1970-01-01 00:00:00
166+ let custom_time_ptr = & TIME_SINCE_EPOCH ;
167+ let mut tm = create_empty_tm ( ) ;
168+
169+ let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
170+
171+ assert_eq ! ( tm. tm_sec, 0 ) ;
172+ assert_eq ! ( tm. tm_min, 0 ) ;
173+ assert_eq ! ( tm. tm_hour, 0 ) ;
174+ assert_eq ! ( tm. tm_mday, 1 ) ;
175+ assert_eq ! ( tm. tm_mon, 0 ) ;
176+ assert_eq ! ( tm. tm_year, 70 ) ;
177+ assert_eq ! ( tm. tm_wday, 4 ) ; // Thursday
178+ assert_eq ! ( tm. tm_yday, 0 ) ;
179+ assert_eq ! ( tm. tm_isdst, -1 ) ;
180+
181+ #[ cfg( any(
182+ target_os = "linux" ,
183+ target_os = "macos" ,
184+ target_os = "freebsd" ,
185+ target_os = "android"
186+ ) ) ]
187+ {
188+ assert_eq ! ( tm. tm_gmtoff, 0 ) ;
189+ unsafe {
190+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "+00" ) ;
191+ }
192+ }
193+
194+ assert ! ( ptr:: eq( res, & mut tm) ) ;
195+ env:: remove_var ( key) ;
196+ }
197+
198+ // Future date test (testing large values).
199+ #[ cfg( target_pointer_width = "64" ) ]
200+ fn test_localtime_r_future_64b ( ) {
201+ let key = "TZ" ;
202+ env:: set_var ( key, "GMT" ) ;
203+
204+ // Using 2050-01-01 00:00:00 for 64-bit systems
205+ // value that's safe for 64-bit time_t
206+ const TIME_SINCE_EPOCH : libc:: time_t = 2524608000 ;
207+ let custom_time_ptr = & TIME_SINCE_EPOCH ;
208+ let mut tm = create_empty_tm ( ) ;
209+
210+ let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
211+
212+ assert_eq ! ( tm. tm_sec, 0 ) ;
213+ assert_eq ! ( tm. tm_min, 0 ) ;
214+ assert_eq ! ( tm. tm_hour, 0 ) ;
215+ assert_eq ! ( tm. tm_mday, 1 ) ;
216+ assert_eq ! ( tm. tm_mon, 0 ) ;
217+ assert_eq ! ( tm. tm_year, 150 ) ; // 2050 - 1900
218+ assert_eq ! ( tm. tm_wday, 6 ) ; // Saturday
219+ assert_eq ! ( tm. tm_yday, 0 ) ;
220+ assert_eq ! ( tm. tm_isdst, -1 ) ;
221+
222+ #[ cfg( any(
223+ target_os = "linux" ,
224+ target_os = "macos" ,
225+ target_os = "freebsd" ,
226+ target_os = "android"
227+ ) ) ]
228+ {
229+ assert_eq ! ( tm. tm_gmtoff, 0 ) ;
230+ unsafe {
231+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "+00" ) ;
232+ }
233+ }
234+
235+ assert ! ( ptr:: eq( res, & mut tm) ) ;
236+ env:: remove_var ( key) ;
237+ }
238+
239+ #[ cfg( target_pointer_width = "32" ) ]
240+ fn test_localtime_r_future_32b ( ) {
241+ let key = "TZ" ;
242+ env:: set_var ( key, "GMT" ) ;
243+
244+ // Using 2030-01-01 00:00:00 for 32-bit systems
245+ // Safe value within i32 range
246+ const TIME_SINCE_EPOCH : libc:: time_t = 1893456000 ;
247+ let custom_time_ptr = & TIME_SINCE_EPOCH ;
248+ let mut tm = create_empty_tm ( ) ;
249+
250+ let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
251+
252+ // Verify 2030-01-01 00:00:00
253+ assert_eq ! ( tm. tm_sec, 0 ) ;
254+ assert_eq ! ( tm. tm_min, 0 ) ;
255+ assert_eq ! ( tm. tm_hour, 0 ) ;
256+ assert_eq ! ( tm. tm_mday, 1 ) ;
257+ assert_eq ! ( tm. tm_mon, 0 ) ;
258+ assert_eq ! ( tm. tm_year, 130 ) ; // 2030 - 1900
259+ assert_eq ! ( tm. tm_wday, 2 ) ; // Tuesday
260+ assert_eq ! ( tm. tm_yday, 0 ) ;
261+ assert_eq ! ( tm. tm_isdst, -1 ) ;
262+
263+ #[ cfg( any(
264+ target_os = "linux" ,
265+ target_os = "macos" ,
266+ target_os = "freebsd" ,
267+ target_os = "android"
268+ ) ) ]
269+ {
270+ assert_eq ! ( tm. tm_gmtoff, 0 ) ;
271+ unsafe {
272+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "+00" ) ;
273+ }
274+ }
275+
276+ assert ! ( ptr:: eq( res, & mut tm) ) ;
113277 env:: remove_var ( key) ;
114278}
0 commit comments