@@ -67,6 +67,48 @@ char* strptime(const char* s, const char* fmt, std::tm* tm) {
67
67
}
68
68
#endif
69
69
70
+ // Convert a cctz::weekday to a tm_wday value (0-6, Sunday = 0).
71
+ int ToTmWday (weekday wd) {
72
+ switch (wd) {
73
+ case weekday::sunday:
74
+ return 0 ;
75
+ case weekday::monday:
76
+ return 1 ;
77
+ case weekday::tuesday:
78
+ return 2 ;
79
+ case weekday::wednesday:
80
+ return 3 ;
81
+ case weekday::thursday:
82
+ return 4 ;
83
+ case weekday::friday:
84
+ return 5 ;
85
+ case weekday::saturday:
86
+ return 6 ;
87
+ }
88
+ return 0 ; /* NOTREACHED*/
89
+ }
90
+
91
+ // Convert a tm_wday value (0-6, Sunday = 0) to a cctz::weekday.
92
+ weekday FromTmWday (int tm_wday) {
93
+ switch (tm_wday) {
94
+ case 0 :
95
+ return weekday::sunday;
96
+ case 1 :
97
+ return weekday::monday;
98
+ case 2 :
99
+ return weekday::tuesday;
100
+ case 3 :
101
+ return weekday::wednesday;
102
+ case 4 :
103
+ return weekday::thursday;
104
+ case 5 :
105
+ return weekday::friday;
106
+ case 6 :
107
+ return weekday::saturday;
108
+ }
109
+ return weekday::sunday; /* NOTREACHED*/
110
+ }
111
+
70
112
std::tm ToTM (const time_zone::absolute_lookup& al) {
71
113
std::tm tm {};
72
114
tm .tm_sec = al.cs .second ();
@@ -84,34 +126,19 @@ std::tm ToTM(const time_zone::absolute_lookup& al) {
84
126
tm .tm_year = static_cast <int >(al.cs .year () - 1900 );
85
127
}
86
128
87
- switch (get_weekday (al.cs )) {
88
- case weekday::sunday:
89
- tm .tm_wday = 0 ;
90
- break ;
91
- case weekday::monday:
92
- tm .tm_wday = 1 ;
93
- break ;
94
- case weekday::tuesday:
95
- tm .tm_wday = 2 ;
96
- break ;
97
- case weekday::wednesday:
98
- tm .tm_wday = 3 ;
99
- break ;
100
- case weekday::thursday:
101
- tm .tm_wday = 4 ;
102
- break ;
103
- case weekday::friday:
104
- tm .tm_wday = 5 ;
105
- break ;
106
- case weekday::saturday:
107
- tm .tm_wday = 6 ;
108
- break ;
109
- }
129
+ tm .tm_wday = ToTmWday (get_weekday (al.cs ));
110
130
tm .tm_yday = get_yearday (al.cs ) - 1 ;
111
131
tm .tm_isdst = al.is_dst ? 1 : 0 ;
112
132
return tm ;
113
133
}
114
134
135
+ // Returns the week of the year [0:53] given a civil day and the day on
136
+ // which weeks are defined to start.
137
+ int ToWeek (const civil_day& cd, weekday week_start) {
138
+ const civil_day d (cd.year () % 400 , cd.month (), cd.day ());
139
+ return static_cast <int >((d - prev_weekday (civil_year (d), week_start)) / 7 );
140
+ }
141
+
115
142
const char kDigits [] = " 0123456789" ;
116
143
117
144
// Formats a 64-bit integer in the given field width. Note that it is up
@@ -355,7 +382,7 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
355
382
if (cur == end || (cur - percent) % 2 == 0 ) continue ;
356
383
357
384
// Simple specifiers that we handle ourselves.
358
- if (strchr (" YmdeHMSzZs %" , *cur)) {
385
+ if (strchr (" YmdeUuWwHMSzZs %" , *cur)) {
359
386
if (cur - 1 != pending) {
360
387
FormatTM (&result, std::string (pending, cur - 1 ), tm );
361
388
}
@@ -376,6 +403,22 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
376
403
if (*cur == ' e' && *bp == ' 0' ) *bp = ' ' ; // for Windows
377
404
result.append (bp, static_cast <std::size_t >(ep - bp));
378
405
break ;
406
+ case ' U' :
407
+ bp = Format02d (ep, ToWeek (civil_day (al.cs ), weekday::sunday));
408
+ result.append (bp, static_cast <std::size_t >(ep - bp));
409
+ break ;
410
+ case ' u' :
411
+ bp = Format64 (ep, 0 , tm .tm_wday ? tm .tm_wday : 7 );
412
+ result.append (bp, static_cast <std::size_t >(ep - bp));
413
+ break ;
414
+ case ' W' :
415
+ bp = Format02d (ep, ToWeek (civil_day (al.cs ), weekday::monday));
416
+ result.append (bp, static_cast <std::size_t >(ep - bp));
417
+ break ;
418
+ case ' w' :
419
+ bp = Format64 (ep, 0 , tm .tm_wday );
420
+ result.append (bp, static_cast <std::size_t >(ep - bp));
421
+ break ;
379
422
case ' H' :
380
423
bp = Format02d (ep, al.cs .hour ());
381
424
result.append (bp, static_cast <std::size_t >(ep - bp));
@@ -610,6 +653,17 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
610
653
return dp;
611
654
}
612
655
656
+ // Sets year, tm_mon and tm_mday given the year, week_num, and tm_wday,
657
+ // and the day on which weeks are defined to start.
658
+ void FromWeek (int week_num, weekday week_start, year_t * year, std::tm * tm ) {
659
+ const civil_year y (*year % 400 );
660
+ civil_day cd = prev_weekday (y, week_start); // week 0
661
+ cd = next_weekday (cd - 1 , FromTmWday (tm ->tm_wday )) + (week_num * 7 );
662
+ *year += cd.year () - y.year ();
663
+ tm ->tm_mon = cd.month () - 1 ;
664
+ tm ->tm_mday = cd.day ();
665
+ }
666
+
613
667
} // namespace
614
668
615
669
// Uses strptime(3) to parse the given input. Supports the same extended
@@ -659,6 +713,8 @@ bool parse(const std::string& format, const std::string& input,
659
713
const char * fmt = format.c_str (); // NUL terminated
660
714
bool twelve_hour = false ;
661
715
bool afternoon = false ;
716
+ int week_num = -1 ;
717
+ weekday week_start = weekday::sunday;
662
718
663
719
bool saw_percent_s = false ;
664
720
std::int_fast64_t percent_s = 0 ;
@@ -697,10 +753,27 @@ bool parse(const std::string& format, const std::string& input,
697
753
case ' m' :
698
754
data = ParseInt (data, 2 , 1 , 12 , &tm .tm_mon );
699
755
if (data != nullptr ) tm .tm_mon -= 1 ;
756
+ week_num = -1 ;
700
757
continue ;
701
758
case ' d' :
702
759
case ' e' :
703
760
data = ParseInt (data, 2 , 1 , 31 , &tm .tm_mday );
761
+ week_num = -1 ;
762
+ continue ;
763
+ case ' U' :
764
+ data = ParseInt (data, 0 , 0 , 53 , &week_num);
765
+ week_start = weekday::sunday;
766
+ continue ;
767
+ case ' W' :
768
+ data = ParseInt (data, 0 , 0 , 53 , &week_num);
769
+ week_start = weekday::monday;
770
+ continue ;
771
+ case ' u' :
772
+ data = ParseInt (data, 0 , 1 , 7 , &tm .tm_wday );
773
+ if (data != nullptr ) tm .tm_wday %= 7 ;
774
+ continue ;
775
+ case ' w' :
776
+ data = ParseInt (data, 0 , 0 , 6 , &tm .tm_wday );
704
777
continue ;
705
778
case ' H' :
706
779
data = ParseInt (data, 2 , 0 , 23 , &tm .tm_hour );
@@ -891,6 +964,9 @@ bool parse(const std::string& format, const std::string& input,
891
964
year += 1900 ;
892
965
}
893
966
967
+ // Compute year, tm.tm_mon and tm.tm_mday if we parsed a week number.
968
+ if (week_num != -1 ) FromWeek (week_num, week_start, &year, &tm );
969
+
894
970
const int month = tm .tm_mon + 1 ;
895
971
civil_second cs (year, month, tm .tm_mday , tm .tm_hour , tm .tm_min , tm .tm_sec );
896
972
0 commit comments