Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
fmt
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
fmt
Commits
90034e4c
Unverified
Commit
90034e4c
authored
Oct 28, 2021
by
Vladislav Shchapov
Committed by
GitHub
Oct 27, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add FMT_ASSERT and validation of values of struct tm members (#2564)
Switch internal year calculations to long long
parent
df40e946
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
99 additions
and
58 deletions
+99
-58
include/fmt/chrono.h
include/fmt/chrono.h
+92
-58
test/chrono-test.cc
test/chrono-test.cc
+7
-0
No files found.
include/fmt/chrono.h
View file @
90034e4c
...
@@ -1406,79 +1406,110 @@ template <typename OutputIt, typename Char> class tm_writer {
...
@@ -1406,79 +1406,110 @@ template <typename OutputIt, typename Char> class tm_writer {
OutputIt
out_
;
OutputIt
out_
;
const
std
::
tm
&
tm_
;
const
std
::
tm
&
tm_
;
auto
tm_year
()
const
noexcept
->
int
{
return
1900
+
tm_
.
tm_year
;
}
auto
tm_sec
()
const
noexcept
->
int
{
FMT_ASSERT
(
tm_
.
tm_sec
>=
0
&&
tm_
.
tm_sec
<=
61
,
""
);
return
tm_
.
tm_sec
;
}
auto
tm_min
()
const
noexcept
->
int
{
FMT_ASSERT
(
tm_
.
tm_min
>=
0
&&
tm_
.
tm_min
<=
59
,
""
);
return
tm_
.
tm_min
;
}
auto
tm_hour
()
const
noexcept
->
int
{
FMT_ASSERT
(
tm_
.
tm_hour
>=
0
&&
tm_
.
tm_hour
<=
23
,
""
);
return
tm_
.
tm_hour
;
}
auto
tm_mday
()
const
noexcept
->
int
{
FMT_ASSERT
(
tm_
.
tm_mday
>=
1
&&
tm_
.
tm_mday
<=
31
,
""
);
return
tm_
.
tm_mday
;
}
auto
tm_mon
()
const
noexcept
->
int
{
FMT_ASSERT
(
tm_
.
tm_mon
>=
0
&&
tm_
.
tm_mon
<=
11
,
""
);
return
tm_
.
tm_mon
;
}
auto
tm_year
()
const
noexcept
->
long
long
{
return
1900ll
+
tm_
.
tm_year
;
}
auto
tm_wday
()
const
noexcept
->
int
{
FMT_ASSERT
(
tm_
.
tm_wday
>=
0
&&
tm_
.
tm_wday
<=
6
,
""
);
return
tm_
.
tm_wday
;
}
auto
tm_yday
()
const
noexcept
->
int
{
FMT_ASSERT
(
tm_
.
tm_yday
>=
0
&&
tm_
.
tm_yday
<=
365
,
""
);
return
tm_
.
tm_yday
;
}
auto
tm_hour12
()
const
noexcept
->
int
{
auto
hour
=
tm_hour
()
%
12
;
return
hour
==
0
?
12
:
hour
;
}
// POSIX and the C Standard are unclear or inconsistent about what %C and %y
// POSIX and the C Standard are unclear or inconsistent about what %C and %y
// do if the year is negative or exceeds 9999. Use the convention that %C
// do if the year is negative or exceeds 9999. Use the convention that %C
// concatenated with %y yields the same output as %Y, and that %Y contains at
// concatenated with %y yields the same output as %Y, and that %Y contains at
// least 4 characters, with more only if necessary.
// least 4 characters, with more only if necessary.
auto
split_year_lower
(
int
year
)
const
noexcept
->
int
{
auto
split_year_lower
(
long
long
year
)
const
noexcept
->
int
{
auto
l
=
year
%
100
;
auto
l
=
year
%
100
;
if
(
l
<
0
)
l
=
-
l
;
// l in [0, 99]
if
(
l
<
0
)
l
=
-
l
;
// l in [0, 99]
return
l
;
return
static_cast
<
int
>
(
l
)
;
}
}
// Algorithm:
// Algorithm:
// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
auto
iso_year_weeks
(
const
int
year
)
const
noexcept
->
int
{
auto
iso_year_weeks
(
long
long
curr_year
)
const
noexcept
->
int
{
const
long
long
curr_year
=
year
;
const
auto
prev_year
=
curr_year
-
1
;
const
long
long
prev_year
=
curr_year
-
1
;
const
auto
curr_p
=
const
int
curr_p
=
static_cast
<
int
>
(
(
curr_year
+
curr_year
/
4
-
curr_year
/
100
+
curr_year
/
400
)
%
(
curr_year
+
curr_year
/
4
-
curr_year
/
100
+
curr_year
/
400
)
%
days_per_week
)
;
days_per_week
;
const
int
prev_p
=
static_cast
<
int
>
(
const
auto
prev_p
=
(
prev_year
+
prev_year
/
4
-
prev_year
/
100
+
prev_year
/
400
)
%
(
prev_year
+
prev_year
/
4
-
prev_year
/
100
+
prev_year
/
400
)
%
days_per_week
)
;
days_per_week
;
return
52
+
((
curr_p
==
4
||
prev_p
==
3
)
?
1
:
0
);
return
52
+
((
curr_p
==
4
||
prev_p
==
3
)
?
1
:
0
);
}
}
auto
iso_week_num
(
int
tm_yday
,
int
tm_wday
)
const
noexcept
->
int
{
auto
iso_week_num
(
int
tm_yday
,
int
tm_wday
)
const
noexcept
->
int
{
return
(
tm_yday
+
11
-
(
tm_wday
==
0
?
days_per_week
:
tm_wday
))
/
return
(
tm_yday
+
11
-
(
tm_wday
==
0
?
days_per_week
:
tm_wday
))
/
days_per_week
;
days_per_week
;
}
}
auto
tm_iso_week_year
()
const
noexcept
->
int
{
auto
tm_iso_week_year
()
const
noexcept
->
long
long
{
const
auto
year
=
tm_year
();
const
auto
year
=
tm_year
();
const
int
w
=
iso_week_num
(
tm_
.
tm_yday
,
tm_
.
tm_wday
);
const
auto
w
=
iso_week_num
(
tm_yday
(),
tm_wday
()
);
if
(
w
<
1
)
return
year
-
1
;
if
(
w
<
1
)
return
year
-
1
;
if
(
w
>
iso_year_weeks
(
year
))
return
year
+
1
;
if
(
w
>
iso_year_weeks
(
year
))
return
year
+
1
;
return
year
;
return
year
;
}
}
auto
tm_iso_week_of_year
()
const
noexcept
->
int
{
auto
tm_iso_week_of_year
()
const
noexcept
->
int
{
const
auto
year
=
tm_year
();
const
auto
year
=
tm_year
();
const
int
w
=
iso_week_num
(
tm_
.
tm_yday
,
tm_
.
tm_wday
);
const
auto
w
=
iso_week_num
(
tm_yday
(),
tm_wday
()
);
if
(
w
<
1
)
return
iso_year_weeks
(
year
-
1
);
if
(
w
<
1
)
return
iso_year_weeks
(
year
-
1
);
if
(
w
>
iso_year_weeks
(
year
))
return
1
;
if
(
w
>
iso_year_weeks
(
year
))
return
1
;
return
w
;
return
w
;
}
}
auto
tm_hour12
()
const
noexcept
->
int
{
void
write1
(
int
value
)
{
auto
hour
=
tm_
.
tm_hour
%
12
;
*
out_
++
=
static_cast
<
char
>
(
'0'
+
to_unsigned
(
value
)
%
10
);
return
hour
==
0
?
12
:
hour
;
}
}
void
write1
(
int
value
)
{
*
out_
++
=
static_cast
<
char
>
(
'0'
+
value
%
10
);
}
void
write2
(
int
value
)
{
void
write2
(
int
value
)
{
const
char
*
d
=
digits2
(
to_unsigned
(
value
));
const
char
*
d
=
digits2
(
to_unsigned
(
value
)
%
100
);
*
out_
++
=
*
d
++
;
*
out_
++
=
*
d
++
;
*
out_
++
=
*
d
;
*
out_
++
=
*
d
;
}
}
void
write_year
(
int
year
)
{
void
write_year_extended
(
long
long
year
)
{
// At least 4 characters.
int
width
=
4
;
if
(
year
<
0
)
{
*
out_
++
=
'-'
;
year
=
0
-
year
;
--
width
;
}
uint32_or_64_or_128_t
<
long
long
>
n
=
to_unsigned
(
year
);
const
int
num_digits
=
count_digits
(
n
);
if
(
width
>
num_digits
)
out_
=
std
::
fill_n
(
out_
,
width
-
num_digits
,
'0'
);
out_
=
format_decimal
<
Char
>
(
out_
,
n
,
num_digits
).
end
;
}
void
write_year
(
long
long
year
)
{
if
(
year
>=
0
&&
year
<
10000
)
{
if
(
year
>=
0
&&
year
<
10000
)
{
write2
(
year
/
100
);
write2
(
static_cast
<
int
>
(
year
/
100
)
);
write2
(
year
%
100
);
write2
(
static_cast
<
int
>
(
year
%
100
)
);
}
else
{
}
else
{
// At least 4 characters.
write_year_extended
(
year
);
int
width
=
4
;
if
(
year
<
0
)
{
*
out_
++
=
'-'
;
year
=
0
-
year
;
--
width
;
}
uint32_or_64_or_128_t
<
int
>
n
=
to_unsigned
(
to_nonnegative_int
(
year
,
max_value
<
int
>
()));
const
int
num_digits
=
count_digits
(
n
);
if
(
width
>
num_digits
)
out_
=
std
::
fill_n
(
out_
,
width
-
num_digits
,
'0'
);
out_
=
format_decimal
<
Char
>
(
out_
,
n
,
num_digits
).
end
;
}
}
}
}
...
@@ -1522,11 +1553,12 @@ template <typename OutputIt, typename Char> class tm_writer {
...
@@ -1522,11 +1553,12 @@ template <typename OutputIt, typename Char> class tm_writer {
void
on_full_weekday
()
{
format_localized
(
'A'
);
}
void
on_full_weekday
()
{
format_localized
(
'A'
);
}
void
on_dec0_weekday
(
numeric_system
ns
)
{
void
on_dec0_weekday
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'w'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'w'
,
'O'
);
write1
(
tm_
.
tm_wday
);
write1
(
tm_
wday
()
);
}
}
void
on_dec1_weekday
(
numeric_system
ns
)
{
void
on_dec1_weekday
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'u'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'u'
,
'O'
);
write1
(
tm_
.
tm_wday
==
0
?
days_per_week
:
tm_
.
tm_wday
);
auto
wday
=
tm_wday
();
write1
(
wday
==
0
?
days_per_week
:
wday
);
}
}
void
on_abbr_month
()
{
format_localized
(
'b'
);
}
void
on_abbr_month
()
{
format_localized
(
'b'
);
}
...
@@ -1543,8 +1575,8 @@ template <typename OutputIt, typename Char> class tm_writer {
...
@@ -1543,8 +1575,8 @@ template <typename OutputIt, typename Char> class tm_writer {
}
}
void
on_us_date
()
{
void
on_us_date
()
{
char
buf
[
8
];
char
buf
[
8
];
write_digit2_separated
(
buf
,
to_unsigned
(
tm_
.
tm_mon
+
1
),
write_digit2_separated
(
buf
,
to_unsigned
(
tm_
mon
()
+
1
),
to_unsigned
(
tm_
.
tm_mday
),
to_unsigned
(
tm_
mday
()
),
to_unsigned
(
split_year_lower
(
tm_year
())),
'/'
);
to_unsigned
(
split_year_lower
(
tm_year
())),
'/'
);
out_
=
copy_str
<
Char
>
(
std
::
begin
(
buf
),
std
::
end
(
buf
),
out_
);
out_
=
copy_str
<
Char
>
(
std
::
begin
(
buf
),
std
::
end
(
buf
),
out_
);
}
}
...
@@ -1556,11 +1588,12 @@ template <typename OutputIt, typename Char> class tm_writer {
...
@@ -1556,11 +1588,12 @@ template <typename OutputIt, typename Char> class tm_writer {
copy2
(
buf
,
digits2
(
to_unsigned
(
year
/
100
)));
copy2
(
buf
,
digits2
(
to_unsigned
(
year
/
100
)));
}
else
{
}
else
{
offset
=
4
;
offset
=
4
;
write_year
(
year
);
write_year
_extended
(
year
);
year
=
0
;
year
=
0
;
}
}
write_digit2_separated
(
buf
+
2
,
year
%
100
,
to_unsigned
(
tm_
.
tm_mon
+
1
),
write_digit2_separated
(
buf
+
2
,
static_cast
<
unsigned
>
(
year
%
100
),
to_unsigned
(
tm_
.
tm_mday
),
'-'
);
to_unsigned
(
tm_mon
()
+
1
),
to_unsigned
(
tm_mday
()),
'-'
);
out_
=
copy_str
<
Char
>
(
std
::
begin
(
buf
)
+
offset
,
std
::
end
(
buf
),
out_
);
out_
=
copy_str
<
Char
>
(
std
::
begin
(
buf
)
+
offset
,
std
::
end
(
buf
),
out_
);
}
}
...
@@ -1586,7 +1619,7 @@ template <typename OutputIt, typename Char> class tm_writer {
...
@@ -1586,7 +1619,7 @@ template <typename OutputIt, typename Char> class tm_writer {
*
out_
++
=
'-'
;
*
out_
++
=
'-'
;
*
out_
++
=
'0'
;
*
out_
++
=
'0'
;
}
else
if
(
upper
>=
0
&&
upper
<
100
)
{
}
else
if
(
upper
>=
0
&&
upper
<
100
)
{
write2
(
upper
);
write2
(
static_cast
<
int
>
(
upper
)
);
}
else
{
}
else
{
out_
=
write
<
Char
>
(
out_
,
upper
);
out_
=
write
<
Char
>
(
out_
,
upper
);
}
}
...
@@ -1594,17 +1627,18 @@ template <typename OutputIt, typename Char> class tm_writer {
...
@@ -1594,17 +1627,18 @@ template <typename OutputIt, typename Char> class tm_writer {
void
on_dec_month
(
numeric_system
ns
)
{
void
on_dec_month
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'm'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'm'
,
'O'
);
write2
(
tm_
.
tm_mon
+
1
);
write2
(
tm_
mon
()
+
1
);
}
}
void
on_dec0_week_of_year
(
numeric_system
ns
)
{
void
on_dec0_week_of_year
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'U'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'U'
,
'O'
);
write2
((
tm_
.
tm_yday
+
days_per_week
-
tm_
.
tm_wday
)
/
days_per_week
);
write2
((
tm_
yday
()
+
days_per_week
-
tm_wday
()
)
/
days_per_week
);
}
}
void
on_dec1_week_of_year
(
numeric_system
ns
)
{
void
on_dec1_week_of_year
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'W'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'W'
,
'O'
);
write2
((
tm_
.
tm_yday
+
days_per_week
-
auto
wday
=
tm_wday
();
(
tm_
.
tm_wday
==
0
?
(
days_per_week
-
1
)
:
(
tm_
.
tm_wday
-
1
)))
/
write2
((
tm_yday
()
+
days_per_week
-
(
wday
==
0
?
(
days_per_week
-
1
)
:
(
wday
-
1
)))
/
days_per_week
);
days_per_week
);
}
}
void
on_iso_week_of_year
(
numeric_system
ns
)
{
void
on_iso_week_of_year
(
numeric_system
ns
)
{
...
@@ -1618,24 +1652,25 @@ template <typename OutputIt, typename Char> class tm_writer {
...
@@ -1618,24 +1652,25 @@ template <typename OutputIt, typename Char> class tm_writer {
}
}
void
on_day_of_year
()
{
void
on_day_of_year
()
{
auto
yday
=
tm_
.
tm_yday
+
1
;
auto
yday
=
tm_
yday
()
+
1
;
write1
(
yday
/
100
);
write1
(
yday
/
100
);
write2
(
yday
%
100
);
write2
(
yday
%
100
);
}
}
void
on_day_of_month
(
numeric_system
ns
)
{
void
on_day_of_month
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'd'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'd'
,
'O'
);
write2
(
tm_
.
tm_mday
);
write2
(
tm_
mday
()
);
}
}
void
on_day_of_month_space
(
numeric_system
ns
)
{
void
on_day_of_month_space
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'e'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'e'
,
'O'
);
const
char
*
d2
=
digits2
(
to_unsigned
(
tm_
.
tm_mday
));
auto
mday
=
to_unsigned
(
tm_mday
())
%
100
;
*
out_
++
=
tm_
.
tm_mday
<
10
?
' '
:
d2
[
0
];
const
char
*
d2
=
digits2
(
mday
);
*
out_
++
=
mday
<
10
?
' '
:
d2
[
0
];
*
out_
++
=
d2
[
1
];
*
out_
++
=
d2
[
1
];
}
}
void
on_24_hour
(
numeric_system
ns
)
{
void
on_24_hour
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'H'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'H'
,
'O'
);
write2
(
tm_
.
tm_hour
);
write2
(
tm_
hour
()
);
}
}
void
on_12_hour
(
numeric_system
ns
)
{
void
on_12_hour
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'I'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'I'
,
'O'
);
...
@@ -1643,24 +1678,23 @@ template <typename OutputIt, typename Char> class tm_writer {
...
@@ -1643,24 +1678,23 @@ template <typename OutputIt, typename Char> class tm_writer {
}
}
void
on_minute
(
numeric_system
ns
)
{
void
on_minute
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'M'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'M'
,
'O'
);
write2
(
tm_
.
tm_min
);
write2
(
tm_
min
()
);
}
}
void
on_second
(
numeric_system
ns
)
{
void
on_second
(
numeric_system
ns
)
{
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'S'
,
'O'
);
if
(
ns
!=
numeric_system
::
standard
)
return
format_localized
(
'S'
,
'O'
);
write2
(
tm_
.
tm_sec
);
write2
(
tm_
sec
()
);
}
}
void
on_12_hour_time
()
{
format_localized
(
'r'
);
}
void
on_12_hour_time
()
{
format_localized
(
'r'
);
}
void
on_24_hour_time
()
{
void
on_24_hour_time
()
{
write2
(
tm_
.
tm_hour
);
write2
(
tm_
hour
()
);
*
out_
++
=
':'
;
*
out_
++
=
':'
;
write2
(
tm_
.
tm_min
);
write2
(
tm_
min
()
);
}
}
void
on_iso_time
()
{
void
on_iso_time
()
{
char
buf
[
8
];
char
buf
[
8
];
write_digit2_separated
(
buf
,
to_unsigned
(
tm_
.
tm_hour
),
write_digit2_separated
(
buf
,
to_unsigned
(
tm_hour
()),
to_unsigned
(
tm_min
()),
to_unsigned
(
tm_
.
tm_min
),
to_unsigned
(
tm_
.
tm_sec
),
to_unsigned
(
tm_sec
()),
':'
);
':'
);
out_
=
copy_str
<
Char
>
(
std
::
begin
(
buf
),
std
::
end
(
buf
),
out_
);
out_
=
copy_str
<
Char
>
(
std
::
begin
(
buf
),
std
::
end
(
buf
),
out_
);
}
}
...
...
test/chrono-test.cc
View file @
90034e4c
...
@@ -93,6 +93,13 @@ TEST(chrono_test, format_tm) {
...
@@ -93,6 +93,13 @@ TEST(chrono_test, format_tm) {
EXPECT_EQ
(
fmt
::
format
(
"{:%Y}"
,
tm
),
"0027"
);
EXPECT_EQ
(
fmt
::
format
(
"{:%Y}"
,
tm
),
"0027"
);
EXPECT_EQ
(
fmt
::
format
(
"{:%C%y}"
,
tm
),
"0027"
);
EXPECT_EQ
(
fmt
::
format
(
"{:%C%y}"
,
tm
),
"0027"
);
// Overflow year
tm
.
tm_year
=
2147483647
;
EXPECT_EQ
(
fmt
::
format
(
"{:%Y}"
,
tm
),
"2147485547"
);
tm
.
tm_year
=
-
2147483648
;
EXPECT_EQ
(
fmt
::
format
(
"{:%Y}"
,
tm
),
"-2147481748"
);
// for week on the year
// for week on the year
// https://www.cl.cam.ac.uk/~mgk25/iso-time.html
// https://www.cl.cam.ac.uk/~mgk25/iso-time.html
std
::
vector
<
std
::
tm
>
tm_list
=
{
std
::
vector
<
std
::
tm
>
tm_list
=
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment