Line data Source code
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 : //
13 : //////////////////////////////////////////////////////////////////////
14 :
15 :
16 : #ifndef SCID_DATE_H
17 : #define SCID_DATE_H
18 :
19 : #include "common.h"
20 : #include <algorithm>
21 : #include <cstdlib>
22 :
23 : // DATE STORAGE FORMAT:
24 : // In memory, dates are stored in a 32-bit (4-byte) uint, of which only
25 : // the lowest 3 bytes need be used, with the lowest 5 bits for the
26 : // day, the next highest 4 bits for the month and the highest bits for
27 : // the year. This makes date comparisons easy: a bigger date value is
28 : // a more recent date. If a field is unknown, its value is set to zero.
29 : // On disk, the date is stored in 3 bytes.
30 :
31 :
32 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33 : // CONSTANTS and MACROS:
34 :
35 : const dateT ZERO_DATE = 0;
36 :
37 : const uint YEAR_SHIFT = 9;
38 : const uint MONTH_SHIFT = 5;
39 : const uint DAY_SHIFT = 0;
40 :
41 : // DAY (31 days) 5 bits (32) , MONTH (12 months) 4 bits (16)
42 :
43 : const uint YEAR_MAX = 2047; // 2^11 - 1
44 :
45 : #define DATE_MAKE(y,m,d) (((y) << YEAR_SHIFT) | ((m) << MONTH_SHIFT) | (d))
46 :
47 :
48 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
49 : // date_GetYear():
50 : // Get the year from a date.
51 : inline uint
52 2586251 : date_GetYear (dateT date)
53 : {
54 2586251 : return (uint) (date >> YEAR_SHIFT);
55 : }
56 :
57 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
58 : // date_GetMonth():
59 : // Get the month from a date.
60 : inline uint
61 510494 : date_GetMonth (dateT date)
62 : {
63 510494 : return (uint) ((date >> MONTH_SHIFT) & 15);
64 : }
65 :
66 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67 : //### date_GetDay():
68 : // Get the day of the month from a date.
69 : inline uint
70 510494 : date_GetDay (dateT date)
71 : {
72 510494 : return (uint) (date & 31);
73 : }
74 :
75 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
76 : //### date_GetMonthDay():
77 : // Get the month and day; used to check for year-only dates. S.A
78 : inline uint
79 : date_GetMonthDay (dateT date)
80 : {
81 : return (uint) (date & 511);
82 : }
83 :
84 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
85 : // date_DecodeToString(): convert date to PGN tag string.
86 : inline void
87 2109 : date_DecodeToString (dateT date, char * str)
88 : {
89 2109 : ASSERT(str != NULL);
90 : uint year, month, day;
91 :
92 2109 : year = date_GetYear (date);
93 2109 : month = date_GetMonth (date);
94 2109 : day = date_GetDay (date);
95 :
96 2109 : if (year == 0) {
97 2011 : *str++ = '?'; *str++ = '?'; *str++ = '?'; *str++ = '?';
98 : } else {
99 98 : *str++ = '0' + (year / 1000);
100 98 : *str++ = '0' + (year % 1000) / 100;
101 98 : *str++ = '0' + (year % 100) / 10;
102 98 : *str++ = '0' + (year % 10);
103 : }
104 2109 : *str++ = '.';
105 :
106 2109 : if (month == 0) {
107 2017 : *str++ = '?'; *str++ = '?';
108 : } else {
109 92 : *str++ = '0' + (month / 10);
110 92 : *str++ = '0' + (month % 10);
111 : }
112 2109 : *str++ = '.';
113 :
114 2109 : if (day == 0) {
115 2026 : *str++ = '?'; *str++ = '?';
116 : } else {
117 83 : *str++ = '0' + (day / 10);
118 83 : *str++ = '0' + (day % 10);
119 : }
120 2109 : *str = 0;
121 2109 : }
122 :
123 : //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
124 : // date_EncodeFromString(): convert PGN tag string to date.
125 : // The date string format is: "yyyy.mm.dd".
126 : inline dateT
127 : date_EncodeFromString (const char * str)
128 : {
129 : // Do checks on str's validity as a date string:
130 : ASSERT(str != NULL);
131 :
132 : dateT date;
133 : uint year, month, day;
134 :
135 : // convert year:
136 : year = std::strtoul(str, NULL, 10);
137 : if (year > YEAR_MAX) { year = 0; }
138 : date = year << YEAR_SHIFT;
139 : while (*str != 0 && *str != '.') { str++; }
140 : if (*str == '.') { str++; }
141 :
142 : // convert month:
143 : month = std::strtoul(str, NULL, 10);
144 : if (month > 12) { return date; }
145 : date |= (month << MONTH_SHIFT);
146 : while (*str != 0 && *str != '.') { str++; }
147 : if (*str == '.') { str++; }
148 :
149 : // convert day:
150 : day = std::strtoul(str, NULL, 10);
151 : if (day > 31) { return date; }
152 : date |= (day << DAY_SHIFT);
153 :
154 : return date;
155 : }
156 :
157 : /**
158 : * Creates a dateT object from a PGN tag value string.
159 : * "The Date tag value field always uses a standard ten character format:
160 : * "YYYY.MM.DD". If the any of the digit fields are not known, then question
161 : * marks are used in place of the digits."
162 : * @param str: pointer to the memory containing the tag value.
163 : * @param len: length of the tag value.
164 : * @returns the dateT object corresponding to @e str.
165 : */
166 2116 : inline dateT date_parsePGNTag(const char* str, size_t len) {
167 2116 : char tmp[10] = {0};
168 23235 : std::transform(str, str + std::min<size_t>(len, 10), tmp, [](char ch) {
169 21119 : return (ch >= '0' && ch <= '9') ? ch - '0' : 0;
170 21119 : });
171 :
172 2116 : uint32_t year = tmp[0] * 1000 + tmp[1] * 100 + tmp[2] * 10 + tmp[3];
173 2116 : uint32_t month = tmp[5] * 10 + tmp[6];
174 2116 : uint32_t day = tmp[8] * 10 + tmp[9];
175 :
176 2116 : if (year > YEAR_MAX)
177 1 : year = 0;
178 :
179 2116 : if (month > 12)
180 3 : month = 0;
181 :
182 2116 : constexpr unsigned char days[] = {31, 31, 28, 31, 30, 31, 30,
183 : 31, 31, 30, 31, 30, 31};
184 2116 : if (day > days[month]) {
185 12 : if (day != 29 || year % 4 || (year % 100 == 0 && year % 400)) {
186 9 : day = 0;
187 : }
188 : }
189 :
190 2116 : return (year << YEAR_SHIFT) | (month << MONTH_SHIFT) | (day << DAY_SHIFT);
191 : }
192 :
193 2075 : inline dateT date_parsePGNTag(std::pair<const char*, const char*> str) {
194 2075 : return date_parsePGNTag(str.first, std::distance(str.first, str.second));
195 : }
196 :
197 : #endif // #ifndef SCID_DATE_H
198 :
199 : //////////////////////////////////////////////////////////////////////
200 : // EOF: date.h
201 : //////////////////////////////////////////////////////////////////////
202 :
|