Time Shield Library
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_ISO_WEEK_CONVERSIONS_HPP_INCLUDED
4#define _TIME_SHIELD_ISO_WEEK_CONVERSIONS_HPP_INCLUDED
5
11
12#include "config.hpp"
13#include "constants.hpp"
14#include "date_struct.hpp"
15#include "date_time_struct.hpp"
16#include "iso_week_struct.hpp"
17#include "time_conversions.hpp"
19
20#include <cctype>
21#include <cstdint>
22#include <cstdio>
23#include <inttypes.h>
24#include <stdexcept>
25#include <string>
26
27namespace time_shield {
28
31
35 TIME_SHIELD_CONSTEXPR inline int iso_weekday_from_weekday(Weekday weekday) noexcept {
36 return static_cast<int>((static_cast<int>(weekday) + DAYS_PER_WEEK - 1) % DAYS_PER_WEEK) + 1;
37 }
38
44 template<class Y = year_t, class M = Month, class D = int>
45 TIME_SHIELD_CONSTEXPR inline int iso_weekday_of_date(Y year, M month, D day) {
47 }
48
54 template<class Y = year_t, class M = Month, class D = int>
55 inline IsoWeekDateStruct to_iso_week_date(Y year, M month, D day) {
56 const int iso_weekday = iso_weekday_of_date(year, month, day);
57 const uday_t unix_day = date_to_unix_day(year, month, day);
58 const uday_t thursday_day = unix_day + static_cast<uday_t>(4 - iso_weekday);
59
60 const DateTimeStruct thursday_date = to_date_time<DateTimeStruct>(unix_day_to_ts(thursday_day));
61 const year_t iso_year = thursday_date.year;
62
63 const uday_t jan4_day = date_to_unix_day(iso_year, 1, 4);
64 const int jan4_iso_weekday = iso_weekday_of_date(iso_year, 1, 4);
65 const uday_t first_thursday = jan4_day + static_cast<uday_t>(4 - jan4_iso_weekday);
66
67 const int32_t week = static_cast<int32_t>((thursday_day - first_thursday) / DAYS_PER_WEEK + 1);
68 return create_iso_week_date_struct(iso_year, week, static_cast<int32_t>(iso_weekday));
69 }
70
75 return to_iso_week_date(date.year, date.mon, date.day);
76 }
77
82 template<class T = ts_t>
85 return to_iso_week_date(date_time.year, date_time.mon, date_time.day);
86 }
87
91 inline int iso_weeks_in_year(year_t iso_year) {
92 const IsoWeekDateStruct info = to_iso_week_date(iso_year, 12, 28);
93 return static_cast<int>(info.week);
94 }
95
101 inline bool is_valid_iso_week_date(year_t iso_year, int week, int weekday) {
102 if (iso_year < MIN_YEAR) return false;
103 if (iso_year > MAX_YEAR) return false;
104 if (weekday < 1 || weekday > 7) return false;
105 if (week < 1) return false;
106 const int max_week = iso_weeks_in_year(iso_year);
107 return week <= max_week;
108 }
109
115 if (!is_valid_iso_week_date(iso_date.year, iso_date.week, iso_date.weekday)) {
116 throw std::invalid_argument("Invalid ISO week date");
117 }
118
119 const uday_t jan4_day = date_to_unix_day(iso_date.year, 1, 4);
120 const int jan4_iso_weekday = iso_weekday_of_date(iso_date.year, 1, 4);
121 const uday_t first_thursday = jan4_day + static_cast<uday_t>(4 - jan4_iso_weekday);
122 const uday_t target_thursday = first_thursday + static_cast<uday_t>(iso_date.week - 1) * DAYS_PER_WEEK;
123 const uday_t target_day = target_thursday + static_cast<uday_t>(iso_date.weekday - 4);
124
125 const DateTimeStruct date_time = to_date_time<DateTimeStruct>(unix_day_to_ts(target_day));
126 return create_date_struct(date_time.year, date_time.mon, date_time.day);
127 }
128
134 inline std::string format_iso_week_date(const IsoWeekDateStruct& iso_date, bool extended = true, bool include_weekday = true) {
135 if (!is_valid_iso_week_date(iso_date.year, iso_date.week, iso_date.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
157 inline bool parse_iso_week_date(const char* input, std::size_t length, IsoWeekDateStruct& iso_date) noexcept {
158 if (input == nullptr) {
159 return false;
160 }
161
162 iso_date = create_iso_week_date_struct(0, 0, 0);
163
164 const char* p = input;
165 const char* const end = input + length;
166
167 bool negative = false;
168 if (p < end && (*p == '+' || *p == '-')) {
169 negative = (*p == '-');
170 ++p;
171 }
172
173 const char* start_digits = p;
174 int64_t value = 0;
175 while (p < end && std::isdigit(static_cast<unsigned char>(*p)) != 0) {
176 value = value * 10 + static_cast<int64_t>(*p - '0');
177 ++p;
178 }
179
180 if (p == start_digits) return false;
181 iso_date.year = negative ? -value : value;
182
183 if (p >= end) return false;
184
185 const bool has_dash_after_year = (*p == '-');
186 if (has_dash_after_year) {
187 ++p;
188 if (p >= end) return false;
189 }
190
191 if (*p != 'W' && *p != 'w') return false;
192 ++p;
193
194 int week = 0;
195 for (int i = 0; i < 2; ++i) {
196 if (p >= end || std::isdigit(static_cast<unsigned char>(*p)) == 0) return false;
197 week = week * 10 + (*p - '0');
198 ++p;
199 }
200
201 if (week == 0) return false;
202
203 bool has_weekday = false;
204 if (p < end) {
205 if ((*p == '-' && has_dash_after_year) || (!has_dash_after_year && std::isdigit(static_cast<unsigned char>(*p)) == 0)) {
206 if (*p == '-') ++p;
207 if (p >= end) return false;
208 if (std::isdigit(static_cast<unsigned char>(*p)) == 0) return false;
209 iso_date.weekday = *p - '0';
210 ++p;
211 has_weekday = true;
212 } else if (std::isdigit(static_cast<unsigned char>(*p)) != 0) {
213 iso_date.weekday = *p - '0';
214 ++p;
215 has_weekday = true;
216 }
217 }
218
219 if (!has_weekday) {
220 iso_date.weekday = 1;
221 }
222
223 iso_date.week = week;
224
225 if (p != end) return false;
226 return is_valid_iso_week_date(iso_date.year, iso_date.week, iso_date.weekday);
227 }
228
233 inline bool parse_iso_week_date(const std::string& input, IsoWeekDateStruct& iso_date) noexcept {
234 return parse_iso_week_date(input.c_str(), input.size(), iso_date);
235 }
236
242 inline bool try_parse_iso_week_date(const char* input, std::size_t length, IsoWeekDateStruct& iso_date) noexcept {
243 return parse_iso_week_date(input, length, iso_date);
244 }
245
250 inline bool try_parse_iso_week_date(const std::string& input, IsoWeekDateStruct& iso_date) noexcept {
251 return parse_iso_week_date(input, iso_date);
252 }
253
255
256}; // namespace time_shield
257
258#endif // _TIME_SHIELD_ISO_WEEK_CONVERSIONS_HPP_INCLUDED
Configuration macros for the library.
Header file with time-related constants.
Header for date structure and related functions.
Header for date and time structure and related functions.
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.
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.
TIME_SHIELD_CONSTEXPR uday_t date_to_unix_day(Year year, Month month, Day day) noexcept
Convert a calendar date to UNIX day count.
TIME_SHIELD_CONSTEXPR ts_t ts(year_t year, int month, int day)
Alias for to_timestamp.
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 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.
constexpr T1 weekday(year_t year, int month, int day)
Alias for day_of_week_date.
TIME_SHIELD_CONSTEXPR T year(ts_t ts=time_shield::ts())
Alias for year_of function.
constexpr T unix_day_to_ts(uday_t unix_day) noexcept
Converts a UNIX day to a timestamp in seconds.
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 uday_t
Unix day count since 1970‑01‑01 (days since epoch).
Definition types.hpp:42
int64_t year_t
Year as an integer (e.g., 2024).
Definition types.hpp:41
Header for ISO week date structure.
Main namespace for the Time Shield library.
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).
Umbrella header for time conversion functions.
Conversions related to UNIX-based time units and epochs.