diff options
Diffstat (limited to 'subprojects/exess/src/date.c')
-rw-r--r-- | subprojects/exess/src/date.c | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/subprojects/exess/src/date.c b/subprojects/exess/src/date.c new file mode 100644 index 00000000..77b336f7 --- /dev/null +++ b/subprojects/exess/src/date.c @@ -0,0 +1,112 @@ +/* + 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 "date_utils.h" +#include "read_utils.h" +#include "timezone.h" +#include "write_utils.h" + +#include "exess/exess.h" + +#include <stdlib.h> +#include <string.h> + +ExessResult +read_date_numbers(ExessDate* const out, const char* const str) +{ + // Read year at the beginning + size_t i = skip_whitespace(str); + ExessResult r = read_year_number(&out->year, str + i); + if (r.status) { + return result(r.status, i + r.count); + } + + // Read year-month delimiter + i += r.count; + if (str[i] != '-') { + return result(EXESS_EXPECTED_DASH, i); + } + + // Read month + ++i; + if ((r = read_two_digit_number(&out->month, 1, 12, str + i)).status) { + return result(r.status, i + r.count); + } + + // Read month-day delimiter + i += r.count; + if (str[i] != '-') { + return result(EXESS_EXPECTED_DASH, i); + } + + // Read day + ++i; + if ((r = read_two_digit_number(&out->day, 1, 31, str + i)).status) { + return result(r.status, i + r.count); + } + + // Check that day is in range + i += r.count; + if (out->day > days_in_month(out->year, out->month)) { + return result(EXESS_OUT_OF_RANGE, i); + } + + return result(EXESS_SUCCESS, i); +} + +ExessResult +exess_read_date(ExessDate* const out, const char* const str) +{ + memset(out, 0, sizeof(*out)); + + // Read YYYY-MM-DD numbers + size_t i = skip_whitespace(str); + ExessResult r = read_date_numbers(out, str + i); + + i += r.count; + if (r.status || is_end(str[i])) { + out->zone.quarter_hours = EXESS_LOCAL; + return result(r.status, i); + } + + // Read timezone + r = exess_read_timezone(&out->zone, str + i); + + return result(r.status, i + r.count); +} + +ExessResult +exess_write_date(const ExessDate value, const size_t buf_size, char* const buf) +{ + if (value.month < 1 || value.month > 12 || value.day < 1 || value.day > 31) { + return end_write(EXESS_BAD_VALUE, buf_size, buf, 0); + } + + ExessResult r = write_year_number(value.year, buf_size, buf); + size_t o = r.count; + if (r.status) { + return end_write(r.status, buf_size, buf, o); + } + + o += write_char('-', buf_size, buf, o); + o += write_two_digit_number(value.month, buf_size, buf, o); + o += write_char('-', buf_size, buf, o); + o += write_two_digit_number(value.day, buf_size, buf, o); + + r = write_timezone(value.zone, buf_size, buf, o); + + return end_write(r.status, buf_size, buf, o + r.count); +} |