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