git: 31edf56b1571 - main - date: add -z output_zone option

From: Baptiste Daroussin <bapt_at_FreeBSD.org>
Date: Fri, 19 May 2023 19:09:54 UTC
The branch main has been updated by bapt:

URL: https://cgit.FreeBSD.org/src/commit/?id=31edf56b157169b2f83d288ebfae7d4b15ce6dde

commit 31edf56b157169b2f83d288ebfae7d4b15ce6dde
Author:     Baptiste Daroussin <bapt@FreeBSD.org>
AuthorDate: 2023-05-19 09:08:11 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2023-05-19 19:09:40 +0000

    date: add -z output_zone option
    
    Inpired by OpenBSD date(1), this option allows to do timezone conversion
    via the date(1) command.
    
    For example, to determine when the BSDCan livestream begins for me:
    
    $ env -i TZ=EST5EDT date -z Europe/Paris -j 0900
    
    MFC After:              1 week
    Reviewed by:            kib, bcr (manpage)
    Differential Revision:  https://reviews.freebsd.org/D40159
---
 bin/date/date.1 | 22 ++++++++++++++++++++--
 bin/date/date.c | 12 +++++++++---
 2 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/bin/date/date.1 b/bin/date/date.1
index 0c92a8c71f90..b8a1ae9718c1 100644
--- a/bin/date/date.1
+++ b/bin/date/date.1
@@ -32,7 +32,7 @@
 .\"     @(#)date.1	8.3 (Berkeley) 4/28/95
 .\" $FreeBSD$
 .\"
-.Dd April 13, 2023
+.Dd May 19, 2023
 .Dt DATE 1
 .Os
 .Sh NAME
@@ -42,6 +42,7 @@
 .\" Display time.
 .Nm
 .Op Fl nRu
+.Op Fl z Ar output_zone
 .Op Fl I Ns Op Ar FMT
 .Op Fl r Ar filename
 .Op Fl r Ar seconds
@@ -56,6 +57,7 @@
 .\" Set time with the default input format.
 .Nm
 .Op Fl jnRu
+.Op Fl z Ar output_zone
 .Op Fl I Ns Op Ar FMT
 .Oo
 .Sm off
@@ -77,6 +79,7 @@
 .\" Set time with the user-provided input format.
 .Nm
 .Op Fl jnRu
+.Op Fl z Ar output_zone
 .Op Fl I Ns Op Ar FMT
 .Oo
 .Sm off
@@ -202,6 +205,14 @@ displays the time in the time zone described by
 or the
 .Ev TZ
 environment variable.
+.It Fl z Ar output_zone
+Just before printing the time, change to the specified timezone;
+see the description of
+.Ev TZ
+below.
+This can be used with
+.Fl j
+to easily convert time specifications from one zone to another.
 .It Xo
 .Fl v
 .Sm off
@@ -514,13 +525,20 @@ will display
 .Pp
 .Dl "2018-08-04T13:42:19-07:00"
 .Pp
-Finally the command:
+The command:
 .Pp
 .Dl "env LC_ALL=C date -j -f ""%a %b %d %T %Z %Y"" ""`env LC_ALL=C date`"" ""+%s"""
 .Pp
 can be used to parse the output from
 .Nm
 and express it in Epoch time.
+.Pp
+Finally the command
+.Pp
+.Dl "TZ=America/Los_Angeles date -z Europe/Paris -j 0900"
+.Pp
+will print the time in the "Europe/Paris" timezone when it is 9:00 in The
+America/Los_Angeles timezone.
 .Sh DIAGNOSTICS
 It is invalid to combine the
 .Fl I
diff --git a/bin/date/date.c b/bin/date/date.c
index 4dc5df0dccfc..c1fb496ce7ee 100644
--- a/bin/date/date.c
+++ b/bin/date/date.c
@@ -95,7 +95,7 @@ main(int argc, char *argv[])
 	bool Iflag, jflag, Rflag;
 	const char *format;
 	char buf[1024];
-	char *fmt;
+	char *fmt, *outzone = NULL;
 	char *tmp;
 	struct vary *v;
 	const struct vary *badv;
@@ -108,7 +108,7 @@ main(int argc, char *argv[])
 	(void) setlocale(LC_TIME, "");
 	rflag = 0;
 	Iflag = jflag = Rflag = 0;
-	while ((ch = getopt(argc, argv, "f:I::jnRr:uv:")) != -1)
+	while ((ch = getopt(argc, argv, "f:I::jnRr:uv:z:")) != -1)
 		switch((char)ch) {
 		case 'f':
 			fmt = optarg;
@@ -152,6 +152,9 @@ main(int argc, char *argv[])
 		case 'u':		/* do everything in UTC */
 			(void)setenv("TZ", "UTC0", 1);
 			break;
+		case 'z':
+			outzone = optarg;
+			break;
 		case 'v':
 			v = vary_append(v, optarg);
 			break;
@@ -189,6 +192,8 @@ main(int argc, char *argv[])
 		format = *argv + 1;
 	}
 
+	if (outzone != NULL && setenv("TZ", outzone, 1) != 0)
+		err(1, "setenv(TZ)");
 	lt = localtime(&tval);
 	if (lt == NULL)
 		errx(1, "invalid time");
@@ -211,6 +216,7 @@ main(int argc, char *argv[])
 		 */
 		setlocale(LC_TIME, "C");
 
+
 	(void)strftime(buf, sizeof(buf), format, lt);
 	printdate(buf);
 }
@@ -385,7 +391,7 @@ usage(void)
 	(void)fprintf(stderr, "%s\n%s\n%s\n",
 	    "usage: date [-jnRu] [-I[date|hours|minutes|seconds]] [-f input_fmt]",
 	    "            "
-	    "[-r filename|seconds] [-v[+|-]val[y|m|w|d|H|M|S]]",
+	    "[ -z output_zone ] [-r filename|seconds] [-v[+|-]val[y|m|w|d|H|M|S]]",
 	    "            "
 	    "[[[[[[cc]yy]mm]dd]HH]MM[.SS] | new_date] [+output_fmt]"
 	    );