@@ -5,7 +5,15 @@ use std::{env, mem, ptr};
5
5
fn main ( ) {
6
6
test_clocks ( ) ;
7
7
test_posix_gettimeofday ( ) ;
8
- test_localtime_r ( ) ;
8
+ test_localtime_r_gmt ( ) ;
9
+ test_localtime_r_pst ( ) ;
10
+ test_localtime_r_epoch ( ) ;
11
+ test_localtime_r_multiple_calls_deduplication ( ) ;
12
+ // Architecture-specific tests.
13
+ #[ cfg( target_pointer_width = "32" ) ]
14
+ test_localtime_r_future_32b ( ) ;
15
+ #[ cfg( target_pointer_width = "64" ) ]
16
+ test_localtime_r_future_64b ( ) ;
9
17
}
10
18
11
19
/// Tests whether clock support exists at all
@@ -46,14 +54,9 @@ fn test_posix_gettimeofday() {
46
54
assert_eq ! ( is_error, -1 ) ;
47
55
}
48
56
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 {
57
+ // Helper function to create an empty tm struct.
58
+ fn create_empty_tm ( ) -> libc:: tm {
59
+ libc:: tm {
57
60
tm_sec : 0 ,
58
61
tm_min : 0 ,
59
62
tm_hour : 0 ,
@@ -77,7 +80,17 @@ fn test_localtime_r() {
77
80
target_os = "android"
78
81
) ) ]
79
82
tm_zone : std:: ptr:: null_mut :: < libc:: c_char > ( ) ,
80
- } ;
83
+ }
84
+ }
85
+
86
+ // Original GMT test
87
+ fn test_localtime_r_gmt ( ) {
88
+ // Set timezone to GMT.
89
+ let key = "TZ" ;
90
+ env:: set_var ( key, "GMT" ) ;
91
+ const TIME_SINCE_EPOCH : libc:: time_t = 1712475836 ; // 2024-04-07 07:43:56 GMT
92
+ let custom_time_ptr = & TIME_SINCE_EPOCH ;
93
+ let mut tm = create_empty_tm ( ) ;
81
94
let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
82
95
83
96
assert_eq ! ( tm. tm_sec, 56 ) ;
@@ -95,20 +108,221 @@ fn test_localtime_r() {
95
108
target_os = "freebsd" ,
96
109
target_os = "android"
97
110
) ) ]
98
- assert_eq ! ( tm. tm_gmtoff, 0 ) ;
111
+ {
112
+ assert_eq ! ( tm. tm_gmtoff, 0 ) ;
113
+ unsafe {
114
+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "+00" ) ;
115
+ }
116
+ }
117
+
118
+ // The returned value is the pointer passed in.
119
+ assert ! ( ptr:: eq( res, & mut tm) ) ;
120
+
121
+ // Remove timezone setting.
122
+ env:: remove_var ( key) ;
123
+ }
124
+
125
+ // PST timezone test (testing different timezone handling).
126
+ fn test_localtime_r_pst ( ) {
127
+ let key = "TZ" ;
128
+ env:: set_var ( key, "PST8PDT" ) ;
129
+ const TIME_SINCE_EPOCH : libc:: time_t = 1712475836 ; // 2024-04-07 07:43:56 GMT
130
+ let custom_time_ptr = & TIME_SINCE_EPOCH ;
131
+ let mut tm = create_empty_tm ( ) ;
132
+
133
+ let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
134
+
135
+ assert_eq ! ( tm. tm_sec, 56 ) ;
136
+ assert_eq ! ( tm. tm_min, 43 ) ;
137
+ assert_eq ! ( tm. tm_hour, 0 ) ; // 7 - 7 = 0 (PDT offset)
138
+ assert_eq ! ( tm. tm_mday, 7 ) ;
139
+ assert_eq ! ( tm. tm_mon, 3 ) ;
140
+ assert_eq ! ( tm. tm_year, 124 ) ;
141
+ assert_eq ! ( tm. tm_wday, 0 ) ;
142
+ assert_eq ! ( tm. tm_yday, 97 ) ;
143
+ assert_eq ! ( tm. tm_isdst, -1 ) ; // DST information unavailable
144
+
99
145
#[ cfg( any(
100
146
target_os = "linux" ,
101
147
target_os = "macos" ,
102
148
target_os = "freebsd" ,
103
149
target_os = "android"
104
150
) ) ]
105
- unsafe {
106
- assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "+00" )
107
- } ;
151
+ {
152
+ assert_eq ! ( tm. tm_gmtoff, -7 * 3600 ) ; // -7 hours in seconds
153
+ unsafe {
154
+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "-07" ) ;
155
+ }
156
+ }
108
157
109
- // The returned value is the pointer passed in.
110
158
assert ! ( ptr:: eq( res, & mut tm) ) ;
159
+ env:: remove_var ( key) ;
160
+ }
111
161
112
- // Remove timezone setting.
162
+ // Unix epoch test (edge case testing).
163
+ fn test_localtime_r_epoch ( ) {
164
+ let key = "TZ" ;
165
+ env:: set_var ( key, "GMT" ) ;
166
+ const TIME_SINCE_EPOCH : libc:: time_t = 0 ; // 1970-01-01 00:00:00
167
+ let custom_time_ptr = & TIME_SINCE_EPOCH ;
168
+ let mut tm = create_empty_tm ( ) ;
169
+
170
+ let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
171
+
172
+ assert_eq ! ( tm. tm_sec, 0 ) ;
173
+ assert_eq ! ( tm. tm_min, 0 ) ;
174
+ assert_eq ! ( tm. tm_hour, 0 ) ;
175
+ assert_eq ! ( tm. tm_mday, 1 ) ;
176
+ assert_eq ! ( tm. tm_mon, 0 ) ;
177
+ assert_eq ! ( tm. tm_year, 70 ) ;
178
+ assert_eq ! ( tm. tm_wday, 4 ) ; // Thursday
179
+ assert_eq ! ( tm. tm_yday, 0 ) ;
180
+ assert_eq ! ( tm. tm_isdst, -1 ) ;
181
+
182
+ #[ cfg( any(
183
+ target_os = "linux" ,
184
+ target_os = "macos" ,
185
+ target_os = "freebsd" ,
186
+ target_os = "android"
187
+ ) ) ]
188
+ {
189
+ assert_eq ! ( tm. tm_gmtoff, 0 ) ;
190
+ unsafe {
191
+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "+00" ) ;
192
+ }
193
+ }
194
+
195
+ assert ! ( ptr:: eq( res, & mut tm) ) ;
196
+ env:: remove_var ( key) ;
197
+ }
198
+
199
+ // Future date test (testing large values).
200
+ #[ cfg( target_pointer_width = "64" ) ]
201
+ fn test_localtime_r_future_64b ( ) {
202
+ let key = "TZ" ;
203
+ env:: set_var ( key, "GMT" ) ;
204
+
205
+ // Using 2050-01-01 00:00:00 for 64-bit systems
206
+ // value that's safe for 64-bit time_t
207
+ const TIME_SINCE_EPOCH : libc:: time_t = 2524608000 ;
208
+ let custom_time_ptr = & TIME_SINCE_EPOCH ;
209
+ let mut tm = create_empty_tm ( ) ;
210
+
211
+ let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
212
+
213
+ assert_eq ! ( tm. tm_sec, 0 ) ;
214
+ assert_eq ! ( tm. tm_min, 0 ) ;
215
+ assert_eq ! ( tm. tm_hour, 0 ) ;
216
+ assert_eq ! ( tm. tm_mday, 1 ) ;
217
+ assert_eq ! ( tm. tm_mon, 0 ) ;
218
+ assert_eq ! ( tm. tm_year, 150 ) ; // 2050 - 1900
219
+ assert_eq ! ( tm. tm_wday, 6 ) ; // Saturday
220
+ assert_eq ! ( tm. tm_yday, 0 ) ;
221
+ assert_eq ! ( tm. tm_isdst, -1 ) ;
222
+
223
+ #[ cfg( any(
224
+ target_os = "linux" ,
225
+ target_os = "macos" ,
226
+ target_os = "freebsd" ,
227
+ target_os = "android"
228
+ ) ) ]
229
+ {
230
+ assert_eq ! ( tm. tm_gmtoff, 0 ) ;
231
+ unsafe {
232
+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "+00" ) ;
233
+ }
234
+ }
235
+
236
+ assert ! ( ptr:: eq( res, & mut tm) ) ;
113
237
env:: remove_var ( key) ;
114
238
}
239
+
240
+ #[ cfg( target_pointer_width = "32" ) ]
241
+ fn test_localtime_r_future_32b ( ) {
242
+ let key = "TZ" ;
243
+ env:: set_var ( key, "GMT" ) ;
244
+
245
+ // Using 2030-01-01 00:00:00 for 32-bit systems
246
+ // Safe value within i32 range
247
+ const TIME_SINCE_EPOCH : libc:: time_t = 1893456000 ;
248
+ let custom_time_ptr = & TIME_SINCE_EPOCH ;
249
+ let mut tm = create_empty_tm ( ) ;
250
+
251
+ let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
252
+
253
+ // Verify 2030-01-01 00:00:00
254
+ assert_eq ! ( tm. tm_sec, 0 ) ;
255
+ assert_eq ! ( tm. tm_min, 0 ) ;
256
+ assert_eq ! ( tm. tm_hour, 0 ) ;
257
+ assert_eq ! ( tm. tm_mday, 1 ) ;
258
+ assert_eq ! ( tm. tm_mon, 0 ) ;
259
+ assert_eq ! ( tm. tm_year, 130 ) ; // 2030 - 1900
260
+ assert_eq ! ( tm. tm_wday, 2 ) ; // Tuesday
261
+ assert_eq ! ( tm. tm_yday, 0 ) ;
262
+ assert_eq ! ( tm. tm_isdst, -1 ) ;
263
+
264
+ #[ cfg( any(
265
+ target_os = "linux" ,
266
+ target_os = "macos" ,
267
+ target_os = "freebsd" ,
268
+ target_os = "android"
269
+ ) ) ]
270
+ {
271
+ assert_eq ! ( tm. tm_gmtoff, 0 ) ;
272
+ unsafe {
273
+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "+00" ) ;
274
+ }
275
+ }
276
+
277
+ assert ! ( ptr:: eq( res, & mut tm) ) ;
278
+ env:: remove_var ( key) ;
279
+ }
280
+
281
+ fn test_localtime_r_multiple_calls_deduplication ( ) {
282
+ let key = "TZ" ;
283
+ env:: set_var ( key, "PST8PDT" ) ;
284
+
285
+ const TIME_SINCE_EPOCH_BASE : libc:: time_t = 1712475836 ; // Base timestamp: 2024-04-07 07:43:56 GMT
286
+ const NUM_CALLS : usize = 50 ;
287
+
288
+ let mut unique_pointers = std:: collections:: HashSet :: new ( ) ;
289
+
290
+ unsafe {
291
+ for i in 0 ..NUM_CALLS {
292
+ let timestamp = TIME_SINCE_EPOCH_BASE + ( i as libc:: time_t * 3600 ) ; // Increment by 1 hour for each call
293
+ let mut tm: libc:: tm = create_empty_tm ( ) ;
294
+ let tm_ptr = libc:: localtime_r ( & timestamp, & mut tm) ;
295
+
296
+ assert ! ( !tm_ptr. is_null( ) , "localtime_r failed for timestamp {timestamp}" ) ;
297
+
298
+ #[ cfg( any(
299
+ target_os = "linux" ,
300
+ target_os = "macos" ,
301
+ target_os = "freebsd" ,
302
+ target_os = "android"
303
+ ) ) ]
304
+ {
305
+ let tm_zone_ptr = tm. tm_zone ;
306
+ unique_pointers. insert ( tm_zone_ptr) ;
307
+ }
308
+ }
309
+
310
+ #[ cfg( any(
311
+ target_os = "linux" ,
312
+ target_os = "macos" ,
313
+ target_os = "freebsd" ,
314
+ target_os = "android"
315
+ ) ) ]
316
+ {
317
+ let unique_count = unique_pointers. len ( ) ;
318
+ println ! ( "Number of unique tm_zone pointers: {}" , unique_count) ;
319
+
320
+ assert ! (
321
+ unique_count >= 2 && unique_count <= ( NUM_CALLS - 1 ) ,
322
+ "Unexpected number of unique tm_zone pointers: {} (expected between 2 and {})" ,
323
+ unique_count,
324
+ NUM_CALLS - 1
325
+ ) ;
326
+ }
327
+ }
328
+ }
0 commit comments