-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinstant.c
252 lines (212 loc) · 7.81 KB
/
instant.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/* Copyright 2019 Bård Bjerke Johannessen <[email protected]>
This file is part of the Chronotis date/time C library.
The Chronotis date/time C library is free software: you can
redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version.
The Chronotis date/time C library is distributed in the hope that it
will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with the Chronotis date/time C library. If not, see
<https://www.gnu.org/licenses/>. */
#include "instant.h"
const char *instant_default_format = INSTANT_DEFAULT_FORMAT;
static const uint8_t month_info[] = {
0, /* Faster and smaller then subtracting 1 when indexing */
31 + (0 << 5), /* January: 31 regular days, day-of-week offset is 0 */
28 + (3 << 5), /* February: 28 regular days, day-of-week offset is 3 */
31 + (2 << 5), /* March: 31 regular days, day-of-week offset is 2 */
30 + (5 << 5), /* April: 30 regular days, day-of-week offset is 5 */
31 + (0 << 5), /* May: 31 regular days, day-of-week offset is 0 */
30 + (3 << 5), /* June: 30 regular days, day-of-week offset is 3 */
31 + (5 << 5), /* July: 31 regular days, day-of-week offset is 5 */
31 + (1 << 5), /* August: 31 regular days, day-of-week offset is 1 */
30 + (4 << 5), /* September: 30 regular days, day-of-week offset is 4 */
31 + (6 << 5), /* October: 31 regular days, day-of-week offset is 6 */
30 + (2 << 5), /* November: 30 regular days, day-of-week offset is 2 */
31 + (4 << 5) /* December: 30 regular days, day-of-week offset is 4 */
};
instant_t *instant_of(const char *string) {
static instant_t instant;
return instant_of_r(string, &instant);
}
instant_t *instant_of_r(const char *string, instant_t *instant) {
instant_parse(instant, instant_default_format, string);
return instant;
}
char *instant_str(const instant_t *instant) {
static char buffer[INSTANT_DEFAULT_LENGTH + 1];
return instant_str_r(instant, buffer);
}
char *instant_str_r(const instant_t *instant, char *buffer) {
instant_format(instant, instant_default_format, buffer);
return buffer;
}
void instant_clear(instant_t *instant) {
instant->year = instant->month = instant->day = instant->hour = instant->minute = instant->second = 0;
}
uint8_t instant_validate(const instant_t *instant) {
uint8_t result = 0;
if (!instant->year)
result += INSTANT_INVALID_YEAR;
if (instant->month < 1 || instant->month > 12)
result += INSTANT_INVALID_MONTH;
if (instant->day < 1 || instant->day > instant_days_of_month(instant))
result += INSTANT_INVALID_DAY;
if (instant->hour > 23)
result += INSTANT_INVALID_HOUR;
if (instant->minute > 59)
result += INSTANT_INVALID_MINUTE;
if (instant->second > 59)
result += INSTANT_INVALID_SECOND;
return result;
}
static uint8_t parse(const char *buffer, uint8_t *value, uint8_t min, uint8_t max) {
if ((buffer[0] <= '9' && buffer[0] >= '0') && (buffer[1] <= '9' && buffer[1] >= '0')) {
uint16_t parsed = (buffer[0] - '0') * 10 + (buffer[1] - '0');
if (parsed >= min && parsed <= max) {
*value = parsed;
return 1;
}
}
return 0;
}
void instant_parse(instant_t *instant, const char *format, const char *string) {
instant_clear(instant);
uint8_t c = 20, d;
while (*format) {
if (*format != '%') {
if (!((*string++) == *format)) goto invalid;
} else {
switch (*++format) {
case 'Y': /* 4 digit year, zero padded */
if (!parse(string, &c, 0, 99)) goto invalid;
string += 2;
/* fall through */
case 'y': /* 2 digit year, zero padded */
if (!parse(string, &d, 0, 99)) goto invalid;
instant->year = c * 100 + d;
break;
case 'm': /* 2 digit month, zero padded */
if (!parse(string, &(instant->month), 1, 12)) goto invalid;
break;
case 'd': /* 2 digit day of month, zero padded */
if (!parse(string, &(instant->day), 1, 31)) goto invalid;
break;
case 'H': /* 2 digit hour (00-23) */
if (!parse(string, &(instant->hour), 0, 23)) goto invalid;
break;
case 'I': /* 2 digit hour (01-12) */
if (!parse(string, &(instant->hour), 1, 12)) goto invalid;
break;
case 'M': /* 2 digit minute (00-59) */
if (!parse(string, &(instant->minute), 0, 59)) goto invalid;
break;
case 'S': /* 2 digit second (00-59) */
if (!parse(string, &(instant->second), 0, 59)) goto invalid;
break;
case 'p': /* A/P */
case 'P': /* a/p */
if (('a' == *string && 'P' == *format) || ('A' == *string && 'p' == *format)) {
if (12 == instant->hour)
instant->hour = 0;
} else if (('p' == *string && 'P' == *format) || ('P' == *string && 'p' == *format)) {
if (instant->hour < 12)
instant->hour += 12;
} else {
goto invalid;
}
string--;
}
string += 2;
}
format++;
}
if (!instant_validate(instant))
return;
invalid:
instant_clear(instant);
}
static void pack(char *buf, const uint8_t val) {
buf[0] = '0' + val / 10 % 10;
buf[1] = '0' + val % 10;
}
void instant_format(const instant_t *instant, const char *format, char *buffer) {
while (*format) {
if (*format == '%') {
switch (*++format) {
case 'Y': /* 4 digit year, zero padded */
pack(buffer, instant->year / 100);
buffer += 2;
/* fall through */
case 'y': /* 2 digit year, zero padded */
pack(buffer, instant->year % 100);
break;
case 'm': /* 2 digit month, zero padded */
pack(buffer, instant->month);
break;
case 'd': /* 2 digit day of month, zero padded */
pack(buffer, instant->day);
break;
case 'H': /* 2 digit hour (00-23) */
pack(buffer, instant->hour);
break;
case 'I': /* 2 digit hour (01-12) */
pack(buffer, (instant->hour % 12) ? instant->hour % 12 : 12);
break;
case 'M': /* 2 digit minute (00-59) */
pack(buffer, instant->minute);
break;
case 'S': /* 2 digit second (00-59) */
pack(buffer, instant->second);
break;
case 'p': /* A/P */
*buffer-- = (instant_is_am(instant) ? 'A' : 'P');
break;
case 'P': /* a/p */
*buffer-- = (instant_is_am(instant) ? 'a' : 'p');
break;
}
buffer += 2;
} else {
*buffer++ = *format;
}
format++;
}
*buffer = 0;
}
int16_t instant_compare(const instant_t *instant1, const instant_t *instant2) {
/* Using the subtraction tricks on 16 bit unsigned values could
* result is a value too small (or large) for a signed 16 bit return
* type. So we chech years for both smaller and greater! */
if (instant1->year > instant2->year)
return 1;
if (instant1->year < instant2->year)
return -1;
if (instant1->month - instant2->month)
return instant1->month - instant2->month;
if (instant1->day - instant2->day)
return instant1->day - instant2->day;
if (instant1->hour - instant2->hour)
return instant1->hour - instant2->hour;
if (instant1->minute - instant2->minute)
return instant1->minute - instant2->minute;
if (instant1->second - instant2->second)
return instant1->second - instant2->second;
return 0;
}
uint8_t instant_day_of_week(const instant_t *instant) {
if (instant->month < 1 || instant->month > 12)
return 0;
uint16_t year = instant->year - (instant->month < 3);
return (((year + year/4 - year/100 + year/400 + (month_info[instant->month] >> 5) + instant->day) - 1) % 7) + 1;
}
uint8_t instant_is_leap_year(const instant_t *instant) {
return (!(instant->year & 3) && instant->year % 100) || !(instant->year % 400);
}
uint8_t instant_days_of_month(const instant_t *instant) {
if (instant->month < 1 || instant->month > 12) return 0;
return ((month_info[instant->month] & 0x1f) + (instant->month == 2 && instant_is_leap_year(instant)));
}