libscid 0.1.0
Chess applications made easy.
Loading...
Searching...
No Matches
date.h
1
2//
3// FILE: date.h
4// Date format and inline date functions.
5//
6// Part of: Scid (Shane's Chess Information Database)
7// Version: 1.9
8//
9// Notice: Copyright (c) 1999 Shane Hudson. All rights reserved.
10//
11// Author: Shane Hudson (sgh@users.sourceforge.net)
12//
14
15
16#ifndef SCID_DATE_H
17#define SCID_DATE_H
18
19#include <algorithm>
20#include <cassert>
21#include <cstddef>
22#include <cstdlib>
23#include <stdint.h>
24
25// DATE STORAGE FORMAT:
26// In memory, dates are stored in a 32-bit (4-byte) uint, of which only
27// the lowest 3 bytes need be used, with the lowest 5 bits for the
28// day, the next highest 4 bits for the month and the highest bits for
29// the year. This makes date comparisons easy: a bigger date value is
30// a more recent date. If a field is unknown, its value is set to zero.
31// On disk, the date is stored in 3 bytes.
32
33
34//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35// CONSTANTS and MACROS:
36
37namespace scid::core {
38
39typedef uint32_t dateT;
40
41const dateT ZERO_DATE = 0;
42
43const uint32_t YEAR_SHIFT = 9;
44const uint32_t MONTH_SHIFT = 5;
45const uint32_t DAY_SHIFT = 0;
46
47// DAY (31 days) 5 bits (32) , MONTH (12 months) 4 bits (16)
48
49const uint32_t YEAR_MAX = 2047; // 2^11 - 1
50
51#define DATE_MAKE(y,m,d) (((y) << scid::core::YEAR_SHIFT) | ((m) << scid::core::MONTH_SHIFT) | (d))
52
53
54//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
55// date_GetYear():
56// Get the year from a date.
57inline uint32_t
58date_GetYear (dateT date)
59{
60 return (uint32_t) (date >> YEAR_SHIFT);
61}
62
63//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
64// date_GetMonth():
65// Get the month from a date.
66inline uint32_t
67date_GetMonth (dateT date)
68{
69 return (uint32_t) ((date >> MONTH_SHIFT) & 15);
70}
71
72//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
73//### date_GetDay():
74// Get the day of the month from a date.
75inline uint32_t
76date_GetDay (dateT date)
77{
78 return (uint32_t) (date & 31);
79}
80
81// Return true if the date does not include the year, the month or the day.
82inline bool date_isPartial(dateT date) {
83 return date_GetYear(date) == 0 || date_GetMonth(date) == 0 ||
84 date_GetDay(date) == 0;
85}
86
87//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
88// date_DecodeToString(): convert date to PGN tag string.
89inline void
90date_DecodeToString (dateT date, char * str)
91{
92 assert(str != NULL);
93 uint32_t year, month, day;
94
95 year = date_GetYear (date);
96 month = date_GetMonth (date);
97 day = date_GetDay (date);
98
99 if (year == 0) {
100 *str++ = '?'; *str++ = '?'; *str++ = '?'; *str++ = '?';
101 } else {
102 *str++ = '0' + (year / 1000);
103 *str++ = '0' + (year % 1000) / 100;
104 *str++ = '0' + (year % 100) / 10;
105 *str++ = '0' + (year % 10);
106 }
107 *str++ = '.';
108
109 if (month == 0) {
110 *str++ = '?'; *str++ = '?';
111 } else {
112 *str++ = '0' + (month / 10);
113 *str++ = '0' + (month % 10);
114 }
115 *str++ = '.';
116
117 if (day == 0) {
118 *str++ = '?'; *str++ = '?';
119 } else {
120 *str++ = '0' + (day / 10);
121 *str++ = '0' + (day % 10);
122 }
123 *str = 0;
124}
125
126//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
127// date_EncodeFromString(): convert PGN tag string to date.
128// The date string format is: "yyyy.mm.dd".
129inline dateT
130date_EncodeFromString (const char * str)
131{
132 // Do checks on str's validity as a date string:
133 assert(str != NULL);
134
135 dateT date;
136 uint32_t year, month, day;
137
138 // convert year:
139 year = std::strtoul(str, NULL, 10);
140 if (year > YEAR_MAX) { year = 0; }
141 date = year << YEAR_SHIFT;
142 while (*str != 0 && *str != '.') { str++; }
143 if (*str == '.') { str++; }
144
145 // convert month:
146 month = std::strtoul(str, NULL, 10);
147 if (month > 12) { return date; }
148 date |= (month << MONTH_SHIFT);
149 while (*str != 0 && *str != '.') { str++; }
150 if (*str == '.') { str++; }
151
152 // convert day:
153 day = std::strtoul(str, NULL, 10);
154 if (day > 31) { return date; }
155 date |= (day << DAY_SHIFT);
156
157 return date;
158}
159
169inline dateT date_parsePGNTag(const char* str, size_t len) {
170 auto is_digit = [](auto v) { return v >= 0 && v <= 9; };
171
172 if (len < 4 || len > 10)
173 return {};
174
175 int tmp[10];
176 std::transform(str, str + len, tmp, [](unsigned char ch) {
177 return ch - static_cast<unsigned char>('0');
178 });
179 std::fill(tmp + len, tmp + 10, -1);
180
181 uint32_t year = tmp[0] * 1000 + tmp[1] * 100 + tmp[2] * 10 + tmp[3];
182 if (!std::all_of(tmp, tmp + 4, is_digit) || year > YEAR_MAX)
183 return {};
184
185 uint32_t month = 0;
186 if (!is_digit(tmp[4]) && is_digit(tmp[5])) {
187 if (!is_digit(tmp[6])) {
188 // Accept the format YYYY.M.DD or YYYY.M.D
189 std::rotate(tmp + 5, tmp + 9, tmp + 10);
190 tmp[5] = 0;
191 }
192 month = tmp[5] * 10 + tmp[6];
193 if (month > 12)
194 month = 0;
195 }
196
197 uint32_t day = 0;
198 if (!is_digit(tmp[7]) && is_digit(tmp[8])) {
199 day = is_digit(tmp[9]) ? tmp[8] * 10 + tmp[9] : tmp[8];
200 if (day > 31)
201 day = 0;
202 }
203
204 return (year << YEAR_SHIFT) | (month << MONTH_SHIFT) | (day << DAY_SHIFT);
205}
206
207inline dateT date_parsePGNTag(std::pair<const char*, const char*> str) {
208 return date_parsePGNTag(str.first, std::distance(str.first, str.second));
209}
210
211
212} // namespace scid::core
213#endif // #ifndef SCID_DATE_H
214
216// EOF: date.h