>From 1da3b6e86292f9a713f00d45d92ca4bc28721e2f Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Mon, 12 Jun 2017 22:57:14 -0700
Subject: [PROPOSED] Bring back Y2K code as DEPRECATE_TWO_DIGIT_YEARS

Perhaps this stuff will be useful around 2100.
Requested by Brian Inglis in:
http://mm.icann.org/pipermail/tz/2017-June/025138.html
* Makefile, NEWS: Mention this.
* strftime.c (YEAR_2000_NAME, IN_NONE, IN_SOME, IN_THIS, IN_ALL):
Bring back these symbols and their uses, except warn if
DEPRECATE_TWO_DIGIT_YEARS, and use enum rather than int.
(strftime): Warn about Y2K problems.
(_fmt): Bring back last (warning) arg; all uses changed.
---
 Makefile   |  2 ++
 NEWS       |  7 +++++--
 strftime.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++------------
 3 files changed, 64 insertions(+), 15 deletions(-)

diff --git a/Makefile b/Makefile
index c02e149..347130f 100644
--- a/Makefile
+++ b/Makefile
@@ -132,6 +132,8 @@ LDLIBS=
 
 # Add the following to the end of the "CFLAGS=" line as needed.
 #  -DBIG_BANG=-9999999LL if the Big Bang occurred at time -9999999 (see zic.c)
+#  -DDEPRECATE_TWO_DIGIT_YEARS for optional runtime warnings about strftime
+#	formats that generate only the last two digits of year numbers
 #  -DHAVE_DECL_ASCTIME_R=0 if <time.h> does not declare asctime_r
 #  -DHAVE_DECL_ENVIRON if <unistd.h> declares 'environ'
 #  -DHAVE_DIRECT_H if mkdir needs <direct.h> (MS-Windows)
diff --git a/NEWS b/NEWS
index 1162a67..3c47f77 100644
--- a/NEWS
+++ b/NEWS
@@ -41,6 +41,11 @@ Unreleased, experimental changes
     or that lack the nsgmls program.  Set UTF8_LOCALE to configure
     the name of a UTF-8 locale, if you have one.
 
+    Y2K runtime checks are no longer enabled by default.  Add
+    -DDEPRECATE_TWO_DIGIT_YEARS to CFLAGS to enable them, instead of
+    adding -DNO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
+    to disable them.  (New name suggested by Brian Inglis.)
+
     The build procedure for zdump now works on AIX 7.1.
     (Problem reported by Kees Dekker.)
 
@@ -81,8 +86,6 @@ Unreleased, experimental changes
     other two variables as optional.  Also, USG_COMPAT is now 1 or 0:
     if not defined, the code attempts to guess it from other macros.
 
-    Y2K runtime checks have been removed, as Y2K was years ago.
-
     localtime.c and difftime.c no longer require stdio.h, and .c files
     other than zic.c no longer require sys/wait.h.
 
diff --git a/strftime.c b/strftime.c
index 76d3342..1ffe618 100644
--- a/strftime.c
+++ b/strftime.c
@@ -39,6 +39,10 @@
 #include <locale.h>
 #include <stdio.h>
 
+#ifndef DEPRECATE_TWO_DIGIT_YEARS
+# define DEPRECATE_TWO_DIGIT_YEARS false
+#endif
+
 struct lc_time_T {
 	const char *	mon[MONSPERYEAR];
 	const char *	month[MONSPERYEAR];
@@ -100,11 +104,18 @@ static const struct lc_time_T	C_time_locale = {
 	"%a %b %e %H:%M:%S %Z %Y"
 };
 
+enum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL };
+
 static char *	_add(const char *, char *, const char *);
 static char *	_conv(int, const char *, char *, const char *);
-static char *	_fmt(const char *, const struct tm *, char *, const char *);
+static char *	_fmt(const char *, const struct tm *, char *, const char *,
+		     enum warn *);
 static char *	_yconv(int, int, bool, bool, char *, char const *);
 
+#ifndef YEAR_2000_NAME
+#define YEAR_2000_NAME	"CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
+#endif /* !defined YEAR_2000_NAME */
+
 #if HAVE_STRFTIME_L
 size_t
 strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
@@ -119,9 +130,22 @@ size_t
 strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
 {
 	char *	p;
+	enum warn warn = IN_NONE;
 
 	tzset();
-	p = _fmt(format, t, s, s + maxsize);
+	p = _fmt(format, t, s, s + maxsize, &warn);
+	if (DEPRECATE_TWO_DIGIT_YEARS
+	    && warn != IN_NONE && getenv(YEAR_2000_NAME)) {
+		fprintf(stderr, "\n");
+		fprintf(stderr, "strftime format \"%s\" ", format);
+		fprintf(stderr, "yields only two digits of years in ");
+		if (warn == IN_SOME)
+			fprintf(stderr, "some locales");
+		else if (warn == IN_THIS)
+			fprintf(stderr, "the current locale");
+		else	fprintf(stderr, "all locales");
+		fprintf(stderr, "\n");
+	}
 	if (p == s + maxsize)
 		return 0;
 	*p = '\0';
@@ -129,7 +153,8 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
 }
 
 static char *
-_fmt(const char *format, const struct tm *t, char *pt, const char *ptlim)
+_fmt(const char *format, const struct tm *t, char *pt,
+     const char *ptlim, enum warn *warnp)
 {
 	for ( ; *format; ++format) {
 		if (*format == '%') {
@@ -175,10 +200,18 @@ label:
 					    true, false, pt, ptlim);
 				continue;
 			case 'c':
-				pt = _fmt(Locale->c_fmt, t, pt, ptlim);
+				{
+				enum warn warn2 = IN_SOME;
+
+				pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
+				if (warn2 == IN_ALL)
+					warn2 = IN_THIS;
+				if (warn2 > *warnp)
+					*warnp = warn2;
+				}
 				continue;
 			case 'D':
-				pt = _fmt("%m/%d/%y", t, pt, ptlim);
+				pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
 				continue;
 			case 'd':
 				pt = _conv(t->tm_mday, "%02d", pt, ptlim);
@@ -199,7 +232,7 @@ label:
 				pt = _conv(t->tm_mday, "%2d", pt, ptlim);
 				continue;
 			case 'F':
-				pt = _fmt("%Y-%m-%d", t, pt, ptlim);
+				pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
 				continue;
 			case 'H':
 				pt = _conv(t->tm_hour, "%02d", pt, ptlim);
@@ -263,10 +296,10 @@ label:
 					pt, ptlim);
 				continue;
 			case 'R':
-				pt = _fmt("%H:%M", t, pt, ptlim);
+				pt = _fmt("%H:%M", t, pt, ptlim, warnp);
 				continue;
 			case 'r':
-				pt = _fmt("%I:%M:%S %p", t, pt, ptlim);
+				pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
 				continue;
 			case 'S':
 				pt = _conv(t->tm_sec, "%02d", pt, ptlim);
@@ -289,7 +322,7 @@ label:
 				}
 				continue;
 			case 'T':
-				pt = _fmt("%H:%M:%S", t, pt, ptlim);
+				pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
 				continue;
 			case 't':
 				pt = _add("\t", pt, ptlim);
@@ -391,6 +424,7 @@ label:
 						pt = _conv(w, "%02d",
 							pt, ptlim);
 					else if (*format == 'g') {
+						*warnp = IN_ALL;
 						pt = _yconv(year, base,
 							false, true,
 							pt, ptlim);
@@ -405,7 +439,7 @@ label:
 				** "date as dd-bbb-YYYY"
 				** (ado, 1993-05-24)
 				*/
-				pt = _fmt("%e-%b-%Y", t, pt, ptlim);
+				pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
 				continue;
 			case 'W':
 				pt = _conv((t->tm_yday + DAYSPERWEEK -
@@ -418,12 +452,21 @@ label:
 				pt = _conv(t->tm_wday, "%d", pt, ptlim);
 				continue;
 			case 'X':
-				pt = _fmt(Locale->X_fmt, t, pt, ptlim);
+				pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
 				continue;
 			case 'x':
-				pt = _fmt(Locale->x_fmt, t, pt, ptlim);
+				{
+				enum warn warn2 = IN_SOME;
+
+				pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
+				if (warn2 == IN_ALL)
+					warn2 = IN_THIS;
+				if (warn2 > *warnp)
+					*warnp = warn2;
+				}
 				continue;
 			case 'y':
+				*warnp = IN_ALL;
 				pt = _yconv(t->tm_year, TM_YEAR_BASE,
 					false, true,
 					pt, ptlim);
@@ -516,7 +559,8 @@ label:
 #endif
 				continue;
 			case '+':
-				pt = _fmt(Locale->date_fmt, t, pt, ptlim);
+				pt = _fmt(Locale->date_fmt, t, pt, ptlim,
+					warnp);
 				continue;
 			case '%':
 			/*
-- 
2.9.4

