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