diff options
Diffstat (limited to 'subprojects/exess/src/time.c')
-rw-r--r-- | subprojects/exess/src/time.c | 172 |
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); +} |