16const std::string TimePoint::ymdFormat =
"%Y-%m-%d";
17const std::string TimePoint::doyFormat =
"%Y-%j";
18const std::string TimePoint::hmsFormat =
"T%H:%M:%SZ";
19const std::string TimePoint::ymdhmsFormat = ymdFormat + hmsFormat;
20const std::string TimePoint::doyhmsFormat = doyFormat + hmsFormat;
21std::array<bool, TimeOptions::COUNT> TimeOptions::m_opt = {
false,
false };
23static const int minuteSeconds = 60;
24static const int hourSeconds = minuteSeconds * 60;
25static const int daySeconds = hourSeconds * 24;
26static const int yearSeconds = daySeconds * 365;
27static const int tmEpochYear = 1900;
28static const int unixEpochYear = 1970;
30std::tm& tmDoy(std::tm& tm)
32 int month0th[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
33 bool isLeap = ((tm.tm_year % 4 == 0) && (tm.tm_year % 100 != 0)) || (tm.tm_year % 400 == 0);
34 int bissextile = (isLeap && tm.tm_mon >= 2) ? 1 : 0;
35 tm.tm_yday = month0th[tm.tm_mon] + tm.tm_mday + bissextile;
39std::time_t
mkgmtime(std::tm* tm,
bool recalculateDoy)
43 std::time_t sum = tm->tm_sec;
44 sum += tm->tm_min * minuteSeconds;
45 sum += tm->tm_hour * hourSeconds;
46 sum += (tm->tm_yday - 1) * daySeconds;
47 int year = tmEpochYear + tm->tm_year;
48 std::time_t unixYear = year - unixEpochYear;
49 sum += unixYear * yearSeconds;
52 int julianLeapDays = (year - 1) / 4 - (unixEpochYear - 1) / 4;
53 sum += julianLeapDays * daySeconds;
64 int centurySinceGreg = (year - 1) / 100 - 15;
65 int leaps = (3 * centurySinceGreg) / 4 + 10;
69bool isDOYFormat(
const std::string& iso)
71 const std::regex ymd(
"^\\d+-\\d+-\\d+($|T)");
72 const std::regex doy(
"^\\d+-\\d+($|T)");
74 bool isYMD = std::regex_search(iso, ymd);
75 bool isDOY = std::regex_search(iso, doy);
78 throw std::invalid_argument(
"Unrecognized date format: " + iso);
80 if (TimeOptions::useDOY() && !isDOY)
81 throw std::invalid_argument(
"Inconsistent date format: " + iso
82 +
" with useDOY = " + (TimeOptions::useDOY() ?
"true" :
"false"));
87std::vector<std::string> splitString(
const std::string& str,
char delim)
89 std::stringstream ss(str);
91 std::vector<std::string> vs;
92 while (std::getline(ss, s, delim)) {
98std::string addTimeLeadingZeros(
const std::string& in)
101 std::vector<std::string> dateTime = splitString(in,
'T');
102 std::string out = dateTime[0];
103 if (dateTime.size() > 1) {
104 std::vector<std::string> hms = splitString(dateTime[1],
':');
105 std::stringstream timeStream;
106 timeStream << std::setfill(
'0');
107 for (
size_t i = 0; i < hms.size(); ++i) {
108 timeStream << std::setw(2) << std::stoi(hms[i]);
109 if (i != hms.size() - 1)
112 out +=
"T" + timeStream.str();
117std::tm getTimTime(
const std::string& in,
bool isDOY)
125 std::string iso = addTimeLeadingZeros(in);
129 std::vector<std::string> dateTime = splitString(iso,
'T');
130 if (dateTime.size() > 1) {
131 std::stringstream timeStream(
"T" + dateTime[1]);
132 timeStream >> std::get_time(&tm, TimePoint::hmsFormat.c_str());
135 std::vector<std::string> yearDoy = splitString(dateTime[0],
'-');
136 tm.tm_year = std::stoi(yearDoy[0]) - tmEpochYear;
137 tm.tm_yday = std::stoi(yearDoy[1]);
139 std::stringstream(iso) >> std::get_time(&tm, TimePoint::ymdhmsFormat.c_str());
146 bool isDOY = isDOYFormat(iso);
147 std::tm tm = getTimTime(iso, isDOY);
158Duration durationFromISO(
const std::string& iso,
int sign = +1)
160 bool isDOY = isDOYFormat(iso);
161 std::tm tm = getTimTime(iso, isDOY);
163 size_t sum = tm.tm_sec;
164 sum += tm.tm_min * minuteSeconds;
165 sum += tm.tm_hour * hourSeconds;
167 sum += tm.tm_yday * daySeconds;
170 sum += tm.tm_mon * 30 * daySeconds;
171 sum += tm.tm_mday * daySeconds;
173 sum += (tmEpochYear + tm.tm_year) * yearSeconds;
174 Duration::Basis dura(std::chrono::seconds(sign * sum));
175 return Duration(dura);
178Duration durationFromISO(std::istream& is,
int sign = +1)
182 return durationFromISO(iso, sign);
185std::istream& Duration::parse(std::istream& is)
190 if (possibleP !=
'P') {
193 restOf = possibleP + restOf;
198 std::regex rx(R
"(^([+-]?(?:[[:d:]]+\.?|[[:d:]]*\.[[:d:]]+))(?:[Ee][+-]?[[:d:]]+)?$)");
199 bool isYMD = std::regex_search(restOf, rx);
201 throw std::invalid_argument(
202 "The duration should be an ISO 8601 duration (P…) or a number of seconds. Got: "
204 double sec = std::stod(restOf);
206 setDurationSeconds(sec);
211 bool isNegative = (is.peek() ==
'-');
217 *
this = durationFromISO(is, isNegative ? -1 : 1);
222Duration::Duration(
const std::string& str) { this->parse(str); }
224Duration::Duration(
double seconds) { setDurationSeconds(seconds); }
226void Duration::setDurationSeconds(
double secs)
228 std::chrono::duration<double> sec(secs);
229 m_d = std::chrono::duration_cast<Basis>(sec);
232TimePoint Duration::operator+(
const TimePoint& t)
const {
return t + *
this; }
234std::tm* TimePoint::gmtime()
const
236 auto tt = Clock::to_time_t(m_t);
237 return std::gmtime(&tt);
std::time_t timeFromISO(const std::string &iso)
Returns a std::time_t from a given ISO date string of a specific format.
int julianGregorianShiftDays(int year)
Returns the number of days between the Julian and Gregorian years.
std::time_t mkgmtime(std::tm *time, bool recalculateDoy=true)
converts the std::tm struct to a std::time_t value assuming UTC.
const double s
Salinity of sea ice. [g kg⁻¹].