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