Time Shield Library 2.0.0
C++ library for working with time
Loading...
Searching...
No Matches
iso_week_conversions.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2#pragma once
3#ifndef TIME_SHIELD_HEADER_CONVERSIONS_ISO_WEEK_CONVERSIONS_HPP_INCLUDED
4#define TIME_SHIELD_HEADER_CONVERSIONS_ISO_WEEK_CONVERSIONS_HPP_INCLUDED
5
11
12#include <time_shield/core.hpp>
13#include "date_conversions.hpp"
16
17#include <cctype>
18#include <cstdint>
19#include <cstdio>
20#include <inttypes.h>
21#include <stdexcept>
22#include <string>
23
24namespace time_shield {
25
28
32 TIME_SHIELD_CONSTEXPR inline int iso_weekday_from_weekday(Weekday weekday) noexcept {
33 return static_cast<int>((static_cast<int>(weekday) + DAYS_PER_WEEK - 1) % DAYS_PER_WEEK) + 1;
34 }
35
41 template<class Y = year_t, class M = Month, class D = int>
42 TIME_SHIELD_CONSTEXPR inline int iso_weekday_of_date(Y year, M month, D day) {
44 }
45
51 template<class Y = year_t, class M = Month, class D = int>
52 inline IsoWeekDateStruct to_iso_week_date(Y year, M month, D day) {
53 const int iso_weekday = iso_weekday_of_date(year, month, day);
54 const dse_t unix_day = date_to_unix_day(year, month, day);
55 const dse_t thursday_day = unix_day + static_cast<dse_t>(4 - iso_weekday);
56
57 const DateTimeStruct thursday_date = to_date_time<DateTimeStruct>(unix_day_to_ts(thursday_day));
58 const year_t iso_year = thursday_date.year;
59
60 const dse_t jan4_day = date_to_unix_day(iso_year, 1, 4);
61 const int jan4_iso_weekday = iso_weekday_of_date(iso_year, 1, 4);
62 const dse_t first_thursday = jan4_day + static_cast<dse_t>(4 - jan4_iso_weekday);
63
64 const int32_t week = static_cast<int32_t>((thursday_day - first_thursday) / DAYS_PER_WEEK + 1);
65 return create_iso_week_date_struct(iso_year, week, static_cast<int32_t>(iso_weekday));
66 }
67
72 return to_iso_week_date(date.year, date.mon, date.day);
73 }
74
79 template<class T = ts_t>
82 return to_iso_week_date(date_time.year, date_time.mon, date_time.day);
83 }
84
88 inline int iso_weeks_in_year(year_t iso_year) {
89 const IsoWeekDateStruct info = to_iso_week_date(iso_year, 12, 28);
90 return static_cast<int>(info.week);
91 }
92
98 inline bool is_valid_iso_week_date(year_t iso_year, int week, int weekday) {
99 if (iso_year < MIN_YEAR) return false;
100 if (iso_year > MAX_YEAR) return false;
101 if (weekday < 1 || weekday > 7) return false;
102 if (week < 1) return false;
103 const int max_week = iso_weeks_in_year(iso_year);
104 return week <= max_week;
105 }
106
112 if (!is_valid_iso_week_date(iso_date.year, iso_date.week, iso_date.weekday)) {
113 throw std::invalid_argument("Invalid ISO week date");
114 }
115
116 const dse_t jan4_day = date_to_unix_day(iso_date.year, 1, 4);
117 const int jan4_iso_weekday = iso_weekday_of_date(iso_date.year, 1, 4);
118 const dse_t first_thursday = jan4_day + static_cast<dse_t>(4 - jan4_iso_weekday);
119 const dse_t target_thursday = first_thursday + static_cast<dse_t>(iso_date.week - 1) * DAYS_PER_WEEK;
120 const dse_t target_day = target_thursday + static_cast<dse_t>(iso_date.weekday - 4);
121
122 const DateTimeStruct date_time = to_date_time<DateTimeStruct>(unix_day_to_ts(target_day));
123 return create_date_struct(date_time.year, date_time.mon, date_time.day);
124 }
125
131 inline std::string format_iso_week_date(const IsoWeekDateStruct& iso_date, bool extended = true, bool include_weekday = true) {
132 const bool has_valid_year = iso_date.year >= MIN_YEAR && iso_date.year <= MAX_YEAR;
133 const bool has_valid_week = has_valid_year && iso_date.week >= 1 && iso_date.week <= iso_weeks_in_year(iso_date.year);
134 const bool has_valid_weekday = iso_date.weekday >= 1 && iso_date.weekday <= 7;
135 if (!has_valid_year || !has_valid_week || (include_weekday && !has_valid_weekday)) {
136 throw std::invalid_argument("Invalid ISO week date");
137 }
138
139 if (!include_weekday) {
140 const char* fmt = extended ? "%" PRId64 "-W%.2d" : "%" PRId64 "W%.2d";
141 char buffer[32] = {0};
142 std::snprintf(buffer, sizeof(buffer), fmt, iso_date.year, iso_date.week);
143 return std::string(buffer);
144 }
145
146 const char* fmt = extended ? "%" PRId64 "-W%.2d-%d" : "%" PRId64 "W%.2d%d";
147 char buffer[32] = {0};
148 std::snprintf(buffer, sizeof(buffer), fmt, iso_date.year, iso_date.week, iso_date.weekday);
149 return std::string(buffer);
150 }
151
160 inline bool parse_iso_week_date(const char* input, std::size_t length, IsoWeekDateStruct& iso_date) noexcept {
161 if (input == nullptr) {
162 return false;
163 }
164
165 iso_date = create_iso_week_date_struct(0, 0, 0);
166
167 const char* p = input;
168 const char* const end = input + length;
169
170 bool negative = false;
171 if (p < end && (*p == '+' || *p == '-')) {
172 negative = (*p == '-');
173 ++p;
174 }
175
176 const char* start_digits = p;
177 int64_t value = 0;
178 while (p < end && std::isdigit(static_cast<unsigned char>(*p)) != 0) {
179 value = value * 10 + static_cast<int64_t>(*p - '0');
180 ++p;
181 }
182
183 if (p == start_digits) return false;
184 iso_date.year = negative ? -value : value;
185
186 if (p >= end) return false;
187
188 const bool has_dash_after_year = (*p == '-');
189 if (has_dash_after_year) {
190 ++p;
191 if (p >= end) return false;
192 }
193
194 if (*p != 'W' && *p != 'w') return false;
195 ++p;
196
197 int week = 0;
198 for (int i = 0; i < 2; ++i) {
199 if (p >= end || std::isdigit(static_cast<unsigned char>(*p)) == 0) return false;
200 week = week * 10 + (*p - '0');
201 ++p;
202 }
203
204 if (week == 0) return false;
205
206 bool has_weekday = false;
207 if (p < end) {
208 if ((*p == '-' && has_dash_after_year) || (!has_dash_after_year && std::isdigit(static_cast<unsigned char>(*p)) == 0)) {
209 if (*p == '-') ++p;
210 if (p >= end) return false;
211 if (std::isdigit(static_cast<unsigned char>(*p)) == 0) return false;
212 iso_date.weekday = *p - '0';
213 ++p;
214 has_weekday = true;
215 } else if (std::isdigit(static_cast<unsigned char>(*p)) != 0) {
216 iso_date.weekday = *p - '0';
217 ++p;
218 has_weekday = true;
219 }
220 }
221
222 if (!has_weekday) {
223 iso_date.weekday = 1;
224 }
225
226 iso_date.week = week;
227
228 if (p != end) return false;
229 return is_valid_iso_week_date(iso_date.year, iso_date.week, iso_date.weekday);
230 }
231
238 inline bool parse_iso_week_date(const std::string& input, IsoWeekDateStruct& iso_date) noexcept {
239 return parse_iso_week_date(input.c_str(), input.size(), iso_date);
240 }
241
247 inline bool try_parse_iso_week_date(const char* input, std::size_t length, IsoWeekDateStruct& iso_date) noexcept {
248 return parse_iso_week_date(input, length, iso_date);
249 }
250
255 inline bool try_parse_iso_week_date(const std::string& input, IsoWeekDateStruct& iso_date) noexcept {
256 return parse_iso_week_date(input, iso_date);
257 }
258
260
261}; // namespace time_shield
262
263#endif // TIME_SHIELD_HEADER_CONVERSIONS_ISO_WEEK_CONVERSIONS_HPP_INCLUDED
Conversions related to calendar dates and DateStruct helpers.
Conversions involving DateTimeStruct and day boundary helpers.
constexpr int64_t DAYS_PER_WEEK
Days per week.
constexpr int64_t MAX_YEAR
Maximum representable year.
constexpr int64_t MIN_YEAR
Minimum representable year.
TIME_SHIELD_CONSTEXPR T unix_day(ts_t ts=time_shield::ts()) noexcept
Alias for days_since_epoch function.
std::string format_iso_week_date(const IsoWeekDateStruct &iso_date, bool extended=true, bool include_weekday=true)
Format ISO week date to string.
bool is_valid_iso_week_date(year_t iso_year, int week, int weekday)
Validate ISO week date components.
TIME_SHIELD_CONSTEXPR T1 day_of_week_date(T2 year, T3 month, T4 day)
Get the day of the week.
int iso_weeks_in_year(year_t iso_year)
Calculate number of ISO weeks in a year.
IsoWeekDateStruct to_iso_week_date(Y year, M month, D day)
Convert calendar date to ISO week date.
DateStruct iso_week_date_to_date(const IsoWeekDateStruct &iso_date)
Convert ISO week date to calendar date.
TIME_SHIELD_CONSTEXPR int iso_weekday_of_date(Y year, M month, D day)
Get ISO weekday for a calendar date.
TIME_SHIELD_CONSTEXPR T1 weekday(year_t year, int month, int day)
Alias for day_of_week_date.
TIME_SHIELD_CONSTEXPR int iso_weekday_from_weekday(Weekday weekday) noexcept
Convert Weekday enum to ISO weekday (Mon=1 .. Sun=7).
bool parse_iso_week_date(const char *input, std::size_t length, IsoWeekDateStruct &iso_date) noexcept
Parse ISO week date string buffer.
bool try_parse_iso_week_date(const char *input, std::size_t length, IsoWeekDateStruct &iso_date) noexcept
Alias for parse_iso_week_date.
TIME_SHIELD_CONSTEXPR T year(ts_t ts=time_shield::ts())
Alias for year_of function.
const DateStruct create_date_struct(int64_t year, int32_t mon=1, int32_t day=1)
Creates a DateStruct instance.
const IsoWeekDateStruct create_iso_week_date_struct(int64_t year, int32_t week=1, int32_t weekday=1)
Creates an IsoWeekDateStruct instance.
T1 to_date_time(T2 ts)
Converts a timestamp to a date-time structure.
int64_t dse_t
Unix day count since 1970‑01‑01 (days since epoch).
Definition types.hpp:41
int64_t year_t
Year as an integer (e.g., 2024).
Definition types.hpp:40
ts_t ts() noexcept
Get the current UTC timestamp in seconds.
Main namespace for the Time Shield library.
TIME_SHIELD_CONSTEXPR T unix_day_to_ts(dse_t unix_day) noexcept
Converts a UNIX day to a timestamp in seconds.
TIME_SHIELD_CONSTEXPR dse_t date_to_unix_day(Year year, Month month, Day day) noexcept
Convert a calendar date to UNIX day count.
Structure to represent a date.
int32_t mon
Month component of the date (1-12).
int32_t day
Day component of the date (1-31).
int64_t year
Year component of the date.
Structure to represent date and time.
int64_t year
Year component of the date.
int day
Day component of the date (1-31).
int mon
Month component of the date (1-12).
Structure to represent an ISO week date.
int64_t year
ISO week-numbering year component.
int32_t week
ISO week number component (1-52/53).
int32_t weekday
ISO weekday component (1=Monday .. 7=Sunday).
Helper functions for unit conversions between seconds, minutes, hours, and milliseconds.