date.c (6853B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _DEFAULT_SOURCE // tm_gmtoff/tm_zone
- #define _POSIX_C_SOURCE 200809L
- #define _XOPEN_SOURCE 700 // strptime is in XSI
- #include "../lib/iso_parse.h" /* iso_parse */
- #include <assert.h>
- #include <errno.h>
- #include <locale.h> /* setlocale() */
- #include <stdbool.h>
- #include <stdio.h> /* BUFSIZ, fprintf(), puts() */
- #include <stdlib.h> /* exit(), strtol() */
- #include <string.h> /* strerror */
- #include <time.h> /* time, localtime, tm, strftime, strptime, clock_settime */
- #include <unistd.h> /* getopt(), optarg, optind */
- const char *argv0 = "date";
- static void
- usage(void)
- {
- fprintf(stderr, "\
- Usage:\n\
- date [-jRu] [-d datetime | -r epoch] [+format]\n\
- date [-jRu] mmddHHMM[[CC]yy] [+format]\n\
- date [-jRu] -f now_format now [+format]\n\
- ");
- }
- int
- main(int argc, char *argv[])
- {
- char outstr[BUFSIZ];
- struct tm tm = {
- .tm_year = 0,
- .tm_mon = 0,
- .tm_mday = 0,
- .tm_hour = 0,
- .tm_min = 0,
- .tm_sec = 0,
- .tm_isdst = -1, // unknown if DST is in effect
- .tm_gmtoff = 0,
- .tm_zone = NULL,
- };
- struct timespec tp = {
- .tv_sec = 0,
- .tv_nsec = 0,
- };
- const char *format = "%c";
- const char *input_format = NULL;
- int uflag = 0;
- int dflag = 0, rflag = 0;
- int c;
- bool jflag = false;
- bool settime = false;
- errno = 0;
- setlocale(LC_ALL, "");
- if(errno != 0)
- {
- fprintf(stderr, "%s: warning: Failed to initialize locales: %s\n", argv0, strerror(errno));
- errno = 0;
- }
- tp.tv_sec = time(NULL);
- if(tp.tv_sec == (time_t)-1)
- {
- fprintf(stderr, "%s: error: Failed getting current time: %s\n", argv0, strerror(errno));
- exit(EXIT_FAILURE);
- }
- while((c = getopt(argc, argv, ":d:f:jr:Ru")) != -1)
- {
- const char *errstr = NULL;
- switch(c)
- {
- case 'd': /* Custom datetime */
- if(input_format != NULL)
- {
- fprintf(stderr, "%s: error: Cannot both use '-d' and '-f'\n", argv0);
- exit(EXIT_FAILURE);
- }
- if(rflag == 1)
- {
- fprintf(stderr, "%s: error: Cannot both use '-d' and '-r'\n", argv0);
- exit(EXIT_FAILURE);
- }
- iso_parse(optarg, &tm, &tp.tv_nsec, &errstr);
- dflag = 1;
- if(errstr != NULL)
- {
- fprintf(stderr, "%s: error: iso_parse(\"%s\", …): %s\n", argv0, optarg, errstr);
- exit(EXIT_FAILURE);
- }
- assert(errno == 0);
- tp.tv_sec = mktime_tz(&tm);
- if(tp.tv_sec == (time_t)-1)
- {
- fprintf(stderr, "%s: error: mktime: %s\n", argv0, strerror(errno));
- exit(EXIT_FAILURE);
- }
- errno = 0;
- break;
- case 'f': /* input datetime format */
- if(dflag == 1)
- {
- fprintf(stderr, "%s: error: Cannot both use '-d' and '-f'\n", argv0);
- exit(EXIT_FAILURE);
- }
- if(rflag == 1)
- {
- fprintf(stderr, "%s: error: Cannot both use '-f' and '-r'\n", argv0);
- exit(EXIT_FAILURE);
- }
- input_format = optarg;
- settime = true;
- break;
- case 'r': /* seconds relative to epoch */
- {
- if(input_format != NULL)
- {
- fprintf(stderr, "%s: error: Cannot both use '-f' and '-r'\n", argv0);
- exit(EXIT_FAILURE);
- }
- if(dflag == 1)
- {
- fprintf(stderr, "%s: error: Cannot both use '-d' and '-r'\n", argv0);
- exit(EXIT_FAILURE);
- }
- char *endptr = NULL;
- errno = 0;
- tp.tv_sec = strtol(optarg, &endptr, 10);
- if(errno != 0)
- {
- fprintf(stderr, "%s: error: Failed parsing '-r %s'\n", argv0, optarg);
- exit(EXIT_FAILURE);
- }
- if(!(endptr == NULL || *endptr == '\0'))
- {
- fprintf(stderr, "%s: error: Invalid characters in '-r %s': %s\n", argv0, optarg, endptr);
- exit(EXIT_FAILURE);
- }
- break;
- }
- case 'R': /* Email (RFC 5322) format */
- format = "%a, %d %b %Y %H:%M:%S %z";
- break;
- case 'u': /* UTC timezone */
- uflag++;
- setenv("TZ", "UTC", 1);
- tzset();
- break;
- case 'j':
- jflag = true;
- break;
- case ':':
- fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
- usage();
- return 1;
- case '?':
- fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
- usage();
- return 1;
- }
- }
- argc -= optind;
- argv += optind;
- if(uflag)
- {
- if(gmtime_r(&tp.tv_sec, &tm) == NULL)
- {
- fprintf(stderr, "%s: error: gmtime_r: %s\n", argv0, strerror(errno));
- exit(EXIT_FAILURE);
- }
- }
- else
- {
- if(localtime_r(&tp.tv_sec, &tm) == NULL)
- {
- fprintf(stderr, "%s: error: localtime_r: %s\n", argv0, strerror(errno));
- exit(EXIT_FAILURE);
- }
- }
- if(argc > 0 && input_format != NULL)
- {
- char *res = strptime(argv[0], input_format, &tm);
- if(res == NULL)
- {
- fprintf(stderr,
- "%s: error: strptime(\"%s\", \"%s\", …) as passed by '-f' option failed\n",
- argv0,
- argv[0],
- input_format);
- exit(EXIT_FAILURE);
- }
- assert(errno == 0);
- tp.tv_sec = mktime_tz(&tm);
- tp.tv_nsec = 0;
- if(tp.tv_sec == (time_t)-1)
- {
- fprintf(stderr, "%s: error: mktime: %s\n", argv0, strerror(errno));
- exit(EXIT_FAILURE);
- }
- errno = 0;
- argv++;
- argc--;
- }
- if(input_format == NULL && argc > 0 && *argv && **argv != '+')
- {
- const char *fmt = "%m%d%H%M";
- char *s = strptime(argv[0], fmt, &tm);
- if(s == NULL)
- {
- fprintf(stderr, "%s: error: strptime(\"%s\", \"%s\", …) returned NULL\n", argv0, *argv, fmt);
- exit(EXIT_FAILURE);
- }
- size_t rem = strlen(s);
- if(rem > 0)
- {
- switch(rem)
- {
- case 2:
- fmt = "%y";
- break;
- case 4:
- fmt = "%Y";
- break;
- default:
- fprintf(stderr,
- "%s: error: Got %zu trailing characters after \"%s\" for mmddHHMM\n",
- argv0,
- rem,
- fmt);
- exit(EXIT_FAILURE);
- }
- s = strptime(s, fmt, &tm);
- if(s == NULL)
- {
- fprintf(
- stderr, "%s: error: strptime(\"%s\", \"%s\", …) returned NULL\n", argv0, *argv, fmt);
- exit(EXIT_FAILURE);
- }
- }
- assert(errno == 0);
- tp.tv_sec = mktime(&tm);
- tp.tv_nsec = 0;
- if(tp.tv_sec == (time_t)-1)
- {
- fprintf(stderr, "%s: error: mktime: %s\n", argv0, strerror(errno));
- exit(EXIT_FAILURE);
- }
- errno = 0;
- argv++;
- argc--;
- settime = true;
- }
- if(settime && !jflag)
- {
- if(clock_settime(CLOCK_REALTIME, &tp) != 0)
- {
- fprintf(stderr,
- "%s: error: clock_settime(CLOCK_REALTIME, {%ld, %ld}): %s\n",
- argv0,
- tp.tv_sec,
- tp.tv_nsec,
- strerror(errno));
- exit(EXIT_FAILURE);
- }
- }
- if(argc > 0 && *argv && **argv == '+')
- {
- format = *argv + 1;
- argv++;
- argc--;
- }
- errno = 0;
- if(strftime(outstr, sizeof(outstr), format, &tm) == 0 && errno != 0)
- {
- fprintf(stderr, "%s: error: Failed formatting time: %s\n", argv0, strerror(errno));
- exit(EXIT_FAILURE);
- }
- if(puts(outstr) < 0)
- {
- fprintf(stderr, "%s: error: Failed writing time: %s\n", argv0, strerror(errno));
- exit(EXIT_FAILURE);
- }
- return 0;
- }