@@ -5,7 +5,14 @@ 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
+ // 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 ( ) ;
9
16
}
10
17
11
18
/// Tests whether clock support exists at all
@@ -46,14 +53,9 @@ fn test_posix_gettimeofday() {
46
53
assert_eq ! ( is_error, -1 ) ;
47
54
}
48
55
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 {
57
59
tm_sec : 0 ,
58
60
tm_min : 0 ,
59
61
tm_hour : 0 ,
@@ -77,7 +79,17 @@ fn test_localtime_r() {
77
79
target_os = "android"
78
80
) ) ]
79
81
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 ( ) ;
81
93
let res = unsafe { libc:: localtime_r ( custom_time_ptr, & mut tm) } ;
82
94
83
95
assert_eq ! ( tm. tm_sec, 56 ) ;
@@ -95,20 +107,172 @@ fn test_localtime_r() {
95
107
target_os = "freebsd" ,
96
108
target_os = "android"
97
109
) ) ]
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 ; // 2024-04-07 07:43:56 GMT
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
+
99
144
#[ cfg( any(
100
145
target_os = "linux" ,
101
146
target_os = "macos" ,
102
147
target_os = "freebsd" ,
103
148
target_os = "android"
104
149
) ) ]
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, -7 * 3600 ) ; // -7 hours in seconds
152
+ unsafe {
153
+ assert_eq ! ( std:: ffi:: CStr :: from_ptr( tm. tm_zone) . to_str( ) . unwrap( ) , "-07" ) ;
154
+ }
155
+ }
108
156
109
- // The returned value is the pointer passed in.
110
157
assert ! ( ptr:: eq( res, & mut tm) ) ;
158
+ env:: remove_var ( key) ;
159
+ }
111
160
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) ) ;
113
277
env:: remove_var ( key) ;
114
278
}
0 commit comments