Branch data Line data Source code
1 : : // Copyright 2019-2026 David Robillard <d@drobilla.net>
2 : : // SPDX-License-Identifier: ISC
3 : :
4 : : #include "timezone.h"
5 : : #include "number_utils.h"
6 : : #include "result.h"
7 : : #include "write_utils.h"
8 : :
9 : : #include <exess/exess.h>
10 : :
11 : : #include <stdint.h>
12 : : #include <stdlib.h>
13 : :
14 : : EXESS_NONBLOCKING ExessTimezone
15 : 130 : exess_timezone(const int8_t hours, const int8_t minutes)
16 : : {
17 [ + + + + ]: 130 : if (hours < -14 || hours > 14) {
18 : 2 : return EXESS_TIMEZONE_LOCAL;
19 : : }
20 : :
21 [ + + + + : 128 : if (hours < 0 && minutes != 0 && minutes != -15 && minutes != -30 &&
+ + + + +
+ ]
22 : : minutes != -45) {
23 : 2 : return EXESS_TIMEZONE_LOCAL;
24 : : }
25 : :
26 [ + + + + : 126 : if (hours > 0 && minutes != 0 && minutes != 15 && minutes != 30 &&
+ + + + +
+ ]
27 : : minutes != 45) {
28 : 2 : return EXESS_TIMEZONE_LOCAL;
29 : : }
30 : :
31 : 124 : return (int8_t)((4 * hours) + (minutes / 15));
32 : : }
33 : :
34 : : EXESS_NONBLOCKING ExessResult
35 : 6350 : read_optional_timezone(ExessTimezone* const out, const char* const str)
36 : : {
37 : 6350 : *out = EXESS_TIMEZONE_LOCAL;
38 : :
39 : : // Handle UTC special case
40 : 6350 : size_t i = 0;
41 [ + + ]: 6350 : if (str[i] == 'Z') {
42 : 155 : *out = 0;
43 : 155 : return RESULT(EXESS_SUCCESS, i + 1);
44 : : }
45 : :
46 : : // Read leading sign
47 : 6195 : int sign = 1;
48 [ + + ]: 6195 : if (str[i] == '-') {
49 : 2204 : sign = -1;
50 [ + + ]: 3991 : } else if (str[i] != '+') {
51 : 1691 : return RESULT(EXESS_SUCCESS, i);
52 : : }
53 : :
54 : 4504 : ++i;
55 : :
56 : : // Read hour digits
57 : 4504 : uint8_t hh = 0U;
58 : 4504 : ExessResult r = read_two_digit_number(&hh, 0U, 14U, str + i);
59 [ + + ]: 4504 : if (r.status) {
60 : 8 : return RESULT(r.status, i + r.count);
61 : : }
62 : :
63 : 4496 : i += 2U;
64 : :
65 : : // Check colon
66 [ + + ]: 4496 : if (str[i] != ':') {
67 : 1 : return RESULT(EXESS_EXPECTED_COLON, i);
68 : : }
69 : :
70 : 4495 : ++i;
71 : :
72 : : // Read minute digits
73 : 4495 : uint8_t mm = 0U;
74 : 4495 : r = read_two_digit_number(&mm, 0U, 59U, str + i);
75 [ + + ]: 4495 : if (r.status) {
76 : 4 : return RESULT(r.status, i + r.count);
77 : : }
78 : :
79 : 4491 : i += 2U;
80 : :
81 : : // Calculate signed hour and minute
82 : 4491 : const int8_t hour = (int8_t)(sign * hh);
83 : 4491 : const int8_t minute = (int8_t)(sign * mm);
84 [ + + ]: 4491 : if (minute % 15) {
85 : 1 : return RESULT(EXESS_UNSUPPORTED, i);
86 : : }
87 : :
88 : : // Convert to quarter hours
89 : 4490 : const int8_t quarters = (int8_t)((4 * hour) + (minute / 15));
90 [ + + + + ]: 4490 : if (quarters < -56 || quarters > 56) {
91 : 2 : return RESULT(EXESS_OUT_OF_RANGE, i);
92 : : }
93 : :
94 : 4488 : *out = quarters;
95 : 4488 : return RESULT(EXESS_SUCCESS, i);
96 : : }
97 : :
98 : : EXESS_NONBLOCKING ExessResult
99 : 6177 : write_timezone(const ExessTimezone value,
100 : : const size_t buf_size,
101 : : char* const buf,
102 : : size_t o)
103 : : {
104 [ + + ]: 6177 : if (value == EXESS_TIMEZONE_LOCAL) {
105 : 1629 : return RESULT(EXESS_SUCCESS, 0);
106 : : }
107 : :
108 [ + + + + ]: 4548 : if (value < -56 || value > 56) {
109 : 4 : return RESULT(EXESS_BAD_VALUE, 0);
110 : : }
111 : :
112 [ + + ]: 4544 : if (value == 0) {
113 : 82 : write_char('Z', buf_size, buf, o);
114 : 82 : return RESULT(EXESS_SUCCESS, 1);
115 : : }
116 : :
117 : 4462 : const uint8_t abs_quarters = (uint8_t)abs(value);
118 : 4462 : const uint8_t abs_hour = abs_quarters / 4;
119 : 4462 : const uint8_t abs_minute = (uint8_t)(15U * (abs_quarters % 4U));
120 : :
121 : 4462 : size_t n = 0;
122 [ + + ]: 4462 : n += write_char(value < 0 ? '-' : '+', buf_size, buf, o + n);
123 : 4462 : n += write_two_digit_number(abs_hour, buf_size, buf, o + n);
124 : 4462 : n += write_char(':', buf_size, buf, o + n);
125 : 4462 : n += write_two_digit_number(abs_minute, buf_size, buf, o + n);
126 : :
127 : 4462 : return RESULT(EXESS_SUCCESS, n);
128 : : }
|