1use crate::{Dt, DtErr, DtErrKind, LiteStr, STRFTIME_SIZE, Scale, 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'L' => {
264 if self.scale != Scale::UTC {
265 Self::write_bytes(buf, pos, self.scale.abbrev().as_bytes())
266 }
267 }
268 b'*' => self.write_unbounded_year(buf, pos, flag, width, colons),
269
270 b'c' | b'r' | b'X' | b'x' => self.write_unsupported(buf, pos),
271 _ => return Err(an_err!(DtErrKind::UnknownDirective)),
272 }
273 }
274
275 Ok(())
276 }
277
278 fn write_bytes(buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize, bytes: &[u8]) {
279 let len = bytes.len();
280 if *pos + len > STRFTIME_SIZE {
281 return;
282 }
283 buf[*pos..*pos + len].copy_from_slice(bytes);
284 *pos += len;
285 }
286
287 fn write_u32_padded(
288 buf: &mut [u8; STRFTIME_SIZE],
289 pos: &mut usize,
290 mut value: u32,
291 flag: u8,
292 width: Option<u8>,
293 default_pad: u8,
294 ) {
295 let w = width.unwrap_or(2) as usize;
296
297 let pad_char = match flag {
303 b'0' => b'0',
304 b'_' => b' ',
305 _ => default_pad,
306 };
307 let pad_left = flag != b'-';
308
309 let mut digits = [0u8; 20];
310 let mut i = 0usize;
311
312 if value == 0 {
313 digits[0] = b'0';
314 i = 1;
315 } else {
316 while value > 0 {
317 digits[i] = b'0' + (value % 10) as u8;
318 value /= 10;
319 i += 1;
320 }
321 }
322 let num_digits = i;
323 let pad_len = if pad_left && num_digits < w {
324 w - num_digits
325 } else {
326 0
327 };
328
329 if *pos + num_digits + pad_len > STRFTIME_SIZE {
330 return;
331 }
332
333 if pad_left {
334 for _ in 0..pad_len {
335 buf[*pos] = pad_char;
336 *pos += 1;
337 }
338 }
339
340 for j in (0..num_digits).rev() {
341 buf[*pos] = digits[j];
342 *pos += 1;
343 }
344 }
346
347 #[allow(unused_mut)]
348 fn write_i64(mut buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize, value: i64) {
349 if value == 0 {
350 Self::write_bytes(buf, pos, b"0");
351 return;
352 }
353
354 let negative = value < 0;
355 let mut v = if negative {
356 value.wrapping_neg()
357 } else {
358 value
359 };
360
361 let mut digits = [0u8; 20];
362 let mut i = 0usize;
363 while v > 0 {
364 digits[i] = b'0' + (v % 10) as u8;
365 v /= 10;
366 i += 1;
367 }
368
369 if negative {
370 if *pos >= STRFTIME_SIZE {
371 return;
372 }
373 buf[*pos] = b'-';
374 *pos += 1;
375 }
376
377 if *pos + i > STRFTIME_SIZE {
378 return;
379 }
380 for j in (0..i).rev() {
381 buf[*pos] = digits[j];
382 *pos += 1;
383 }
384 }
385
386 fn write_i64_padded(
387 buf: &mut [u8; STRFTIME_SIZE],
388 pos: &mut usize,
389 value: i64,
390 flag: u8,
391 width: Option<u8>,
392 default_pad: u8,
393 ) {
394 let w = width.unwrap_or(4) as usize; let negative = value < 0;
397 let abs_val = if negative {
398 value.wrapping_neg()
399 } else {
400 value
401 };
402
403 let mut digits = [0u8; 20];
404 let mut i = 0usize;
405
406 let mut v = abs_val;
407 if v == 0 {
408 digits[0] = b'0';
409 i = 1;
410 } else {
411 while v > 0 {
412 digits[i] = b'0' + (v % 10) as u8;
413 v /= 10;
414 i += 1;
415 }
416 }
417
418 let num_digits = i;
419 let pad_char = match flag {
420 b'-' => b' ',
421 b'0' => b'0',
422 b'_' => b' ',
423 _ => default_pad,
424 };
425 let pad_left = flag != b'-';
426 let pad_len = if pad_left && num_digits < w {
427 w - num_digits
428 } else {
429 0
430 };
431
432 if *pos + (if negative { 1 } else { 0 }) + num_digits + pad_len > STRFTIME_SIZE {
433 return;
434 }
435
436 if negative {
437 buf[*pos] = b'-';
438 *pos += 1;
439 }
440
441 if pad_left {
442 for _ in 0..pad_len {
443 buf[*pos] = pad_char;
444 *pos += 1;
445 }
446 }
447
448 for j in (0..num_digits).rev() {
449 buf[*pos] = digits[j];
450 *pos += 1;
451 }
452 }
453
454 fn write_fractional(
455 buf: &mut [u8; STRFTIME_SIZE],
456 pos: &mut usize,
457 subsec: u64,
458 width: Option<u8>,
459 trim: bool,
460 ) -> bool {
461 let w = width.unwrap_or(18).min(18) as usize;
462 if w == 0 {
463 return false;
464 }
465
466 let mut n = subsec;
467 let mut digits = [b'0'; 18];
468 for i in (0..18).rev() {
469 digits[i] = b'0' + (n % 10) as u8;
470 n /= 10;
471 }
472
473 let mut end = w;
474 if trim {
475 while end > 0 && digits[end - 1] == b'0' {
479 end -= 1;
480 }
481 if end == 0 {
482 return false; }
484 }
485 Self::write_bytes(buf, pos, &digits[0..end]);
486 true
487 }
488
489 #[inline]
494 pub(crate) fn write_weekday_full(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
495 let name = WEEKDAYS_FULL[self.wkday as usize];
496 Self::write_bytes(buf, pos, name);
497 }
498
499 #[inline]
500 pub(crate) fn write_weekday_abbrev(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
501 let name = WEEKDAYS_ABBR[self.wkday as usize];
502 Self::write_bytes(buf, pos, name);
503 }
504
505 #[inline]
506 pub(crate) fn write_month_name_full(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
507 let name = MONTHS_FULL[self.mo as usize - 1];
508 Self::write_bytes(buf, pos, name);
509 }
510
511 #[inline]
512 pub(crate) fn write_month_name_abbrev(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
513 let name = MONTHS_ABBR[self.mo as usize - 1];
514 Self::write_bytes(buf, pos, name);
515 }
516
517 #[inline]
518 pub(crate) fn write_century(
519 &self,
520 buf: &mut [u8; STRFTIME_SIZE],
521 pos: &mut usize,
522 _flag: u8,
523 _width: Option<u8>,
524 _colons: u8,
525 ) {
526 let century = self.yr.div_euclid(100);
528 Self::write_i64(buf, pos, century);
529 }
530
531 #[inline]
532 pub(crate) fn write_day_of_month(
533 &self,
534 buf: &mut [u8; STRFTIME_SIZE],
535 pos: &mut usize,
536 flag: u8,
537 width: Option<u8>,
538 _colons: u8,
539 pad: bool,
540 ) {
541 let default_pad = if pad { b'0' } else { b' ' };
542 Self::write_u32_padded(buf, pos, self.day as u32, flag, width, default_pad);
543 }
544
545 #[inline]
546 pub(crate) fn write_fractional_seconds(
547 &self,
548 buf: &mut [u8; STRFTIME_SIZE],
549 pos: &mut usize,
550 _flag: u8,
551 width: Option<u8>,
552 _colons: u8,
553 trim: bool,
554 ) -> bool {
555 Self::write_fractional(buf, pos, self.attos, width, trim)
556 }
557
558 #[inline]
559 pub(crate) fn write_iso_week_year(
560 &self,
561 buf: &mut [u8; STRFTIME_SIZE],
562 pos: &mut usize,
563 flag: u8,
564 width: Option<u8>,
565 _colons: u8,
566 ) {
567 Self::write_i64_padded(buf, pos, self.iso_yr, flag, width, b'0');
568 }
569
570 #[inline]
571 pub(crate) fn write_two_digit_iso_week_year(
572 &self,
573 buf: &mut [u8; STRFTIME_SIZE],
574 pos: &mut usize,
575 flag: u8,
576 width: Option<u8>,
577 _colons: u8,
578 ) {
579 let yy = (self.iso_yr % 100).saturating_abs() as u32;
580 Self::write_u32_padded(buf, pos, yy, flag, width.or(Some(2)), b'0');
581 }
582
583 #[inline]
584 pub(crate) fn write_hour24(
585 &self,
586 buf: &mut [u8; STRFTIME_SIZE],
587 pos: &mut usize,
588 flag: u8,
589 width: Option<u8>,
590 _colons: u8,
591 pad: bool,
592 ) {
593 let default_pad = if pad { b'0' } else { b' ' };
594 Self::write_u32_padded(buf, pos, self.hr as u32, flag, width, default_pad);
595 }
596
597 #[inline]
598 pub(crate) fn write_hour12(
599 &self,
600 buf: &mut [u8; STRFTIME_SIZE],
601 pos: &mut usize,
602 flag: u8,
603 width: Option<u8>,
604 _colons: u8,
605 ) {
606 let hour24 = self.hr;
607 let hour12 = if hour24 == 0 {
608 12
609 } else if hour24 > 12 {
610 hour24 - 12
611 } else {
612 hour24
613 };
614 Self::write_u32_padded(buf, pos, hour12 as u32, flag, width.or(Some(2)), b'0');
615 }
616
617 #[inline]
618 pub(crate) fn write_day_of_year(
619 &self,
620 buf: &mut [u8; STRFTIME_SIZE],
621 pos: &mut usize,
622 flag: u8,
623 width: Option<u8>,
624 _colons: u8,
625 ) {
626 Self::write_u32_padded(
627 buf,
628 pos,
629 self.day_of_yr as u32,
630 flag,
631 width.or(Some(3)),
632 b'0',
633 );
634 }
635
636 #[inline]
637 pub(crate) fn write_minute(
638 &self,
639 buf: &mut [u8; STRFTIME_SIZE],
640 pos: &mut usize,
641 flag: u8,
642 width: Option<u8>,
643 _colons: u8,
644 pad: bool,
645 ) {
646 let default_pad = if pad { b'0' } else { b' ' };
647 Self::write_u32_padded(buf, pos, self.min as u32, flag, width, default_pad);
648 }
649
650 #[inline]
651 pub(crate) fn write_month_number(
652 &self,
653 buf: &mut [u8; STRFTIME_SIZE],
654 pos: &mut usize,
655 flag: u8,
656 width: Option<u8>,
657 _colons: u8,
658 pad: bool,
659 ) {
660 let default_pad = if pad { b'0' } else { b' ' };
661 Self::write_u32_padded(buf, pos, self.mo as u32, flag, width, default_pad);
662 }
663
664 #[inline]
665 pub(crate) fn write_whitespace(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize, ch: u8) {
666 let bytes = if ch == b'n' { b"\n" } else { b"\t" };
667 Self::write_bytes(buf, pos, bytes);
668 }
669
670 #[inline]
671 pub(crate) fn write_ampm(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize, upper: bool) {
672 let hour = self.hr;
673 let bytes = if hour < 12 {
674 if upper { b"AM" } else { b"am" }
675 } else if upper {
676 b"PM"
677 } else {
678 b"pm"
679 };
680 Self::write_bytes(buf, pos, bytes);
681 }
682
683 #[inline]
684 pub(crate) fn write_second(
685 &self,
686 buf: &mut [u8; STRFTIME_SIZE],
687 pos: &mut usize,
688 flag: u8,
689 width: Option<u8>,
690 _colons: u8,
691 pad: bool,
692 ) {
693 let default_pad = if pad { b'0' } else { b' ' };
694 Self::write_u32_padded(buf, pos, self.sec as u32, flag, width, default_pad);
695 }
696
697 #[inline]
698 pub(crate) fn write_unix_timestamp(
699 &self,
700 buf: &mut [u8; STRFTIME_SIZE],
701 pos: &mut usize,
702 _flag: u8,
703 _width: Option<u8>,
704 _colons: u8,
705 ) {
706 let (seconds, _) = self.unix_timestamp();
707 Self::write_i64(buf, pos, seconds);
708 }
709
710 #[inline]
711 pub(crate) fn write_weekday_number_sunday_based(
712 &self,
713 buf: &mut [u8; STRFTIME_SIZE],
714 pos: &mut usize,
715 flag: u8,
716 width: Option<u8>,
717 _colons: u8,
718 ) {
719 Self::write_u32_padded(
720 buf,
721 pos,
722 self.wkday_sun() as u32,
723 flag,
724 width.or(Some(1)),
725 b'0',
726 );
727 }
728
729 #[inline]
730 pub(crate) fn write_weekday_number_monday_based(
731 &self,
732 buf: &mut [u8; STRFTIME_SIZE],
733 pos: &mut usize,
734 flag: u8,
735 width: Option<u8>,
736 _colons: u8,
737 ) {
738 Self::write_u32_padded(
739 buf,
740 pos,
741 self.wkday_mon() as u32,
742 flag,
743 width.or(Some(1)),
744 b'0',
745 );
746 }
747
748 #[inline]
749 pub(crate) fn write_week_iso(
750 &self,
751 buf: &mut [u8; STRFTIME_SIZE],
752 pos: &mut usize,
753 flag: u8,
754 width: Option<u8>,
755 _colons: u8,
756 ) {
757 Self::write_u32_padded(buf, pos, self.iso_wk as u32, flag, width.or(Some(2)), b'0');
758 }
759
760 #[inline]
761 pub(crate) fn write_week_number_sunday_based(
762 &self,
763 buf: &mut [u8; STRFTIME_SIZE],
764 pos: &mut usize,
765 flag: u8,
766 width: Option<u8>,
767 _colons: u8,
768 ) {
769 Self::write_u32_padded(
770 buf,
771 pos,
772 self.wk_of_yr_sun as u32,
773 flag,
774 width.or(Some(2)),
775 b'0',
776 );
777 }
778
779 #[inline]
780 pub(crate) fn write_week_number_monday_based(
781 &self,
782 buf: &mut [u8; STRFTIME_SIZE],
783 pos: &mut usize,
784 flag: u8,
785 width: Option<u8>,
786 _colons: u8,
787 ) {
788 Self::write_u32_padded(
789 buf,
790 pos,
791 self.wk_of_yr_mon as u32,
792 flag,
793 width.or(Some(2)),
794 b'0',
795 );
796 }
797
798 #[inline]
799 pub(crate) fn write_full_year(
800 &self,
801 buf: &mut [u8; STRFTIME_SIZE],
802 pos: &mut usize,
803 flag: u8,
804 width: Option<u8>,
805 _colons: u8,
806 _pad: bool,
807 ) {
808 Self::write_i64_padded(buf, pos, self.yr, flag, width, b'0');
809 }
810
811 #[inline]
812 pub(crate) fn write_two_digit_year(
813 &self,
814 buf: &mut [u8; STRFTIME_SIZE],
815 pos: &mut usize,
816 flag: u8,
817 width: Option<u8>,
818 _colons: u8,
819 _pad: bool,
820 ) {
821 let yy = (self.yr % 100).saturating_abs() as u32;
822 Self::write_u32_padded(buf, pos, yy, flag, width.or(Some(2)), b'0');
823 }
824
825 #[inline]
826 pub(crate) fn write_unbounded_year(
827 &self,
828 buf: &mut [u8; STRFTIME_SIZE],
829 pos: &mut usize,
830 flag: u8,
831 width: Option<u8>,
832 _colons: u8,
833 ) {
834 Self::write_i64_padded(buf, pos, self.yr, flag, width, b'0');
835 }
836
837 pub(crate) fn write_timezone_offset(
838 &self,
839 buf: &mut [u8; STRFTIME_SIZE],
840 pos: &mut usize,
841 _flag: u8,
842 _width: Option<u8>,
843 colons: u8,
844 ) {
845 let Some(offset_sec) = self.offset_sec() else {
846 return;
847 };
848 let (negative, hours, minutes) = Dt::sec_as_hhmm(offset_sec);
849 let sign = if negative { b'-' } else { b'+' };
850
851 let seconds = ((offset_sec.saturating_abs() % 3600) % 60) as u8;
853
854 match colons {
855 0 => {
856 let mut tmp = [0u8; 5];
858 tmp[0] = sign;
859 tmp[1] = b'0' + hours / 10;
860 tmp[2] = b'0' + hours % 10;
861 tmp[3] = b'0' + minutes / 10;
862 tmp[4] = b'0' + minutes % 10;
863 Self::write_bytes(buf, pos, &tmp);
864 }
865 1 => {
866 let mut tmp = [0u8; 6];
868 tmp[0] = sign;
869 tmp[1] = b'0' + hours / 10;
870 tmp[2] = b'0' + hours % 10;
871 tmp[3] = b':';
872 tmp[4] = b'0' + minutes / 10;
873 tmp[5] = b'0' + minutes % 10;
874 Self::write_bytes(buf, pos, &tmp);
875 }
876 2 => {
877 let mut tmp = [0u8; 9];
879 tmp[0] = sign;
880 tmp[1] = b'0' + hours / 10;
881 tmp[2] = b'0' + hours % 10;
882 tmp[3] = b':';
883 tmp[4] = b'0' + minutes / 10;
884 tmp[5] = b'0' + minutes % 10;
885 tmp[6] = b':';
886 tmp[7] = b'0' + seconds / 10;
887 tmp[8] = b'0' + seconds % 10;
888 Self::write_bytes(buf, pos, &tmp);
889 }
890 _ => Self::write_bytes(buf, pos, b"+0000"),
891 }
892 }
893
894 #[inline]
895 pub(crate) fn write_timezone_abbrev(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
896 if let Some(abbrev) = self.tz_abbrev() {
897 Self::write_bytes(buf, pos, abbrev.as_bytes());
898 } else {
899 Self::write_bytes(buf, pos, b"UTC");
900 }
901 }
902
903 #[inline]
904 pub(crate) fn write_iso_date(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
905 Self::write_i64_padded(buf, pos, self.yr, b'0', Some(4), b'0');
906 Self::write_bytes(buf, pos, b"-");
907 Self::write_u32_padded(buf, pos, self.mo as u32, b'0', Some(2), b'0');
908 Self::write_bytes(buf, pos, b"-");
909 Self::write_u32_padded(buf, pos, self.day as u32, b'0', Some(2), b'0');
910 }
911
912 #[inline]
913 pub(crate) fn write_us_date_shortcut(&self, buf: &mut [u8; STRFTIME_SIZE], pos: &mut usize) {
914 Self::write_u32_padded(buf, pos, self.mo as u32, b'0', Some(2), b'0');
915 Self::write_bytes(buf, pos, b"/");
916 Self::write_u32_padded(buf, pos, self.day as u32, b'0', Some(2), b'0');
917 Self::write_bytes(buf, pos, b"/");
918 Self::write_u32_padded(
919 buf,
920 pos,
921 (self.yr % 100).saturating_abs() as u32,
922 b'0',
923 Some(2),
924 b'0',
925 );
926 }
927
928 #[inline]
929 pub(crate) fn write_time_with_seconds_shortcut(
930 &self,
931 buf: &mut [u8; STRFTIME_SIZE],
932 pos: &mut usize,
933 ) {
934 Self::write_u32_padded(buf, pos, self.hr as u32, b'0', Some(2), b'0');
935 Self::write_bytes(buf, pos, b":");
936 Self::write_u32_padded(buf, pos, self.min as u32, b'0', Some(2), b'0');
937 Self::write_bytes(buf, pos, b":");
938 Self::write_u32_padded(buf, pos, self.sec as u32, b'0', Some(2), b'0');
939 }
940
941 #[inline]
942 pub(crate) fn write_time_without_seconds_shortcut(
943 &self,
944 buf: &mut [u8; STRFTIME_SIZE],
945 pos: &mut usize,
946 ) {
947 Self::write_u32_padded(buf, pos, self.hr as u32, b'0', Some(2), b'0');
948 Self::write_bytes(buf, pos, b":");
949 Self::write_u32_padded(buf, pos, self.min as u32, b'0', Some(2), b'0');
950 }
951
952 #[inline]
953 pub(crate) fn write_unsupported(&self, _buf: &mut [u8; STRFTIME_SIZE], _pos: &mut usize) {
954 }
956}