aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/exess/src/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/exess/src/time.c')
-rw-r--r--subprojects/exess/src/time.c172
1 files changed, 172 insertions, 0 deletions
diff --git a/subprojects/exess/src/time.c b/subprojects/exess/src/time.c
new file mode 100644
index 00000000..8c7bc12c
--- /dev/null
+++ b/subprojects/exess/src/time.c
@@ -0,0 +1,172 @@
+/*
+ Copyright 2019-2021 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "read_utils.h"
+#include "string_utils.h"
+#include "time_utils.h"
+#include "timezone.h"
+#include "write_utils.h"
+
+#include "exess/exess.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+ExessResult
+read_nanoseconds(uint32_t* const out, const char* const str)
+{
+ size_t i = 0;
+ char frac_digits[10] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', 0};
+ for (unsigned j = 0u; j < 9u && is_digit(str[i]); ++j) {
+ frac_digits[j] = str[i++];
+ }
+
+ return result(exess_read_uint(out, frac_digits).status, i);
+}
+
+ExessResult
+exess_read_time(ExessTime* const out, const char* const str)
+{
+ memset(out, 0, sizeof(*out));
+
+ // Read hour
+ size_t i = skip_whitespace(str);
+ ExessResult r = read_two_digit_number(&out->hour, 0, 24, str + i);
+ if (r.status) {
+ return result(r.status, i + r.count);
+ }
+
+ // Read hour-minute delimiter
+ i += r.count;
+ if (str[i] != ':') {
+ return result(EXESS_EXPECTED_COLON, i);
+ }
+
+ // Read minute
+ ++i;
+ r = read_two_digit_number(&out->minute, 0, 59, str + i);
+ if (r.status) {
+ return result(r.status, i + r.count);
+ }
+
+ // Read minute-second delimiter
+ i += r.count;
+ if (str[i] != ':') {
+ return result(EXESS_EXPECTED_COLON, i);
+ }
+
+ // Read second
+ ++i;
+ r = read_two_digit_number(&out->second, 0, 59, str + i);
+ i += r.count;
+ if (r.status) {
+ return result(r.status, i);
+ }
+
+ // Read nanoseconds if present
+ if (str[i] == '.') {
+ ++i;
+ r = read_nanoseconds(&out->nanosecond, str + i);
+ i += r.count;
+ }
+
+ // Read timezone if present
+ if (!is_end(str[i])) {
+ r = exess_read_timezone(&out->zone, str + i);
+ i += r.count;
+ } else {
+ out->zone.quarter_hours = EXESS_LOCAL;
+ }
+
+ return end_read(r.status, str, i);
+}
+
+size_t
+write_nanoseconds(const uint32_t nanosecond,
+ const size_t buf_size,
+ char* const buf,
+ const size_t i)
+{
+ assert(nanosecond <= 999999999);
+
+ if (nanosecond == 0) {
+ return 0;
+ }
+
+ char frac_digits[10] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', 0};
+
+ // Write digits right to left, but replace trailing zeros with null
+ uint32_t remaining = nanosecond;
+ uint32_t n_trailing = 0;
+ bool wrote_nonzero = false;
+ for (uint32_t j = 0; remaining > 0; ++j) {
+ const char digit = (char)('0' + (remaining % 10));
+ if (!wrote_nonzero && digit == '0') {
+ frac_digits[8 - j] = '\0';
+ ++n_trailing;
+ } else {
+ frac_digits[8 - j] = digit;
+ }
+
+ wrote_nonzero = wrote_nonzero || digit != '0';
+ remaining /= 10;
+ }
+
+ size_t n = write_char('.', buf_size, buf, i);
+
+ n += write_string(9 - n_trailing, frac_digits, buf_size, buf, i + n);
+
+ return n;
+}
+
+ExessResult
+write_time(const ExessTime value,
+ const size_t buf_size,
+ char* const buf,
+ const size_t offset)
+{
+ if (value.hour > 24 || value.minute > 59 || value.second > 59 ||
+ value.nanosecond > 999999999 ||
+ (value.hour == 24 &&
+ (value.minute != 0 || value.second != 0 || value.nanosecond != 0))) {
+ return result(EXESS_BAD_VALUE, 0);
+ }
+
+ size_t o = offset;
+
+ // Write integral hour, minute, and second
+ o += write_two_digit_number(value.hour, buf_size, buf, o);
+ o += write_char(':', buf_size, buf, o);
+ o += write_two_digit_number(value.minute, buf_size, buf, o);
+ o += write_char(':', buf_size, buf, o);
+ o += write_two_digit_number(value.second, buf_size, buf, o);
+ o += write_nanoseconds(value.nanosecond, buf_size, buf, o);
+
+ const ExessResult r = write_timezone(value.zone, buf_size, buf, o);
+
+ return result(r.status, o - offset + r.count);
+}
+
+ExessResult
+exess_write_time(const ExessTime value, const size_t buf_size, char* const buf)
+{
+ const ExessResult r = write_time(value, buf_size, buf, 0);
+
+ return end_write(r.status, buf_size, buf, r.count);
+}