iso_parse.c (1688B)
- // Collection of Unix tools, comparable to coreutils
- // SPDX-FileCopyrightText: 2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _DEFAULT_SOURCE // tm_gmtoff/tm_zone
- #define _XOPEN_SOURCE 700 // strptime (NetBSD)
- #define _POSIX_C_SOURCE 200809L // st_atim/st_mtim
- #include <ctype.h> /* isdigit */
- #include <errno.h> /* errno */
- #include <stdio.h> /* perror, sscanf */
- #include <stdlib.h> /* exit */
- #include <string.h> /* memset */
- #include <time.h> /* strptime, tm */
- // Calls exit() on failure
- struct timespec
- iso_parse(char *arg)
- {
- // YYYY-MM-DD[T ]hh:mm:SS([,\.]frac)?Z?
- // Dammit Unixes why no nanoseconds in `struct tm` nor `strptime`
- struct timespec time = {.tv_sec = 0, .tv_nsec = 0};
- // For Alpine's abuild compatibility
- if(arg[0] == '@')
- {
- arg++;
- errno = 0;
- time.tv_sec = strtol(arg, NULL, 10);
- if(errno != 0)
- {
- perror("strtol");
- exit(EXIT_FAILURE);
- }
- return time;
- }
- struct tm tm;
- memset(&tm, 0, sizeof(tm));
- // No %F in POSIX
- char *s = strptime(arg, "%Y-%m-%d", &tm);
- if(s[0] != 'T' && s[0] != ' ') exit(EXIT_FAILURE);
- s++;
- s = strptime(s, "%H:%M:%S", &tm);
- if(s[0] == ',' || s[0] == '.')
- {
- double fraction = 0.0;
- int parsed = 0;
- if(s[0] == ',') s[0] = '.';
- if(sscanf(s, "%10lf%n", &fraction, &parsed) < 1) exit(EXIT_FAILURE);
- time.tv_nsec = fraction * 1000000000;
- s += parsed;
- // too many digits
- if(isdigit(s[0])) exit(EXIT_FAILURE);
- }
- if(s[0] == 'Z')
- {
- tm.tm_gmtoff = 0;
- tm.tm_zone = "UTC";
- }
- time.tv_sec = mktime(&tm);
- if(time.tv_sec == (time_t)-1)
- {
- perror("mktime");
- exit(EXIT_FAILURE);
- }
- return time;
- }