Use 64-bit years

This change makes strftime() go faster and makes it possible to format
timestamps through the big bang to most of the stelliferous era. India
has also been added as a timezone to most binaries. Since we were able
to change the struct tm abi, this makes cosmopolitan libc superior, to
just about everything else, when it comes to standing the test of time
This commit is contained in:
Justine Tunney 2022-05-11 17:48:50 -07:00
parent 2aebda7718
commit cfc3a953ae
13 changed files with 542 additions and 482 deletions

View file

@ -16,9 +16,11 @@
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "libc/fmt/fmt.h"
#include "libc/bits/pushpop.h"
#include "libc/fmt/itoa.h"
#include "libc/inttypes.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/time/time.h"
#include "libc/time/tz.internal.h"
#include "libc/unicode/locale.h"
@ -113,14 +115,56 @@ strftime_add(const char *str, char *pt, const char *ptlim)
return pt;
}
static char *
strftime_conv(int n, const char *format, char *pt, const char *ptlim)
static dontinline char *
strftime_conv(int64_t x, char *pt, const char *ptlim, int width, char pad)
{
char buf[INT_STRLEN_MAXIMUM(int) + 1];
(sprintf)(buf, format, n);
int n;
char buf[21], *ptr, *end;
end = FormatInt64(buf, x);
for (n = width - (end - buf); n > 0; --n) {
if (pt < ptlim) {
*pt++ = pad;
}
}
return strftime_add(buf, pt, ptlim);
}
static dontinline char *
strftime_conv_d(int64_t n, char *pt, const char *ptlim)
{
return strftime_conv(n, pt, ptlim, 0, 0);
}
static dontinline char *
strftime_conv_2d(int64_t n, char *pt, const char *ptlim)
{
return strftime_conv(n, pt, ptlim, pushpop(2L), pushpop(' '));
}
static dontinline char *
strftime_conv_0(int64_t n, char *pt, const char *ptlim, int width)
{
return strftime_conv(n, pt, ptlim, width, pushpop('0'));
}
static dontinline char *
strftime_conv_02d(int64_t n, char *pt, const char *ptlim)
{
return strftime_conv_0(n, pt, ptlim, pushpop(2L));
}
static dontinline char *
strftime_conv_03d(int64_t n, char *pt, const char *ptlim)
{
return strftime_conv_0(n, pt, ptlim, pushpop(3L));
}
static dontinline char *
strftime_conv_04d(int64_t n, char *pt, const char *ptlim)
{
return strftime_conv_0(n, pt, ptlim, pushpop(4L));
}
/*
** POSIX and the C Standard are unclear or inconsistent about
** what %C and %y do if the year is negative or exceeds 9999.
@ -131,15 +175,15 @@ strftime_conv(int n, const char *format, char *pt, const char *ptlim)
static char *
strftime_yconv(
int a,
int b,
int64_t a,
int64_t b,
bool convert_top,
bool convert_yy,
char *pt,
const char *ptlim)
{
register int lead;
register int trail;
int64_t lead;
int64_t trail;
trail = a % DIVISOR + b % DIVISOR;
lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
@ -154,10 +198,10 @@ strftime_yconv(
if (convert_top) {
if (lead == 0 && trail < 0)
pt = strftime_add("-0", pt, ptlim);
else pt = strftime_conv(lead, "%02d", pt, ptlim);
else pt = strftime_conv_02d(lead, pt, ptlim);
}
if (convert_yy)
pt = strftime_conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
pt = strftime_conv_02d(((trail < 0) ? -trail : trail), pt, ptlim);
return pt;
}
@ -227,7 +271,7 @@ label:
pt = strftime_fmt("%m/%d/%y", t, pt, ptlim, warnp);
continue;
case 'd':
pt = strftime_conv(t->tm_mday, "%02d", pt, ptlim);
pt = strftime_conv_02d(t->tm_mday, pt, ptlim);
continue;
case 'E':
case 'O':
@ -242,21 +286,21 @@ label:
*/
goto label;
case 'e':
pt = strftime_conv(t->tm_mday, "%2d", pt, ptlim);
pt = strftime_conv_2d(t->tm_mday, pt, ptlim);
continue;
case 'F':
pt = strftime_fmt("%Y-%m-%d", t, pt, ptlim, warnp);
continue;
case 'H':
pt = strftime_conv(t->tm_hour, "%02d", pt, ptlim);
pt = strftime_conv_02d(t->tm_hour, pt, ptlim);
continue;
case 'I':
pt = strftime_conv((t->tm_hour % 12) ?
(t->tm_hour % 12) : 12,
"%02d", pt, ptlim);
pt = strftime_conv_02d((t->tm_hour % 12) ?
(t->tm_hour % 12) : 12,
pt, ptlim);
continue;
case 'j':
pt = strftime_conv(t->tm_yday + 1, "%03d", pt, ptlim);
pt = strftime_conv_03d(t->tm_yday + 1, pt, ptlim);
continue;
case 'k':
/*
@ -269,8 +313,7 @@ label:
** "%l" have been swapped.
** (ado, 1993-05-24)
*/
pt = strftime_conv(t->tm_hour, "%2d",
pt, ptlim);
pt = strftime_conv_2d(t->tm_hour, pt, ptlim);
continue;
#ifdef KITCHEN_SINK
case 'K':
@ -290,17 +333,15 @@ label:
** "%l" have been swapped.
** (ado, 1993-05-24)
*/
pt = strftime_conv((t->tm_hour % 12) ?
(t->tm_hour % 12) : 12,
"%2d", pt, ptlim);
pt = strftime_conv_2d((t->tm_hour % 12) ?
(t->tm_hour % 12) : 12,
pt, ptlim);
continue;
case 'M':
pt = strftime_conv(t->tm_min, "%02d",
pt, ptlim);
pt = strftime_conv_02d(t->tm_min, pt, ptlim);
continue;
case 'm':
pt = strftime_conv(t->tm_mon + 1, "%02d",
pt, ptlim);
pt = strftime_conv_02d(t->tm_mon + 1, pt, ptlim);
continue;
case 'n':
pt = strftime_add("\n", pt, ptlim);
@ -320,14 +361,12 @@ label:
ptlim, warnp);
continue;
case 'S':
pt = strftime_conv(t->tm_sec, "%02d", pt,
ptlim);
pt = strftime_conv_02d(t->tm_sec, pt, ptlim);
continue;
case 's':
{
struct tm tm;
char buf[INT_STRLEN_MAXIMUM(
time_t) + 1];
char buf[21];
time_t mkt;
tm = *t;
@ -346,14 +385,7 @@ label:
&& tm.tm_sec == tm_1.tm_sec))
return NULL;
}
if (TYPE_SIGNED(time_t)) {
intmax_t n = mkt;
(sprintf)(buf, "%"PRIdMAX, n);
} else {
uintmax_t n = mkt;
(sprintf)(buf, "%"PRIuMAX, n);
}
pt = strftime_add(buf, pt, ptlim);
strftime_conv_d(mkt, pt, ptlim);
}
continue;
case 'T':
@ -363,9 +395,10 @@ label:
pt = strftime_add("\t", pt, ptlim);
continue;
case 'U':
pt = strftime_conv((t->tm_yday + DAYSPERWEEK -
t->tm_wday) / DAYSPERWEEK,
"%02d", pt, ptlim);
pt = strftime_conv_02d(
(t->tm_yday + DAYSPERWEEK -
t->tm_wday) / DAYSPERWEEK,
pt, ptlim);
continue;
case 'u':
/*
@ -374,9 +407,9 @@ label:
** [1 (Monday) - 7]"
** (ado, 1993-05-24)
*/
pt = strftime_conv((t->tm_wday == 0) ?
DAYSPERWEEK : t->tm_wday,
"%d", pt, ptlim);
pt = strftime_conv_d((t->tm_wday == 0) ?
DAYSPERWEEK : t->tm_wday,
pt, ptlim);
continue;
case 'V': /* ISO 8601 week number */
case 'G': /* ISO 8601 year (four digits) */
@ -400,22 +433,22 @@ label:
** (ado, 1996-01-02)
*/
{
int year;
int base;
int yday;
int wday;
int w;
int64_t year;
int64_t base;
int64_t yday;
int64_t wday;
int64_t w;
year = t->tm_year;
base = TM_YEAR_BASE;
yday = t->tm_yday;
wday = t->tm_wday;
for ( ; ; ) {
int len;
int bot;
int top;
int64_t len;
int64_t bot;
int64_t top;
len = isleap_sum(year, base) ?
len = _isleapsum(year, base) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
/*
@ -444,13 +477,13 @@ label:
break;
}
--base;
yday += isleap_sum(year, base) ?
yday += _isleapsum(year, base) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
}
if (*format == 'V')
pt = strftime_conv(w, "%02d",
pt, ptlim);
pt = strftime_conv_02d(
w, pt, ptlim);
else if (*format == 'g') {
*warnp = IN_ALL;
pt = strftime_yconv(year, base,
@ -470,15 +503,15 @@ label:
pt = strftime_fmt("%e-%b-%Y", t, pt, ptlim, warnp);
continue;
case 'W':
pt = strftime_conv(
pt = strftime_conv_02d(
(t->tm_yday + DAYSPERWEEK -
(t->tm_wday ?
(t->tm_wday - 1) :
(DAYSPERWEEK - 1))) / DAYSPERWEEK,
"%02d", pt, ptlim);
pt, ptlim);
continue;
case 'w':
pt = strftime_conv(t->tm_wday, "%d", pt, ptlim);
pt = strftime_conv_d(t->tm_wday, pt, ptlim);
continue;
case 'X':
pt = strftime_fmt(Locale->X_fmt, t, pt, ptlim, warnp);
@ -533,7 +566,7 @@ label:
diff /= SECSPERMIN;
diff = (diff / MINSPERHOUR) * 100 +
(diff % MINSPERHOUR);
pt = strftime_conv(diff, "%04d", pt, ptlim);
pt = strftime_conv_04d(diff, pt, ptlim);
}
continue;
case '+':