Line data Source code
1 : // Copyright (c) 2026 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : /** \file
20 : * \brief Verify that the timestamp functions work as expected.
21 : *
22 : * This file implements tests for the Unix timestamp functions which
23 : * convert separate integers into a time_t value at compile.
24 : */
25 :
26 : // self
27 : //
28 : #include <snapdev/timestamp.h>
29 :
30 : #include <snapdev/math.h>
31 :
32 : #include "catch_main.h"
33 :
34 :
35 : // snapdev
36 : //
37 : #include <snapdev/not_reached.h>
38 :
39 :
40 : // C++
41 : //
42 : #include <algorithm>
43 : #include <iomanip>
44 : #include <ranges>
45 :
46 :
47 : // last include
48 : //
49 : #include <snapdev/poison.h>
50 :
51 :
52 :
53 : namespace
54 : {
55 :
56 :
57 :
58 : int const g_leap_years[] = {
59 : 1584, 1588, 1592, 1596, 1600, 1604, 1608, 1612,
60 : 1616, 1620, 1624, 1628, 1632, 1636, 1640, 1644,
61 : 1648, 1652, 1656, 1660, 1664, 1668, 1672, 1676,
62 : 1680, 1684, 1688, 1692, 1696, 1704, 1708, 1712,
63 : 1716, 1720, 1724, 1728, 1732, 1736, 1740, 1744,
64 : 1748, 1752, 1756, 1760, 1764, 1768, 1772, 1776,
65 : 1780, 1784, 1788, 1792, 1796, 1804, 1808, 1812,
66 : 1816, 1820, 1824, 1828, 1832, 1836, 1840, 1844,
67 : 1848, 1852, 1856, 1860, 1864, 1868, 1872, 1876,
68 : 1880, 1884, 1888, 1892, 1896, 1904, 1908, 1912,
69 : 1916, 1920, 1924, 1928, 1932, 1936, 1940, 1944,
70 : 1948, 1952, 1956, 1960, 1964, 1968, 1972, 1976,
71 : 1980, 1984, 1988, 1992, 1996, 2000, 2004, 2008,
72 : 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040,
73 : 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072,
74 : 2076, 2080, 2084, 2088, 2092, 2096, 2104, 2108,
75 : 2112, 2116, 2120, 2124, 2128, 2132, 2136, 2140,
76 : 2144, 2148, 2152, 2156, 2160, 2164, 2168, 2172,
77 : 2176, 2180, 2184, 2188, 2192, 2196, 2204, 2208,
78 : 2212, 2216, 2220, 2224, 2228, 2232, 2236, 2240,
79 : 2244, 2248, 2252, 2256, 2260, 2264, 2268, 2272,
80 : 2276, 2280, 2284, 2288, 2292, 2296, 2304, 2308,
81 : 2312, 2316, 2320, 2324, 2328, 2332, 2336, 2340,
82 : 2344, 2348, 2352, 2356, 2360, 2364, 2368, 2372,
83 : 2376, 2380, 2384, 2388, 2392, 2396, 2400,
84 : };
85 :
86 :
87 :
88 : }
89 : // no name namespace
90 :
91 :
92 :
93 3 : CATCH_TEST_CASE("timestamp", "[time][math]")
94 : {
95 3 : CATCH_START_SECTION("timestamp: verify that the math is working as expected for February")
96 : {
97 : // the Gregorian leap years were introduced in Sept. 1752
98 : // where we started to see exceptions for the years dividable
99 : // by 100 (not a leap) or 400 (except those)
100 : //
101 824 : for(int year(1581); year <= 2403; ++year)
102 : {
103 823 : int const days(snapdev::unix_timestamp_february_days(year));
104 823 : if(days != (std::ranges::contains(g_leap_years, year) ? 29 : 28))
105 : {
106 0 : std::cerr << "error: for year " << year << ", expected "
107 0 : << (std::ranges::contains(g_leap_years, days) ? 29 : 28)
108 0 : << ", but got " << days << " instead." << std::endl;
109 : }
110 823 : CATCH_REQUIRE(days == (std::ranges::contains(g_leap_years, year) ? 29 : 28));
111 : }
112 : }
113 3 : CATCH_END_SECTION()
114 :
115 3 : CATCH_START_SECTION("timestamp: check number of days in a month")
116 : {
117 : // random year
118 : //
119 1 : int const year(snapdev::random(1581, 2403));
120 :
121 13 : for(int month(1); month <= 12; ++month)
122 : {
123 12 : int const days(snapdev::unix_timestamp_month_days(year, month));
124 :
125 12 : switch(month)
126 : {
127 7 : case 1:
128 : case 3:
129 : case 5:
130 : case 7:
131 : case 8:
132 : case 10:
133 : case 12:
134 7 : CATCH_REQUIRE(days == 31);
135 7 : break;
136 :
137 4 : case 4:
138 : case 6:
139 : case 9:
140 : case 11:
141 4 : CATCH_REQUIRE(days == 30);
142 4 : break;
143 :
144 1 : case 2:
145 : {
146 1 : int const expected_days(std::ranges::contains(g_leap_years, year) ? 29 : 28);
147 :
148 1 : CATCH_REQUIRE(expected_days == days);
149 : }
150 1 : break;
151 :
152 0 : default:
153 0 : CATCH_FAIL("invalid month?!");
154 : break;
155 :
156 : }
157 : }
158 : }
159 3 : CATCH_END_SECTION()
160 :
161 3 : CATCH_START_SECTION("timestamp: check some random time_t")
162 : {
163 1001 : for(int count(0); count < 1'000; ++count)
164 : {
165 1000 : time_t const org(snapdev::random(-1'000'000LL, 2'000'000'000LL));
166 :
167 1000 : struct tm t;
168 1000 : gmtime_r(&org, &t);
169 :
170 1000 : time_t const timestamp(snapdev::unix_timestamp(t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec));
171 :
172 1000 : CATCH_REQUIRE(org == timestamp);
173 : }
174 : }
175 3 : CATCH_END_SECTION()
176 3 : }
177 :
178 :
179 1 : CATCH_TEST_CASE("timestamp_error", "[time][error]")
180 : {
181 1 : CATCH_START_SECTION("timestamp_error: verify that the month must be between 1 and 12 calling unix_timestamp_month_days()")
182 : {
183 38 : for(int month(-12); month <= 24; ++month)
184 : {
185 37 : if(month < 1 || month > 12)
186 : {
187 25 : CATCH_REQUIRE_THROWS_MATCHES(
188 : snapdev::unix_timestamp_month_days(2000, month)
189 : , std::logic_error
190 : , Catch::Matchers::ExceptionMessage(
191 : "snapdev::unix_timestamp_month_days() called with "
192 : + std::to_string(month)
193 : + " which is not a valid month number."));
194 : }
195 : }
196 : }
197 1 : CATCH_END_SECTION()
198 1 : }
199 :
200 :
201 :
202 : // vim: ts=4 sw=4 et
|