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