aboutsummaryrefslogtreecommitdiff
path: root/bin/date/date.c
diff options
context:
space:
mode:
authorConrad Meyer <cem@FreeBSD.org>2018-08-04 21:54:30 +0000
committerConrad Meyer <cem@FreeBSD.org>2018-08-04 21:54:30 +0000
commit2c77ec54190294415c52a8da0c0d9c5a957c03a3 (patch)
tree587394b4b3d6fe70a9d4b3be978dcd7e21db6bf9 /bin/date/date.c
parent3395e43a049adc37b3f3c8edcb12b9ee01af0a29 (diff)
downloadsrc-2c77ec54190294415c52a8da0c0d9c5a957c03a3.tar.gz
src-2c77ec54190294415c52a8da0c0d9c5a957c03a3.zip
date(1): Add ISO 8601 formatting option
The new flag is named '-I'. It is documented in the manual page and covered by basic unit tests.
Notes
Notes: svn path=/head/; revision=337332
Diffstat (limited to 'bin/date/date.c')
-rw-r--r--bin/date/date.c104
1 files changed, 95 insertions, 9 deletions
diff --git a/bin/date/date.c b/bin/date/date.c
index 6d0e3d986f1e..a658ae1c38a6 100644
--- a/bin/date/date.c
+++ b/bin/date/date.c
@@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <ctype.h>
#include <err.h>
#include <locale.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -68,10 +69,25 @@ __FBSDID("$FreeBSD$");
static time_t tval;
int retval;
-static void setthetime(const char *, const char *, int, int);
static void badformat(void);
+static void iso8601_usage(const char *);
+static void multipleformats(void);
+static void printdate(const char *);
+static void printisodate(struct tm *);
+static void setthetime(const char *, const char *, int, int);
static void usage(void);
+static const struct iso8601_fmt {
+ const char *refname;
+ const char *format_string;
+} iso8601_fmts[] = {
+ { "date", "%Y-%m-%d" },
+ { "hours", "T%H" },
+ { "minutes", ":%M" },
+ { "seconds", ":%S" },
+};
+static const struct iso8601_fmt *iso8601_selected;
+
static const char *rfc2822_format = "%a, %d %b %Y %T %z";
int
@@ -79,7 +95,7 @@ main(int argc, char *argv[])
{
struct timezone tz;
int ch, rflag;
- int jflag, nflag, Rflag;
+ bool Iflag, jflag, nflag, Rflag;
const char *format;
char buf[1024];
char *endptr, *fmt;
@@ -89,15 +105,16 @@ main(int argc, char *argv[])
const struct vary *badv;
struct tm *lt;
struct stat sb;
+ size_t i;
v = NULL;
fmt = NULL;
(void) setlocale(LC_TIME, "");
tz.tz_dsttime = tz.tz_minuteswest = 0;
rflag = 0;
- jflag = nflag = Rflag = 0;
+ Iflag = jflag = nflag = Rflag = 0;
set_timezone = 0;
- while ((ch = getopt(argc, argv, "d:f:jnRr:t:uv:")) != -1)
+ while ((ch = getopt(argc, argv, "d:f:I::jnRr:t:uv:")) != -1)
switch((char)ch) {
case 'd': /* daylight savings time */
tz.tz_dsttime = strtol(optarg, &endptr, 10) ? 1 : 0;
@@ -108,6 +125,22 @@ main(int argc, char *argv[])
case 'f':
fmt = optarg;
break;
+ case 'I':
+ if (Rflag)
+ multipleformats();
+ Iflag = 1;
+ if (optarg == NULL) {
+ iso8601_selected = iso8601_fmts;
+ break;
+ }
+ for (i = 0; i < nitems(iso8601_fmts); i++)
+ if (strcmp(optarg, iso8601_fmts[i].refname) == 0)
+ break;
+ if (i == nitems(iso8601_fmts))
+ iso8601_usage(optarg);
+
+ iso8601_selected = &iso8601_fmts[i];
+ break;
case 'j':
jflag = 1; /* don't set time */
break;
@@ -115,6 +148,8 @@ main(int argc, char *argv[])
nflag = 1;
break;
case 'R': /* RFC 2822 datetime format */
+ if (Iflag)
+ multipleformats();
Rflag = 1;
break;
case 'r': /* user specified seconds */
@@ -163,6 +198,8 @@ main(int argc, char *argv[])
/* allow the operands in any order */
if (*argv && **argv == '+') {
+ if (Iflag)
+ multipleformats();
format = *argv + 1;
++argv;
}
@@ -173,8 +210,11 @@ main(int argc, char *argv[])
} else if (fmt != NULL)
usage();
- if (*argv && **argv == '+')
+ if (*argv && **argv == '+') {
+ if (Iflag)
+ multipleformats();
format = *argv + 1;
+ }
lt = localtime(&tval);
if (lt == NULL)
@@ -188,6 +228,9 @@ main(int argc, char *argv[])
}
vary_destroy(v);
+ if (Iflag)
+ printisodate(lt);
+
if (format == rfc2822_format)
/*
* When using RFC 2822 datetime format, don't honor the
@@ -196,12 +239,40 @@ main(int argc, char *argv[])
setlocale(LC_TIME, "C");
(void)strftime(buf, sizeof(buf), format, lt);
+ printdate(buf);
+}
+
+static void
+printdate(const char *buf)
+{
(void)printf("%s\n", buf);
if (fflush(stdout))
err(1, "stdout");
exit(retval);
}
+static void
+printisodate(struct tm *lt)
+{
+ const struct iso8601_fmt *it;
+ char fmtbuf[32], buf[32], tzbuf[8];
+
+ fmtbuf[0] = 0;
+ for (it = iso8601_fmts; it <= iso8601_selected; it++)
+ strlcat(fmtbuf, it->format_string, sizeof(fmtbuf));
+
+ (void)strftime(buf, sizeof(buf), fmtbuf, lt);
+
+ if (iso8601_selected > iso8601_fmts) {
+ (void)strftime(tzbuf, sizeof(tzbuf), "%z", lt);
+ memmove(&tzbuf[4], &tzbuf[3], 3);
+ tzbuf[3] = ':';
+ strlcat(buf, tzbuf, sizeof(buf));
+ }
+
+ printdate(buf);
+}
+
#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
static void
@@ -327,12 +398,27 @@ badformat(void)
}
static void
+iso8601_usage(const char *badarg)
+{
+ errx(1, "invalid argument '%s' for -I", badarg);
+}
+
+static void
+multipleformats(void)
+{
+ errx(1, "multiple output formats specified");
+}
+
+static void
usage(void)
{
- (void)fprintf(stderr, "%s\n%s\n",
- "usage: date [-jnRu] [-d dst] [-r seconds] [-t west] "
- "[-v[+|-]val[ymwdHMS]] ... ",
+ (void)fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: date [-jnRu] [-d dst] [-r seconds|file] [-t west] "
+ "[-v[+|-]val[ymwdHMS]]",
+ " "
+ "[-I[date | hours | minutes | seconds]]",
" "
- "[-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format]");
+ "[-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format]"
+ );
exit(1);
}