1use crate::{AsciiStr, Dt, DtErr, DtErrKind, GregorianTime, STRFTIME_SIZE, an_err};
2
3pub(crate) const WEEKDAYS_FULL: [&[u8]; 7] = [
4 b"Sunday",
5 b"Monday",
6 b"Tuesday",
7 b"Wednesday",
8 b"Thursday",
9 b"Friday",
10 b"Saturday",
11];
12pub(crate) const WEEKDAYS_ABBR: [&[u8]; 7] =
13 [b"Sun", b"Mon", b"Tue", b"Wed", b"Thu", b"Fri", b"Sat"];
14pub(crate) const MONTHS_FULL: [&[u8]; 12] = [
15 b"January",
16 b"February",
17 b"March",
18 b"April",
19 b"May",
20 b"June",
21 b"July",
22 b"August",
23 b"September",
24 b"October",
25 b"November",
26 b"December",
27];
28pub(crate) const MONTHS_ABBR: [&[u8]; 12] = [
29 b"Jan", b"Feb", b"Mar", b"Apr", b"May", b"Jun", b"Jul", b"Aug", b"Sep", b"Oct", b"Nov", b"Dec",
30];
31
32impl GregorianTime {
33 #[cfg(feature = "alloc")]
34 pub fn to_str(&self, fmt: &str) -> Result<alloc::string::String, DtErr> {
35 let mut buf = [0u8; STRFTIME_SIZE];
36 let mut pos = 0usize;
37 self.format_to_buffer(fmt.as_bytes(), &mut buf, &mut pos)?;
38 Ok(alloc::string::String::from_utf8_lossy(&buf[0..pos]).into_owned())
39 }
40
41 pub fn to_ascii_str(&self, fmt: &str) -> Result<AsciiStr<STRFTIME_SIZE>, DtErr> {
43 let mut buf = [0u8; STRFTIME_SIZE];
44 let mut pos = 0usize;
45 self.format_to_buffer(fmt.as_bytes(), &mut buf, &mut pos)?;
46 Ok(AsciiStr::from_filled_buffer(buf))
47 }
48
49 pub(crate) fn format_to_buffer(
50 &self,
51 fmt: &[u8],
52 buf: &mut [u8; STRFTIME_SIZE],
53 pos: &mut usize,
54 ) -> Result<(), DtErr> {
55 let mut i = 0usize;
56
57 while i < fmt.len() {
58 let byte = fmt[i];
59
60 if byte != b'%' {
61 Self::write_bytes(buf, pos, &[byte]);
62 i += 1;
63 continue;
64 }
65
66 i += 1; if i >= fmt.len() {
69 return Err(an_err!(DtErrKind::UnexpectedEnd, "after %"));
70 }
71
72 if fmt[i] == b'%' {
74 Self::write_bytes(buf, pos, b"%");
75 i += 1;
76 continue;
77 }
78
79 let mut flag = b'0'; let mut trim_trailing = false;
83 while i < fmt.len() {
84 match fmt[i] {
85 b'-' | b'0' | b'_' => {
86 flag = fmt[i];
87 i += 1;
88 }
89 b'~' => {
90 trim_trailing = true;
91 i += 1;
92 }
93 _ => break,
94 }
95 }
96
97 let mut width: Option<u8> = None;
99 let width_start = i;
100 while i < fmt.len() && fmt[i].is_ascii_digit() {
101 i += 1;
102 }
103 if i > width_start {
104 if let Ok(s) = core::str::from_utf8(&fmt[width_start..i]) {
105 if let Ok(w) = s.parse::<u8>() {
106 width = Some(w);
107 }
108 }
109 }
110
111 let mut colons: u8 = 0;
113 while i < fmt.len() && fmt[i] == b':' {
114 colons += 1;
115 i += 1;
116 }
117
118 if i >= fmt.len() {
119 return Err(an_err!(DtErrKind::UnexpectedEnd, "after %"));
120 }
121
122 let directive = fmt[i];
123 i += 1;
124
125 if directive == b'.' {
127 let mut frac_width: Option<u8> = None;
128 let frac_start = i;
129 while i < fmt.len() && fmt[i].is_ascii_digit() {
130 i += 1;
131 }
132 if i > frac_start {
133 if let Ok(s) = core::str::from_utf8(&fmt[frac_start..i]) {
134 if let Ok(w) = s.parse::<u8>() {
135 frac_width = Some(w);
136 }
137 }
138 }
139
140 if i >= fmt.len() {
141 return Err(an_err!(DtErrKind::BadFractional, "expected f or N after ."));
142 }
143
144 if fmt[i] == b'~' {
146 trim_trailing = true;
147 i += 1;
148 }
149
150 if i >= fmt.len() {
151 return Err(an_err!(DtErrKind::BadFractional, "expected f or N after ."));
152 }
153
154 let next = fmt[i];
155 i += 1;
156
157 if matches!(next, b'f' | b'N') {
158 let width_val = frac_width.unwrap_or(18);
163 let add_dot = (next == b'f') && (width_val > 0);
164
165 let dot_pos = if add_dot {
166 let p = *pos;
167 Self::write_bytes(buf, pos, b".");
168 Some(p)
169 } else {
170 None
171 };
172
173 let wrote_frac = self.write_fractional_seconds(
174 buf,
175 pos,
176 flag,
177 frac_width,
178 colons,
179 trim_trailing,
180 );
181
182 if add_dot && !wrote_frac {
183 if let Some(p) = dot_pos {
185 *pos = p;
186 }
187 }
188 continue;
189 } else {
190 return Err(an_err!(DtErrKind::BadFractional, "expected f or N after ."));
191 }
192 }
193
194 match directive {
196 b'A' => self.write_weekday_full(buf, pos),
197 b'a' => self.write_weekday_abbrev(buf, pos),
198 b'B' => self.write_month_name_full(buf, pos),
199 b'b' | b'h' => self.write_month_name_abbrev(buf, pos),
200 b'C' => self.write_century(buf, pos, flag, width, colons),
201 b'd' | b'e' => self.write_day_of_month(buf, pos, flag, width, colons, true),
202 b'f' | b'N' => {
203 let _ =
204 self.write_fractional_seconds(buf, pos, flag, width, colons, trim_trailing);
205 }
206 b'G' => self.write_iso_week_year(buf, pos, flag, width, colons),
207 b'g' => self.write_two_digit_iso_week_year(buf, pos, flag, width, colons),
208 b'H' | b'k' => self.write_hour24(buf, pos, flag, width, colons, true),
209 b'I' | b'l' => self.write_hour12(buf, pos, flag, width, colons),
210 b'j' => self.write_day_of_year(buf, pos, flag, width, colons),
211 b'M' => self.write_minute(buf, pos, flag, width, colons, true),
212 b'm' => self.write_month_number(buf, pos, flag, width, colons, true),
213 b'n' => self.write_whitespace(buf, pos, b'n'),
214 b't' => self.write_whitespace(buf, pos, b't'),
215 b'P' => self.write_ampm(buf, pos, false),
216 b'p' => self.write_ampm(buf, pos, true),
217 b'S' => self.write_second(buf, pos, flag, width, colons, true),
218 b's' => self.write_unix_timestamp(buf, pos, flag, width, colons),
219 b'U' => self.write_week_number_sunday_based(buf, pos, flag, width, colons),
220 b'u' => self.write_weekday_number_monday_based(buf, pos, flag, width, colons),
221 b'V' => self.write_week_iso(buf, pos, flag, width, colons),
222 b'W' => self.write_week_number_monday_based(buf, pos, flag, width, colons),
223 b'w' => self.write_weekday_number_sunday_based(buf, pos, flag, width, colons),
224 b'Y' => self.write_full_year(buf, pos, flag, width, colons, true),
225 b'y' => self.write_two_digit_year(buf, pos, flag, width, colons, true),
226 b'z' => self.write_timezone_offset(buf, pos, flag, width, colons),
227 b'F' => self.write_iso_date(buf, pos),
228 b'D' => self.write_us_date_shortcut(buf, pos),
229 b'T' => self.write_time_with_seconds_shortcut(buf, pos),
230 b'R' => self.write_time_without_seconds_shortcut(buf, pos),
231 b'Z' => self.write_timezone_abbrev(buf, pos),
232
233 b'Q' => {
234 if let Some(iana) = self.tz() {
241 Self::write_bytes(buf, pos, iana.as_bytes());
242 } else if let Some(abbrev) = self.tz_abbrev() {
243 Self::write_bytes(buf, pos, abbrev.as_bytes());
244 } else if self.offset_sec().unwrap_or_default() == 0 {
245 Self::write_bytes(buf, pos, "UTC".as_bytes());
246 } else if i >= fmt.len() {
247 while *pos > 0 && matches!(buf[*pos - 1], b' ' | b'\t' | b'\n' | b'\r') {
248 *pos -= 1;
249 }
250 }
251 }
252 b'*' => self.write_unbounded_year(buf, pos, flag, width, colons),
254
255 b'c' | b'r' | b'X' | b'x' => self.write_unsupported(buf, pos),
256 _ => return Err(an_err!(DtErrKind::UnknownDirective)),
257 }
258 }
259
260 Ok(())
261 }
262
263 fn write_bytes(buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize, bytes: &[u8]) {
264 let len = bytes.len();
265 if *pos + len > STRFTIME_SIZE {
266 return;
267 }
268 buf[*pos..*pos + len].copy_from_slice(bytes);
269 *pos += len;
270 }
271
272 fn write_u32_padded(
273 buf: &mut [u8; STRFTIME_SIZE],
274 pos: &mut usize,
275 mut value: u32,
276 flag: u8,
277 width: Option<u8>,
278 default_pad: u8,
279 ) {
280 let w = width.unwrap_or(2) as usize;
281
282 let pad_char = match flag {
288 b'0' => b'0',
289 b'_' => b' ',
290 _ => default_pad,
291 };
292 let pad_left = flag != b'-';
293
294 let mut digits = [0u8; 20];
295 let mut i = 0usize;
296
297 if value == 0 {
298 digits[0] = b'0';
299 i = 1;
300 } else {
301 while value > 0 {
302 digits[i] = b'0' + (value % 10) as u8;
303 value /= 10;
304 i += 1;
305 }
306 }
307 let num_digits = i;
308 let pad_len = if pad_left && num_digits < w {
309 w - num_digits
310 } else {
311 0
312 };
313
314 if *pos + num_digits + pad_len > STRFTIME_SIZE {
315 return;
316 }
317
318 if pad_left {
319 for _ in 0..pad_len {
320 buf[*pos] = pad_char;
321 *pos += 1;
322 }
323 }
324
325 for j in (0..num_digits).rev() {
326 buf[*pos] = digits[j];
327 *pos += 1;
328 }
329 }
331
332 #[allow(unused_mut)]
333 fn write_i64(mut buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize, value: i64) {
334 if value == 0 {
335 Self::write_bytes(buf, pos, b"0");
336 return;
337 }
338
339 let negative = value < 0;
340 let mut v = if negative {
341 value.wrapping_neg()
342 } else {
343 value
344 };
345
346 let mut digits = [0u8; 20];
347 let mut i = 0usize;
348 while v > 0 {
349 digits[i] = b'0' + (v % 10) as u8;
350 v /= 10;
351 i += 1;
352 }
353
354 if negative {
355 if *pos >= STRFTIME_SIZE {
356 return;
357 }
358 buf[*pos] = b'-';
359 *pos += 1;
360 }
361
362 if *pos + i > STRFTIME_SIZE {
363 return;
364 }
365 for j in (0..i).rev() {
366 buf[*pos] = digits[j];
367 *pos += 1;
368 }
369 }
370
371 fn write_i64_padded(
372 buf: &mut [u8; STRFTIME_SIZE],
373 pos: &mut usize,
374 value: i64,
375 flag: u8,
376 width: Option<u8>,
377 default_pad: u8,
378 ) {
379 let w = width.unwrap_or(4) as usize; let negative = value < 0;
382 let abs_val = if negative {
383 value.wrapping_neg()
384 } else {
385 value
386 };
387
388 let mut digits = [0u8; 20];
389 let mut i = 0usize;
390
391 let mut v = abs_val;
392 if v == 0 {
393 digits[0] = b'0';
394 i = 1;
395 } else {
396 while v > 0 {
397 digits[i] = b'0' + (v % 10) as u8;
398 v /= 10;
399 i += 1;
400 }
401 }
402
403 let num_digits = i;
404 let pad_char = match flag {
405 b'-' => b' ',
406 b'0' => b'0',
407 b'_' => b' ',
408 _ => default_pad,
409 };
410 let pad_left = flag != b'-';
411 let pad_len = if pad_left && num_digits < w {
412 w - num_digits
413 } else {
414 0
415 };
416
417 if *pos + (if negative { 1 } else { 0 }) + num_digits + pad_len > STRFTIME_SIZE {
418 return;
419 }
420
421 if negative {
422 buf[*pos] = b'-';
423 *pos += 1;
424 }
425
426 if pad_left {
427 for _ in 0..pad_len {
428 buf[*pos] = pad_char;
429 *pos += 1;
430 }
431 }
432
433 for j in (0..num_digits).rev() {
434 buf[*pos] = digits[j];
435 *pos += 1;
436 }
437 }
438
439 fn write_fractional(
440 buf: &mut [u8; STRFTIME_SIZE],
441 pos: &mut usize,
442 subsec: u64,
443 width: Option<u8>,
444 trim: bool,
445 ) -> bool {
446 let w = width.unwrap_or(18).min(18) as usize;
447 if w == 0 {
448 return false;
449 }
450
451 let mut n = subsec;
452 let mut digits = [b'0'; 18];
453 for i in (0..18).rev() {
454 digits[i] = b'0' + (n % 10) as u8;
455 n /= 10;
456 }
457
458 let mut end = w;
459 if trim {
460 while end > 0 && digits[end - 1] == b'0' {
464 end -= 1;
465 }
466 if end == 0 {
467 return false; }
469 }
470 Self::write_bytes(buf, pos, &digits[0..end]);
471 true
472 }
473
474 #[inline]
479 pub(crate) fn write_weekday_full(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
480 let name = WEEKDAYS_FULL[self.wkday as usize];
481 Self::write_bytes(buf, pos, name);
482 }
483
484 #[inline]
485 pub(crate) fn write_weekday_abbrev(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
486 let name = WEEKDAYS_ABBR[self.wkday as usize];
487 Self::write_bytes(buf, pos, name);
488 }
489
490 #[inline]
491 pub(crate) fn write_month_name_full(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
492 let name = MONTHS_FULL[self.mo as usize - 1];
493 Self::write_bytes(buf, pos, name);
494 }
495
496 #[inline]
497 pub(crate) fn write_month_name_abbrev(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
498 let name = MONTHS_ABBR[self.mo as usize - 1];
499 Self::write_bytes(buf, pos, name);
500 }
501
502 #[inline]
503 pub(crate) fn write_century(
504 &self,
505 buf: &mut [u8; STRFTIME_SIZE],
506 pos: &mut usize,
507 _flag: u8,
508 _width: Option<u8>,
509 _colons: u8,
510 ) {
511 let century = self.yr.div_euclid(100);
513 Self::write_i64(buf, pos, century);
514 }
515
516 #[inline]
517 pub(crate) fn write_day_of_month(
518 &self,
519 buf: &mut [u8; STRFTIME_SIZE],
520 pos: &mut usize,
521 flag: u8,
522 width: Option<u8>,
523 _colons: u8,
524 pad: bool,
525 ) {
526 let default_pad = if pad { b'0' } else { b' ' };
527 Self::write_u32_padded(buf, pos, self.day as u32, flag, width, default_pad);
528 }
529
530 #[inline]
531 pub(crate) fn write_fractional_seconds(
532 &self,
533 buf: &mut [u8; STRFTIME_SIZE],
534 pos: &mut usize,
535 _flag: u8,
536 width: Option<u8>,
537 _colons: u8,
538 trim: bool,
539 ) -> bool {
540 Self::write_fractional(buf, pos, self.attos, width, trim)
541 }
542
543 #[inline]
544 pub(crate) fn write_iso_week_year(
545 &self,
546 buf: &mut [u8; STRFTIME_SIZE],
547 pos: &mut usize,
548 flag: u8,
549 width: Option<u8>,
550 _colons: u8,
551 ) {
552 Self::write_i64_padded(buf, pos, self.iso_yr, flag, width, b'0');
553 }
554
555 #[inline]
556 pub(crate) fn write_two_digit_iso_week_year(
557 &self,
558 buf: &mut [u8; STRFTIME_SIZE],
559 pos: &mut usize,
560 flag: u8,
561 width: Option<u8>,
562 _colons: u8,
563 ) {
564 let yy = (self.iso_yr % 100).saturating_abs() as u32;
565 Self::write_u32_padded(buf, pos, yy, flag, width.or(Some(2)), b'0');
566 }
567
568 #[inline]
569 pub(crate) fn write_hour24(
570 &self,
571 buf: &mut [u8; STRFTIME_SIZE],
572 pos: &mut usize,
573 flag: u8,
574 width: Option<u8>,
575 _colons: u8,
576 pad: bool,
577 ) {
578 let default_pad = if pad { b'0' } else { b' ' };
579 Self::write_u32_padded(buf, pos, self.hr as u32, flag, width, default_pad);
580 }
581
582 #[inline]
583 pub(crate) fn write_hour12(
584 &self,
585 buf: &mut [u8; STRFTIME_SIZE],
586 pos: &mut usize,
587 flag: u8,
588 width: Option<u8>,
589 _colons: u8,
590 ) {
591 let hour24 = self.hr;
592 let hour12 = if hour24 == 0 {
593 12
594 } else if hour24 > 12 {
595 hour24 - 12
596 } else {
597 hour24
598 };
599 Self::write_u32_padded(buf, pos, hour12 as u32, flag, width.or(Some(2)), b'0');
600 }
601
602 #[inline]
603 pub(crate) fn write_day_of_year(
604 &self,
605 buf: &mut [u8; STRFTIME_SIZE],
606 pos: &mut usize,
607 flag: u8,
608 width: Option<u8>,
609 _colons: u8,
610 ) {
611 Self::write_u32_padded(
612 buf,
613 pos,
614 self.day_of_yr as u32,
615 flag,
616 width.or(Some(3)),
617 b'0',
618 );
619 }
620
621 #[inline]
622 pub(crate) fn write_minute(
623 &self,
624 buf: &mut [u8; STRFTIME_SIZE],
625 pos: &mut usize,
626 flag: u8,
627 width: Option<u8>,
628 _colons: u8,
629 pad: bool,
630 ) {
631 let default_pad = if pad { b'0' } else { b' ' };
632 Self::write_u32_padded(buf, pos, self.min as u32, flag, width, default_pad);
633 }
634
635 #[inline]
636 pub(crate) fn write_month_number(
637 &self,
638 buf: &mut [u8; STRFTIME_SIZE],
639 pos: &mut usize,
640 flag: u8,
641 width: Option<u8>,
642 _colons: u8,
643 pad: bool,
644 ) {
645 let default_pad = if pad { b'0' } else { b' ' };
646 Self::write_u32_padded(buf, pos, self.mo as u32, flag, width, default_pad);
647 }
648
649 #[inline]
650 pub(crate) fn write_whitespace(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize, ch: u8) {
651 let bytes = if ch == b'n' { b"\n" } else { b"\t" };
652 Self::write_bytes(buf, pos, bytes);
653 }
654
655 #[inline]
656 pub(crate) fn write_ampm(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize, upper: bool) {
657 let hour = self.hr;
658 let bytes = if hour < 12 {
659 if upper { b"AM" } else { b"am" }
660 } else if upper {
661 b"PM"
662 } else {
663 b"pm"
664 };
665 Self::write_bytes(buf, pos, bytes);
666 }
667
668 #[inline]
669 pub(crate) fn write_second(
670 &self,
671 buf: &mut [u8; STRFTIME_SIZE],
672 pos: &mut usize,
673 flag: u8,
674 width: Option<u8>,
675 _colons: u8,
676 pad: bool,
677 ) {
678 let default_pad = if pad { b'0' } else { b' ' };
679 Self::write_u32_padded(buf, pos, self.sec as u32, flag, width, default_pad);
680 }
681
682 #[inline]
683 pub(crate) fn write_unix_timestamp(
684 &self,
685 buf: &mut [u8; STRFTIME_SIZE],
686 pos: &mut usize,
687 _flag: u8,
688 _width: Option<u8>,
689 _colons: u8,
690 ) {
691 let (seconds, _) = self.unix_timestamp();
692 Self::write_i64(buf, pos, seconds);
693 }
694
695 #[inline]
696 pub(crate) fn write_weekday_number_sunday_based(
697 &self,
698 buf: &mut [u8; STRFTIME_SIZE],
699 pos: &mut usize,
700 flag: u8,
701 width: Option<u8>,
702 _colons: u8,
703 ) {
704 Self::write_u32_padded(
705 buf,
706 pos,
707 self.wkday_sun() as u32,
708 flag,
709 width.or(Some(1)),
710 b'0',
711 );
712 }
713
714 #[inline]
715 pub(crate) fn write_weekday_number_monday_based(
716 &self,
717 buf: &mut [u8; STRFTIME_SIZE],
718 pos: &mut usize,
719 flag: u8,
720 width: Option<u8>,
721 _colons: u8,
722 ) {
723 Self::write_u32_padded(
724 buf,
725 pos,
726 self.wkday_mon() as u32,
727 flag,
728 width.or(Some(1)),
729 b'0',
730 );
731 }
732
733 #[inline]
734 pub(crate) fn write_week_iso(
735 &self,
736 buf: &mut [u8; STRFTIME_SIZE],
737 pos: &mut usize,
738 flag: u8,
739 width: Option<u8>,
740 _colons: u8,
741 ) {
742 Self::write_u32_padded(buf, pos, self.iso_wk as u32, flag, width.or(Some(2)), b'0');
743 }
744
745 #[inline]
746 pub(crate) fn write_week_number_sunday_based(
747 &self,
748 buf: &mut [u8; STRFTIME_SIZE],
749 pos: &mut usize,
750 flag: u8,
751 width: Option<u8>,
752 _colons: u8,
753 ) {
754 Self::write_u32_padded(
755 buf,
756 pos,
757 self.wk_of_yr_sun as u32,
758 flag,
759 width.or(Some(2)),
760 b'0',
761 );
762 }
763
764 #[inline]
765 pub(crate) fn write_week_number_monday_based(
766 &self,
767 buf: &mut [u8; STRFTIME_SIZE],
768 pos: &mut usize,
769 flag: u8,
770 width: Option<u8>,
771 _colons: u8,
772 ) {
773 Self::write_u32_padded(
774 buf,
775 pos,
776 self.wk_of_yr_mon as u32,
777 flag,
778 width.or(Some(2)),
779 b'0',
780 );
781 }
782
783 #[inline]
784 pub(crate) fn write_full_year(
785 &self,
786 buf: &mut [u8; STRFTIME_SIZE],
787 pos: &mut usize,
788 flag: u8,
789 width: Option<u8>,
790 _colons: u8,
791 _pad: bool,
792 ) {
793 Self::write_i64_padded(buf, pos, self.yr, flag, width, b'0');
794 }
795
796 #[inline]
797 pub(crate) fn write_two_digit_year(
798 &self,
799 buf: &mut [u8; STRFTIME_SIZE],
800 pos: &mut usize,
801 flag: u8,
802 width: Option<u8>,
803 _colons: u8,
804 _pad: bool,
805 ) {
806 let yy = (self.yr % 100).saturating_abs() as u32;
807 Self::write_u32_padded(buf, pos, yy, flag, width.or(Some(2)), b'0');
808 }
809
810 #[inline]
811 pub(crate) fn write_unbounded_year(
812 &self,
813 buf: &mut [u8; STRFTIME_SIZE],
814 pos: &mut usize,
815 flag: u8,
816 width: Option<u8>,
817 _colons: u8,
818 ) {
819 Self::write_i64_padded(buf, pos, self.yr, flag, width, b'0');
820 }
821
822 pub(crate) fn write_timezone_offset(
823 &self,
824 buf: &mut [u8; STRFTIME_SIZE],
825 pos: &mut usize,
826 _flag: u8,
827 _width: Option<u8>,
828 colons: u8,
829 ) {
830 let Some(offset_sec) = self.offset_sec() else {
831 return;
832 };
833 let (negative, hours, minutes) = Dt::sec_as_hhmm(offset_sec);
834 let sign = if negative { b'-' } else { b'+' };
835
836 let seconds = ((offset_sec.saturating_abs() % 3600) % 60) as u8;
838
839 match colons {
840 0 => {
841 let mut tmp = [0u8; 5];
843 tmp[0] = sign;
844 tmp[1] = b'0' + hours / 10;
845 tmp[2] = b'0' + hours % 10;
846 tmp[3] = b'0' + minutes / 10;
847 tmp[4] = b'0' + minutes % 10;
848 Self::write_bytes(buf, pos, &tmp);
849 }
850 1 => {
851 let mut tmp = [0u8; 6];
853 tmp[0] = sign;
854 tmp[1] = b'0' + hours / 10;
855 tmp[2] = b'0' + hours % 10;
856 tmp[3] = b':';
857 tmp[4] = b'0' + minutes / 10;
858 tmp[5] = b'0' + minutes % 10;
859 Self::write_bytes(buf, pos, &tmp);
860 }
861 2 => {
862 let mut tmp = [0u8; 9];
864 tmp[0] = sign;
865 tmp[1] = b'0' + hours / 10;
866 tmp[2] = b'0' + hours % 10;
867 tmp[3] = b':';
868 tmp[4] = b'0' + minutes / 10;
869 tmp[5] = b'0' + minutes % 10;
870 tmp[6] = b':';
871 tmp[7] = b'0' + seconds / 10;
872 tmp[8] = b'0' + seconds % 10;
873 Self::write_bytes(buf, pos, &tmp);
874 }
875 _ => Self::write_bytes(buf, pos, b"+0000"),
876 }
877 }
878
879 #[inline]
880 pub(crate) fn write_timezone_abbrev(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
881 if let Some(abbrev) = self.tz_abbrev() {
882 Self::write_bytes(buf, pos, abbrev.as_bytes());
883 } else {
884 Self::write_bytes(buf, pos, b"UTC");
885 }
886 }
887
888 #[inline]
889 pub(crate) fn write_iso_date(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
890 Self::write_i64_padded(buf, pos, self.yr, b'0', Some(4), b'0');
891 Self::write_bytes(buf, pos, b"-");
892 Self::write_u32_padded(buf, pos, self.mo as u32, b'0', Some(2), b'0');
893 Self::write_bytes(buf, pos, b"-");
894 Self::write_u32_padded(buf, pos, self.day as u32, b'0', Some(2), b'0');
895 }
896
897 #[inline]
898 pub(crate) fn write_us_date_shortcut(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
899 Self::write_u32_padded(buf, pos, self.mo as u32, b'0', Some(2), b'0');
900 Self::write_bytes(buf, pos, b"/");
901 Self::write_u32_padded(buf, pos, self.day as u32, b'0', Some(2), b'0');
902 Self::write_bytes(buf, pos, b"/");
903 Self::write_u32_padded(
904 buf,
905 pos,
906 (self.yr % 100).saturating_abs() as u32,
907 b'0',
908 Some(2),
909 b'0',
910 );
911 }
912
913 #[inline]
914 pub(crate) fn write_time_with_seconds_shortcut(
915 &self,
916 buf: &mut [u8; STRFTIME_SIZE],
917 pos: &mut usize,
918 ) {
919 Self::write_u32_padded(buf, pos, self.hr as u32, b'0', Some(2), b'0');
920 Self::write_bytes(buf, pos, b":");
921 Self::write_u32_padded(buf, pos, self.min as u32, b'0', Some(2), b'0');
922 Self::write_bytes(buf, pos, b":");
923 Self::write_u32_padded(buf, pos, self.sec as u32, b'0', Some(2), b'0');
924 }
925
926 #[inline]
927 pub(crate) fn write_time_without_seconds_shortcut(
928 &self,
929 buf: &mut [u8; STRFTIME_SIZE],
930 pos: &mut usize,
931 ) {
932 Self::write_u32_padded(buf, pos, self.hr as u32, b'0', Some(2), b'0');
933 Self::write_bytes(buf, pos, b":");
934 Self::write_u32_padded(buf, pos, self.min as u32, b'0', Some(2), b'0');
935 }
936
937 #[inline]
938 pub(crate) fn write_unsupported(&self, _buf: &mut [u8; STRFTIME_SIZE], _pos: &mut usize) {
939 }
941}