netxsimdg
Loading...
Searching...
No Matches
Time_test.cpp
Go to the documentation of this file.
1
8#include "include/Time.hpp"
9
10#include <sstream>
11
12#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
13#include <doctest/doctest.h>
14
15namespace Nextsim {
16
17TEST_SUITE_BEGIN("Time");
18TEST_CASE("TimePoint parsing and formating")
19{
20 // Time with explicit timezone marker so the initial and final strings match.
21 std::stringstream is("2022-06-07T14:16:01Z");
22
23 TimePoint tp;
24 tp.parse(is);
25
26 std::stringstream os;
27 tp.format(os);
28
29 REQUIRE(is.str() == os.str());
30
31 // Directly with strings
32 TimePoint tq;
33 std::string rightNow = "2022-06-10T09:33:21Z";
34 tq.parse(rightNow);
35 REQUIRE(tq.format() == rightNow);
36}
37
38TEST_CASE("Julian-Gregorian shifts")
39{
40 // In the 1500s, the inital 10 day shift.
41 REQUIRE(julianGregorianShiftDays(1582) == -10);
42 // In the 1600s, 1600 had been a leap year, as expected, so still 10 days.
43 REQUIRE(julianGregorianShiftDays(1688) == -10);
44 // In 1700, the different leap day had not yet been reached.
45 REQUIRE(julianGregorianShiftDays(1700) == -10);
46 // By 1701, there had been one extra skipped Gregorian leap day.
47 REQUIRE(julianGregorianShiftDays(1701) == -11);
48 // By 1970, there had been 3 further fewer Gregorian leap days (1700, 1800, 1900).
49 REQUIRE(julianGregorianShiftDays(1970) == -13);
50 // In 2525, if man is still alive, there will have been 7 further fewer
51 // Gregorian leap days (1700, 1800, 1900, 2100, 2200, 2300, 2500).
52 REQUIRE(julianGregorianShiftDays(2525) == -17);
53}
54
55TEST_CASE("mkgmtime")
56{
57 const int days = 24 * 60 * 60;
58
59 const char* iso = TimePoint::ymdhmsFormat.c_str();
60
61 std::stringstream ss("1970-01-01T00:00:00");
62 std::tm epoch_tm;
63 ss >> std::get_time(&epoch_tm, iso);
64
65 std::time_t epoch_time = mkgmtime(&epoch_tm);
66
67 REQUIRE(epoch_time == 0);
68
69 std::stringstream ss1("1971-01-01T00:00:00");
70 std::tm tm;
71 // tmZero(tm);
72 ss1 >> std::get_time(&tm, iso);
73 REQUIRE(mkgmtime(&tm) == 365 * days);
74
75 std::tm tow_tm;
76 std::stringstream stow("2022-06-07T15:37:45"); // UTC
77 stow >> std::get_time(&tow_tm, iso);
78 std::time_t timeOfWriting = mkgmtime(&tow_tm);
79
80 REQUIRE(timeOfWriting == 1654616265);
81
82 // Leap Day William
83 std::stringstream("1980-02-28T00:00:00") >> std::get_time(&tm, iso);
84 std::time_t before = mkgmtime(&tm);
85
86 std::stringstream("1980-03-01T00:00:00") >> std::get_time(&tm, iso);
87 std::time_t after = mkgmtime(&tm);
88
89 REQUIRE((after - before) == 2 * days);
90 std::stringstream("1980-02-13T00:00:00") >> std::get_time(&tm, iso);
91 REQUIRE(mkgmtime(&tm) == 319248000);
92
93 // Gregorian non-leap day
94 std::stringstream("2100-02-28T00:00:00") >> std::get_time(&tm, iso);
95 before = mkgmtime(&tm);
96
97 std::stringstream("2100-03-01T00:00:00") >> std::get_time(&tm, iso);
98 after = mkgmtime(&tm);
99
100 REQUIRE((after - before) == 1 * days);
101 // REQUIRE(std::mktime(&tm) > 0); // How does linux return -ve here?
102 REQUIRE(before == 4107456000);
103 REQUIRE(after == 4107542400);
104
105 // Turn of the 22nd century
106 std::stringstream("2099-12-31T00:00:00") >> std::get_time(&tm, iso);
107 REQUIRE(mkgmtime(&tm) == 4102358400);
108 std::stringstream("2100-01-01T00:00:00") >> std::get_time(&tm, iso);
109 REQUIRE(mkgmtime(&tm) == 4102444800);
110 std::stringstream("2101-01-01T00:00:00") >> std::get_time(&tm, iso);
111 REQUIRE(mkgmtime(&tm) == 4133980800);
112 std::stringstream("2101-01-01T00:00:00") >> std::get_time(&tm, iso);
113 after = mkgmtime(&tm);
114 std::stringstream("2100-01-01T00:00:00") >> std::get_time(&tm, iso);
115 before = mkgmtime(&tm);
116 REQUIRE(after - before == 365 * days);
117}
118
119TEST_CASE("timeFromISO")
120{
121 const int days = 24 * 60 * 60;
122
123 // Not a date, should throw
124 REQUIRE_THROWS(timeFromISO("fhqwhgads"));
125
126 // Default format
127 REQUIRE(timeFromISO("1970-01-03T12:00:00Z") == 2 * days + (days / 2));
128 // No Zulu time marker
129 REQUIRE(timeFromISO("1970-02-01T06:00:00") == 31 * days + (days / 4));
130 // No time value (defaults to 00UT)
131 REQUIRE(timeFromISO("1971-01-02") == 366 * days);
132
133 // YYYY-DDD parsing
134 REQUIRE(timeFromISO("1971-003T00:00:00") == 367 * days);
135 REQUIRE(timeFromISO("1971-031") == 395 * days);
136
137 // Set DoY only parsing, try reading YMD
138 bool previousDOY = TimeOptions::useDOY();
139 TimeOptions::useDOY() = true;
140 REQUIRE_THROWS(timeFromISO("1971-01-02"));
141 TimeOptions::useDOY() = previousDOY;
142}
143
144TEST_CASE("TimePoints")
145{
146 TimePoint tp;
147 TimePoint tq;
148
149 // Comparison operators
150 REQUIRE(tp.parse("1980-07-30") == tq.parse("1980-212"));
151 REQUIRE(tp.parse("1980-07-30") > tq.parse("1980-07-29"));
152 REQUIRE(tp.parse("1980-07-30") >= tq.parse("1980-212"));
153 REQUIRE(tp.parse("1980-07-30") >= tq.parse("1980-07-29"));
154 REQUIRE(tp.parse("1980-07-30") < tq.parse("1980-07-31"));
155 REQUIRE(tp.parse("1980-07-30") <= tq.parse("1980-212"));
156 REQUIRE(tp.parse("1980-07-30") <= tq.parse("1980-07-31"));
157 REQUIRE(tp.parse("1980-07-30") != tq.parse("1980-07-29"));
158}
159
160TEST_CASE("Durations")
161{
162 const int days = 24 * 60 * 60;
163
164 Duration dur;
165 // Basic values
166 REQUIRE_THROWS(dur.parse("0-0-0T0:0:1"));
167 REQUIRE(dur.parse("P0-1").seconds() == 1 * days);
168 REQUIRE(dur.parse("P0-0T0:0:1").seconds() == 1);
169 REQUIRE(dur.parse("P-0-0T0:0:1").seconds() == -1);
170
171 // arithmetic
172 Duration yr("P1-0");
173 Duration dy("P0-1");
174 Duration s("P0-0T0:0:1");
175
176 int daySeconds = 86400;
177 int yearSeconds = 365 * daySeconds;
178 REQUIRE((yr + dy).seconds() == yearSeconds + daySeconds);
179 REQUIRE((dy - yr).seconds() == daySeconds - yearSeconds);
180
181 yr += s;
182 REQUIRE(yr.seconds() == yearSeconds + 1);
183 yr -= s;
184 REQUIRE(yr.seconds() == yearSeconds);
185 yr *= 2;
186 REQUIRE(yr.seconds() == 2 * yearSeconds);
187 yr /= 2;
188 REQUIRE(yr.seconds() == yearSeconds);
189
190 // TimePoint manipulation
191 TimePoint tt("2010-01-01T00:00:00Z");
192 TimePoint tt_day("2010-01-02T00:00:00Z");
193
194 REQUIRE(tt + dy == tt_day);
195 REQUIRE(dy + tt == tt_day);
196
197 TimePoint ttOther(tt);
198 ttOther += dy;
199 REQUIRE(ttOther == tt_day);
200
201 Duration maybeADay = tt_day - tt;
202 REQUIRE(maybeADay.seconds() == 1 * days);
203
204 Duration tenSeconds(10.);
205 REQUIRE(tenSeconds.seconds() == 10);
206
207 Duration hundredSeconds("100.");
208 REQUIRE(hundredSeconds.seconds() == 100);
209
210 Duration minusThousandSeconds("-1000.");
211 REQUIRE(minusThousandSeconds.seconds() == -1000);
212
213 Duration myriadSeconds(1e4);
214 REQUIRE(myriadSeconds.seconds() == 10000);
215
216 Duration strMyriadSeconds("1e4");
217 REQUIRE(strMyriadSeconds.seconds() == 10000);
218
219 Duration complexStrSec("-1440.42e-2");
220 REQUIRE(complexStrSec.seconds() == -14);
221
222 Duration aDay(daySeconds);
223 REQUIRE(aDay.seconds() == daySeconds);
224}
225
226TEST_CASE("gmtime and doy")
227{
228 TimePoint janfirst("2010-01-01T00:00:00Z");
229 std::tm* timeStruct = janfirst.gmtime();
230 REQUIRE(timeStruct->tm_yday == 0);
231
232 // Test that leap years work
233 TimePoint marchfirst("2010-03-01T00:00:00Z");
234 REQUIRE(marchfirst.gmtime()->tm_yday == 59);
235 TimePoint bissextile("2020-03-01T00:00:00Z");
236 REQUIRE(bissextile.gmtime()->tm_yday == 60);
237}
238TEST_SUITE_END();
239
240}
std::time_t timeFromISO(const std::string &iso)
Returns a std::time_t from a given ISO date string of a specific format.
Definition Time.cpp:144
int julianGregorianShiftDays(int year)
Returns the number of days between the Julian and Gregorian years.
Definition Time.cpp:60
std::time_t mkgmtime(std::tm *time, bool recalculateDoy=true)
converts the std::tm struct to a std::time_t value assuming UTC.
Definition Time.cpp:39
const double s
Salinity of sea ice. [g kg⁻¹].
Definition constants.hpp:66