1use spinoso_time::strftime::{
4 ASCTIME_FORMAT_STRING,
5 Error::{FormattedStringTooLarge, InvalidFormatString, WriteZero},
6};
7
8use crate::convert::implicitly_convert_to_int;
9use crate::convert::to_str;
10use crate::extn::core::string::{Encoding, String};
11use crate::extn::core::time::{Offset, Time, args::Args, subsec::Subsec};
12use crate::extn::prelude::*;
13
14pub fn now(interp: &mut Artichoke) -> Result<Value, Error> {
16 let now = Time::now()?;
17 let result = Time::alloc_value(now, interp)?;
18 Ok(result)
19}
20
21pub fn at(
22 interp: &mut Artichoke,
23 seconds: Value,
24 first: Option<Value>,
25 second: Option<Value>,
26 third: Option<Value>,
27) -> Result<Value, Error> {
28 let mut subsec = first;
50 let mut subsec_unit = second;
51 let mut options = third;
52
53 if let Some(third_param) = third {
75 if third_param.ruby_type() != Ruby::Hash {
76 return Err(ArgumentError::with_message("invalid offset options").into());
77 }
78 } else {
79 options = if let Some(second_param) = second {
80 if second_param.ruby_type() == Ruby::Hash {
81 subsec_unit = None;
82 Some(second_param)
83 } else if let Some(first_param) = first {
84 if first_param.ruby_type() == Ruby::Hash {
85 subsec = None;
86 Some(first_param)
87 } else {
88 None
89 }
90 } else {
91 None
92 }
93 } else {
94 None
95 }
96 }
97
98 let subsec: Subsec = interp.try_convert_mut((subsec, subsec_unit))?;
99 let (subsec_secs, subsec_nanos) = subsec.to_tuple();
100
101 let seconds = implicitly_convert_to_int(interp, seconds)?
102 .checked_add(subsec_secs)
103 .ok_or(ArgumentError::with_message("Time too large"))?;
104
105 let offset: Offset = if let Some(options) = options {
106 let offset: Option<Offset> = interp.try_convert_mut(options)?;
107 offset.unwrap_or_else(Offset::local)
108 } else {
109 Offset::local()
110 };
111
112 let time = Time::with_timespec_and_offset(seconds, subsec_nanos, offset)?;
113
114 Time::alloc_value(time, interp)
115}
116
117pub fn mkutc(interp: &mut Artichoke, args: &mut [Value]) -> Result<Value, Error> {
118 let args: Args = interp.try_convert_mut(args)?;
119
120 let time = Time::utc(
121 args.year,
122 args.month,
123 args.day,
124 args.hour,
125 args.minute,
126 args.second,
127 args.nanoseconds,
128 )?;
129
130 Time::alloc_value(time, interp)
131}
132
133pub fn mktime(interp: &mut Artichoke, args: &mut [Value]) -> Result<Value, Error> {
134 let args: Args = interp.try_convert_mut(args)?;
135
136 let time = Time::local(
137 args.year,
138 args.month,
139 args.day,
140 args.hour,
141 args.minute,
142 args.second,
143 args.nanoseconds,
144 )?;
145
146 Time::alloc_value(time, interp)
147}
148
149pub fn to_int(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
152 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
153 let timestamp = time.to_int();
154 Ok(interp.convert(timestamp))
155}
156
157pub fn to_float(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
158 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
159 let duration = time.to_float();
160 Ok(interp.convert_mut(duration))
161}
162
163pub fn to_rational(interp: &mut Artichoke, time: Value) -> Result<Value, Error> {
164 let _ = interp;
165 let _ = time;
166 Err(NotImplementedError::new().into())
168}
169
170pub fn cmp(interp: &mut Artichoke, mut time: Value, mut other: Value) -> Result<Value, Error> {
171 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
172 if let Ok(other) = unsafe { Time::unbox_from_value(&mut other, interp) } {
173 let cmp = time.cmp(&other);
174 Ok(interp.convert(cmp as i32))
175 } else {
176 let mut message = b"comparison of Time with ".to_vec();
177 message.extend_from_slice(interp.inspect_type_name_for_value(other).as_bytes());
178 message.extend_from_slice(b" failed");
179 Err(ArgumentError::from(message).into())
180 }
181}
182
183pub fn eql(interp: &mut Artichoke, mut time: Value, mut other: Value) -> Result<Value, Error> {
184 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
185 if let Ok(other) = unsafe { Time::unbox_from_value(&mut other, interp) } {
186 let cmp = time.eq(&other);
187 Ok(interp.convert(cmp))
188 } else {
189 Ok(interp.convert(false))
190 }
191}
192
193pub fn hash(interp: &mut Artichoke, time: Value) -> Result<Value, Error> {
194 let _ = interp;
195 let _ = time;
196 Err(NotImplementedError::new().into())
197}
198
199pub fn initialize<T>(interp: &mut Artichoke, time: Value, args: T) -> Result<Value, Error>
200where
201 T: IntoIterator<Item = Value>,
202{
203 let _ = interp;
204 let _ = time;
205 let _ignored_while_unimplemented = args.into_iter();
206 Err(NotImplementedError::new().into())
207}
208
209pub fn initialize_copy(interp: &mut Artichoke, time: Value, mut from: Value) -> Result<Value, Error> {
210 let from = unsafe { Time::unbox_from_value(&mut from, interp)? };
211 let result = *from;
212 Time::box_into_value(result, time, interp)
213}
214
215pub fn mutate_to_local(interp: &mut Artichoke, time: Value, offset: Option<Value>) -> Result<Value, Error> {
218 let _ = interp;
219 let _ = time;
220 let _ = offset;
221 Err(NotImplementedError::new().into())
222}
223
224pub fn mutate_to_utc(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
225 let mut obj = unsafe { Time::unbox_from_value(&mut time, interp)? };
226 obj.set_utc()?;
227 Ok(time)
228}
229
230pub fn as_local(interp: &mut Artichoke, time: Value, offset: Option<Value>) -> Result<Value, Error> {
231 let _ = interp;
232 let _ = time;
233 let _ = offset;
234 Err(NotImplementedError::new().into())
235}
236
237pub fn as_utc(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
238 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
239 let utc = time.to_utc()?;
240 Time::alloc_value(utc, interp)
241}
242
243pub fn asctime(interp: &mut Artichoke, time: Value) -> Result<Value, Error> {
246 strftime_with_encoding(interp, time, ASCTIME_FORMAT_STRING.as_bytes(), Encoding::Utf8)
247}
248
249pub fn to_string(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
250 let format = if unsafe { Time::unbox_from_value(&mut time, interp)? }.is_utc() {
253 "%Y-%m-%d %H:%M:%S UTC"
254 } else {
255 "%Y-%m-%d %H:%M:%S %z"
256 };
257
258 strftime_with_encoding(interp, time, format.as_bytes(), Encoding::Utf8)
259}
260
261pub fn to_array(interp: &mut Artichoke, time: Value) -> Result<Value, Error> {
262 let _ = interp;
264 let _ = time;
265 Err(NotImplementedError::new().into())
266}
267
268pub fn plus(interp: &mut Artichoke, mut time: Value, mut other: Value) -> Result<Value, Error> {
271 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
272 if unsafe { Time::unbox_from_value(&mut other, interp) }.is_ok() {
273 Err(TypeError::with_message("time + time?").into())
282 } else if let Ok(other) = other.try_convert_into::<f64>(interp) {
283 let result = time.checked_add_f64(other)?;
284
285 Time::alloc_value(result, interp)
286 } else if let Ok(other) = implicitly_convert_to_int(interp, other) {
287 let result = time.checked_add_i64(other)?;
288
289 Time::alloc_value(result, interp)
290 } else {
291 Err(TypeError::with_message("can't convert into an exact number").into())
292 }
293}
294
295pub fn minus(interp: &mut Artichoke, mut time: Value, mut other: Value) -> Result<Value, Error> {
296 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
297 if let Ok(other) = unsafe { Time::unbox_from_value(&mut other, interp) } {
298 let result: Value = interp.convert_mut(time.to_float() - other.to_float());
299 Ok(result)
300 } else if let Ok(other) = implicitly_convert_to_int(interp, other) {
301 let result = time.checked_sub_i64(other)?;
302
303 Time::alloc_value(result, interp)
304 } else if let Ok(other) = other.try_convert_into::<f64>(interp) {
305 let result = time.checked_sub_f64(other)?;
306
307 Time::alloc_value(result, interp)
308 } else {
309 Err(TypeError::with_message("can't convert into an exact number").into())
310 }
311}
312
313pub fn succ(interp: &mut Artichoke, time: Value) -> Result<Value, Error> {
316 interp.warn(b"warning: Time#succ is obsolete; use time + 1")?;
317 plus(interp, time, interp.convert(1))
318}
319
320pub fn round(interp: &mut Artichoke, time: Value, num_digits: Option<Value>) -> Result<Value, Error> {
321 let _ = interp;
322 let _ = time;
323 let _ = num_digits;
324 Err(NotImplementedError::new().into())
325}
326
327pub fn second(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
330 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
331 let second = time.second();
332 let result = interp.convert(second);
333 Ok(result)
334}
335
336pub fn minute(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
337 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
338 let minute = time.minute();
339 let result = interp.convert(minute);
340 Ok(result)
341}
342
343pub fn hour(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
344 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
345 let hour = time.hour();
346 let result = interp.convert(hour);
347 Ok(result)
348}
349
350pub fn day(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
351 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
352 let day = time.day();
353 let result = interp.convert(day);
354 Ok(result)
355}
356
357pub fn month(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
358 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
359 let month = time.month();
360 let result = interp.convert(month);
361 Ok(result)
362}
363
364pub fn year(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
365 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
366 let year = time.year();
367 let result = interp.convert(year);
368 Ok(result)
369}
370
371pub fn weekday(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
372 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
373 let weekday = time.day_of_week();
374 let result = interp.convert(weekday);
375 Ok(result)
376}
377
378pub fn year_day(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
379 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
380 let year_day = time.day_of_year();
381 let result = interp.convert(year_day);
382 Ok(result)
383}
384
385pub fn is_dst(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
386 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
387 let is_dst = time.is_dst();
388 Ok(interp.convert(is_dst))
389}
390
391pub fn timezone(interp: &mut Artichoke, time: Value) -> Result<Value, Error> {
392 let _ = interp;
393 let _ = time;
394 Err(NotImplementedError::new().into())
395}
396
397pub fn utc_offset(interp: &mut Artichoke, time: Value) -> Result<Value, Error> {
398 let _ = interp;
399 let _ = time;
400 Err(NotImplementedError::new().into())
401}
402
403pub fn is_utc(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
406 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
407 let is_utc = time.is_utc();
408 Ok(interp.convert(is_utc))
409}
410
411pub fn is_sunday(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
414 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
415 let is_sunday = time.is_sunday();
416 let result = interp.convert(is_sunday);
417 Ok(result)
418}
419
420pub fn is_monday(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
421 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
422 let is_monday = time.is_monday();
423 let result = interp.convert(is_monday);
424 Ok(result)
425}
426
427pub fn is_tuesday(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
428 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
429 let is_tuesday = time.is_tuesday();
430 let result = interp.convert(is_tuesday);
431 Ok(result)
432}
433
434pub fn is_wednesday(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
435 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
436 let is_wednesday = time.is_wednesday();
437 let result = interp.convert(is_wednesday);
438 Ok(result)
439}
440
441pub fn is_thursday(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
442 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
443 let is_thursday = time.is_thursday();
444 let result = interp.convert(is_thursday);
445 Ok(result)
446}
447
448pub fn is_friday(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
449 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
450 let is_friday = time.is_friday();
451 let result = interp.convert(is_friday);
452 Ok(result)
453}
454
455pub fn is_saturday(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
456 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
457 let is_saturday = time.is_saturday();
458 let result = interp.convert(is_saturday);
459 Ok(result)
460}
461
462pub fn microsecond(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
465 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
466 let microsecond = time.microseconds();
467 let result = interp.convert(microsecond);
468 Ok(result)
469}
470
471pub fn nanosecond(interp: &mut Artichoke, mut time: Value) -> Result<Value, Error> {
472 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
473 let nanosecond = time.nanoseconds();
474 let result = interp.convert(nanosecond);
475 Ok(result)
476}
477
478pub fn subsec(interp: &mut Artichoke, time: Value) -> Result<Value, Error> {
479 let _ = interp;
480 let _ = time;
481 Err(NotImplementedError::new().into())
483}
484
485fn strftime_with_encoding(
487 interp: &mut Artichoke,
488 mut time: Value,
489 format: &[u8],
490 encoding: Encoding,
491) -> Result<Value, Error> {
492 let time = unsafe { Time::unbox_from_value(&mut time, interp)? };
493
494 if format.is_empty() {
509 interp.warn(b"strftime called with empty format string")?;
519 }
520
521 let bytes: Vec<u8> = time.strftime(format).map_err(|e| {
522 match e {
538 InvalidFormatString => {
539 let mut message = b"invalid format: ".to_vec();
540 message.extend_from_slice(format);
541 Error::from(ArgumentError::from(message))
542 }
543 FormattedStringTooLarge => {
544 let mut message = b"Result too large - ".to_vec();
555 message.extend_from_slice(format);
556 Error::from(ArgumentError::from(message))
557 }
558 WriteZero => {
559 let mut message = b"Result too large - ".to_vec();
570 message.extend_from_slice(format);
571 Error::from(ArgumentError::from(message))
572 }
573 _ => Error::from(RuntimeError::with_message("Unexpected failure")),
574 }
575 })?;
576
577 let result = String::with_bytes_and_encoding(bytes, encoding);
578
579 String::alloc_value(result, interp)
580}
581
582pub fn strftime(interp: &mut Artichoke, time: Value, format: Value) -> Result<Value, Error> {
583 let mut format = to_str(interp, format)?;
584
585 let format = unsafe { String::unbox_from_value(&mut format, interp)? };
586
587 strftime_with_encoding(interp, time, &format, format.encoding())
588}