Revert "Use 64-bit years"

This reverts commit cfc3a953ae.
This commit is contained in:
Justine Tunney 2022-05-12 06:11:22 -07:00
parent cf73bbd678
commit dd9ab01d25
13 changed files with 484 additions and 544 deletions

View file

@ -83,13 +83,13 @@ typedef struct {
} div_t; } div_t;
typedef struct { typedef struct {
long quot; long int quot;
long rem; long int rem;
} ldiv_t; } ldiv_t;
typedef struct { typedef struct {
long long quot; long long int quot;
long long rem; long long int rem;
} lldiv_t; } lldiv_t;
typedef struct { typedef struct {

View file

@ -1,26 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/time/time.h"
/**
* Returns true if `year` is a leap year.
*/
bool _isleap(int64_t year) {
return !(year % 4) && ((year % 100) || !(year % 400));
}

View file

@ -1,41 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/time/time.h"
/*
* Since everything in _isleap() is modulo 400 (or a factor of 400), we
* know that:
*
* isleap(y) == isleap(y % 400)
*
* And so:
*
* isleap(a + b) == isleap((a + b) % 400)
*
* Or:
*
* isleap(a + b) == isleap(a % 400 + b % 400)
*
* This is true even if % means modulo rather than Fortran remainder
* (which is allowed by C89 but not by C99 or later). We use this to
* avoid addition overflow problems.
*/
bool _isleapsum(int64_t a, int64_t b) {
return _isleap(a % 400 + b % 400);
}

File diff suppressed because it is too large Load diff

View file

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

View file

@ -37,10 +37,10 @@ Copyright 2005-2019 Rich Felker, et. al.\"");
asm(".include \"libc/disclaimer.inc\""); asm(".include \"libc/disclaimer.inc\"");
char *strptime(const char *s, const char *f, struct tm *tm) { char *strptime(const char *s, const char *f, struct tm *tm) {
size_t len; int i, w, neg, adj, min, range, itemsize, *dest, dummy;
const char *ex, *ss; const char *ex, *ss;
long i, w, neg, adj, min, range, itemsize, *dest, dummy; size_t len;
long want_century = 0, century = 0, relyear = 0; int want_century = 0, century = 0, relyear = 0;
while (*f) { while (*f) {
if (*f != '%') { if (*f != '%') {
if (isspace(*f)) { if (isspace(*f)) {

View file

@ -2,24 +2,16 @@
#define COSMOPOLITAN_LIBC_TIME_STRUCT_TM_H_ #define COSMOPOLITAN_LIBC_TIME_STRUCT_TM_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* Time component structure.
*
* This structure is used by gmtime() and localtime(). It's not a kernel
* interface. We use a different ABI than most C libraries here, because
* we want to support timestamps dating back to the big bang, as well as
* timestamps through much of the stelliferous era.
*/
struct tm { struct tm {
int64_t tm_year; /* minus 1900 */ int32_t tm_sec;
int64_t tm_mon; /* 0-indexed */ int32_t tm_min;
int64_t tm_mday; /* 1-indexed */ int32_t tm_hour;
int64_t tm_hour; int32_t tm_mday; /* 1-indexed */
int64_t tm_min; int32_t tm_mon; /* 0-indexed */
int64_t tm_sec; int32_t tm_year; /* minus 1900 */
int64_t tm_wday; int32_t tm_wday;
int64_t tm_yday; int32_t tm_yday;
int64_t tm_isdst; int32_t tm_isdst;
int64_t tm_gmtoff; int64_t tm_gmtoff;
const char *tm_zone; const char *tm_zone;
}; };

View file

@ -66,8 +66,6 @@ extern long double (*nowl)(void);
long double ConvertTicksToNanos(uint64_t); long double ConvertTicksToNanos(uint64_t);
void RefreshTime(void); void RefreshTime(void);
bool _isleap(int64_t);
bool _isleapsum(int64_t, int64_t);
double difftime(int64_t, int64_t) dontthrow pureconst; double difftime(int64_t, int64_t) dontthrow pureconst;
char *iso8601(char[hasatleast 20], struct tm *); char *iso8601(char[hasatleast 20], struct tm *);
size_t wcsftime(wchar_t *, size_t, const wchar_t *, const struct tm *); size_t wcsftime(wchar_t *, size_t, const wchar_t *, const struct tm *);

View file

@ -70,9 +70,7 @@ o/$(MODE)/libc/time/localtime.o: \
OVERRIDE_CPPFLAGS += \ OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED -DSTACK_FRAME_UNLIMITED
# guarantee constant divisor optimization o/$(MODE)/libc/time/now.o: \
o/$(MODE)/libc/time/isleap.o \
o/$(MODE)/libc/time/isleapsum.o: \
OVERRIDE_CFLAGS += \ OVERRIDE_CFLAGS += \
-O3 -O3

View file

@ -5,7 +5,6 @@
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/inttypes.h" #include "libc/inttypes.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/consts/ok.h" #include "libc/sysv/consts/ok.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
@ -378,6 +377,7 @@ int64_t time2posix_z(timezone_t, int64_t) nosideeffect;
*/ */
#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT) #define TYPE_BIT(type) (sizeof(type) * CHAR_BIT)
#define TYPE_SIGNED(type) (((type) -1) < 0)
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0) #define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
/* Max and min values of the integer type T, of which only the bottom /* Max and min values of the integer type T, of which only the bottom
@ -443,6 +443,19 @@ int64_t time2posix_z(timezone_t, int64_t) nosideeffect;
# define UNINIT_TRAP 0 # define UNINIT_TRAP 0
#endif #endif
#ifdef DEBUG
# define UNREACHABLE() abort()
#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
# define UNREACHABLE() __builtin_unreachable()
#elif defined __has_builtin
# if __has_builtin(__builtin_unreachable)
# define UNREACHABLE() __builtin_unreachable()
# endif
#endif
#ifndef UNREACHABLE
# define UNREACHABLE() ((void) 0)
#endif
/* /*
** For the benefit of GNU folk... ** For the benefit of GNU folk...
** '_(MSGID)' uses the current locale's message library string for MSGID. ** '_(MSGID)' uses the current locale's message library string for MSGID.
@ -510,6 +523,22 @@ char *ctime_r(int64_t const *, char *);
#define EPOCH_YEAR 1970 #define EPOCH_YEAR 1970
#define EPOCH_WDAY TM_THURSDAY #define EPOCH_WDAY TM_THURSDAY
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
/*
** Since everything in isleap is modulo 400 (or a factor of 400), we know that
** isleap(y) == isleap(y % 400)
** and so
** isleap(a + b) == isleap((a + b) % 400)
** or
** isleap(a + b) == isleap(a % 400 + b % 400)
** This is true even if % means modulo rather than Fortran remainder
** (which is allowed by C89 but not by C99 or later).
** We use this to avoid addition overflow problems.
*/
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ */ #endif /* COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ */

View file

@ -20,7 +20,6 @@
#include "libc/calls/struct/timeval.h" #include "libc/calls/struct/timeval.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/mem/fmt.h" #include "libc/mem/fmt.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/clock.h"
@ -34,11 +33,10 @@
static char *xiso8601_impl(struct timespec *opt_ts, int sswidth) { static char *xiso8601_impl(struct timespec *opt_ts, int sswidth) {
char *p; char *p;
int i, j, n;
struct tm tm; struct tm tm;
struct timespec ts; struct timespec ts;
int64_t sec, subsec; int64_t sec, subsec;
char buf[128], ibuf[21]; char timebuf[64], zonebuf[8];
if (opt_ts) { if (opt_ts) {
sec = opt_ts->tv_sec; sec = opt_ts->tv_sec;
subsec = opt_ts->tv_nsec; subsec = opt_ts->tv_nsec;
@ -58,20 +56,10 @@ static char *xiso8601_impl(struct timespec *opt_ts, int sswidth) {
sswidth = 7; /* windows nt uses hectonanoseconds */ sswidth = 7; /* windows nt uses hectonanoseconds */
} }
localtime_r(&sec, &tm); localtime_r(&sec, &tm);
i = strftime(buf, 64, "%Y-%m-%dT%H:%M:%S", &tm); strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", &tm);
p = FormatInt64(ibuf, subsec); strftime(zonebuf, sizeof(zonebuf), "%z", &tm);
for (n = sswidth - (p - ibuf); n > 0; --n) { (asprintf)(&p, "%s.%0*ld%s", timebuf, sswidth, subsec, zonebuf);
if (i < sizeof(buf)) { return p;
buf[i++] = '0';
}
}
for (j = 0; ibuf[j]; ++j) {
if (i < sizeof(buf)) {
buf[i++] = ibuf[j];
}
}
strftime(buf + i, sizeof(buf) - i, "%z", &tm);
return strdup(buf);
} }
/** /**

View file

@ -18,10 +18,7 @@
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/time/time.h" #include "libc/time/time.h"
@ -79,6 +76,12 @@ TEST(strftime_201, rfc822_GoogleStandardTime) {
FormatTime("%a, %d %b %y %T %z", localtime(&t))); FormatTime("%a, %d %b %y %T %z", localtime(&t)));
} }
/* TEST(xiso8601, testModernity_TODO) { */
/* int64_t t = (1600 - 1970) * 31536000; */
/* ASSERT_STREQ("1600-01-01T00:00:00Z", */
/* FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); */
/* } */
TEST(xiso8601, testAtLeastBetterThanTraditionalUnixLimit) { TEST(xiso8601, testAtLeastBetterThanTraditionalUnixLimit) {
int64_t t = 10737418235; int64_t t = 10737418235;
ASSERT_STREQ("2310-04-04T16:10:35Z", ASSERT_STREQ("2310-04-04T16:10:35Z",
@ -91,36 +94,8 @@ TEST(xiso8601, testSomethingHuge) {
FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t)));
} }
// this requires 52 bit year /* TEST(xiso8601, testMostOfStelliferousEra_TODO) { */
TEST(xiso8601, testBigBang) { /* int64_t t = INT64_MAX; */
struct tm tm; /* ASSERT_STREQ("somethinghuge-01-01T00:00:00Z", */
int64_t t = -13700000000L * 31536000L; /* FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); */
CHECK_NOTNULL(gmtime_r(&t, &tm)); /* } */
ASSERT_STREQ("-13690902019-07-22T00:00:00Z",
FormatTime("%Y-%m-%dT%H:%M:%SZ", &tm));
}
// stelliferous era is 10**6 < n < 10**14 years (71-bit)
// we support up to approx. 10**11 yr after the big bang
TEST(xiso8601, testMostOfStelliferousEra) {
struct tm tm;
int64_t t = -13700000000L + 100000000000 /*000*/ * 31536000L;
CHECK_NOTNULL(gmtime_r(&t, &tm));
ASSERT_STREQ("99933607290-12-11T04:26:40Z",
FormatTime("%Y-%m-%dT%H:%M:%SZ", &tm));
}
const char *GmtimeFormatTime(void) {
struct tm tm;
int64_t t = -13700000000L * 31536000L;
CHECK_NOTNULL(gmtime_r(&t, &tm));
return FormatTime("%Y-%m-%dT%H:%M:%SZ", &tm);
}
BENCH(strftime, bench) {
struct tm tm;
int64_t t = -13700000000L * 31536000L;
CHECK_NOTNULL(gmtime_r(&t, &tm));
EZBENCH2("strftime", donothing, FormatTime("%Y-%m-%dT%H:%M:%SZ", &tm));
EZBENCH2("gmtime+strftime", donothing, GmtimeFormatTime());
}

View file

@ -2796,12 +2796,12 @@ UNIX MODULE
stdio stdio
Allows clock_getres, clock_gettime, close, dup, fchdir, fstat, Allows clock_getres, clock_gettime, close, dup, dup2, dup3,
fsync, ftruncate, getdents, getegid, getrandom, geteuid, getgid, fchdir, fstat, fsync, ftruncate, getdents, getegid, getrandom,
getgroups, getitimer, getpgid, getpgrp, getpid, getppid, geteuid, getgid, getgroups, getitimer, getpgid, getpgrp, getpid,
getresgid, getresuid, getrlimit, getsid, gettimeofday, getuid, getppid, getresgid, getresuid, getrlimit, getsid, gettimeofday,
lseek, madvise, brk, mmap, mprotect, munmap, nanosleep, pipe, getuid, lseek, madvise, brk, mmap, mprotect, munmap, nanosleep,
pipe2, poll, pread, preadv, pwrite, pwritev, read, readv, pipe, pipe2, poll, pread, preadv, pwrite, pwritev, read, readv,
recvfrom, recvmsg, select, sendmsg, sendto, setitimer, shutdown, recvfrom, recvmsg, select, sendmsg, sendto, setitimer, shutdown,
sigaction, sigprocmask, sigreturn, socketpair, umask, wait4, sigaction, sigprocmask, sigreturn, socketpair, umask, wait4,
write, writev. write, writev.
@ -2866,49 +2866,17 @@ UNIX MODULE
├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
└─→ nil,unix.Errno └─→ nil,unix.Errno
Breaks down UNIX timestamp into Shaka Zulu Time numbers. Breaks down UNIX timestamp into Zulu Time numbers.
This function is like localtime() except it always returns Greenwich - `mon` 1 ≤ mon ≤ 12
Mean Time irrespective of the TZ environment variable. - `mday` 1 ≤ mday ≤ 31
- `hour` 0 ≤ hour ≤ 23
For example: - `min` 0 ≤ min ≤ 59
- `sec` 0 ≤ sec ≤ 60
>: unix.gmtime(unix.clock_gettime()) - `gmtoff` ±93600 seconds
2022 5 11 22 43 20 0 3 130 0 "GMT" - `wday` 0 ≤ wday ≤ 6
- `yday` 0 ≤ yday ≤ 365
Here's how you might format a localized timestamp with nanoseconds: - `dst` 1 if daylight savings, 0 if not, -1 if not unknown
>: unixsec, nanos = unix.clock_gettime()
>: year,mon,mday,hour,min,sec = unix.localtime(unixsec)
>: '%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.9dZ' % {year,mon,mday,hour,min,sec,nanos}
"2022-05-11T15:46:32.160239978Z"
`year` is the year, where zero is defined as 0 A.D. This value may
be on the interval `-13.7e9 ≤ year ≤ 10e14` which is the time from
the Big Bang, through most of the Stelliferous Era.
`mon` is the month of the year, on the interval `1 ≤ mon ≤ 12` in
order to make printf style formatting easier.
`mday` is the day of the month, on the interval `1 ≤ mday ≤ 31` in
order to make printf style formatting easier.
`hour` represent hours, on the interval `0 ≤ hour ≤ 23`. - `min`
represents minutes, on the interval `0 ≤ min ≤ 59`.
`sec` represents seconds, on the interval `0 ≤ sec ≤ 60`. Please
note this is a 61 second interval in order to accommodate highly
rare leap second events.
`wday` is the day of the week, on the interval `0 ≤ wday ≤ 6`.
`yday` is the day of the year on the interval `0 ≤ yday ≤ 365`.
`gmtoff` is the Shaka Zulu time offset in seconds, which should be
on the interval ±93600 seconds.
`dst` will be 1 if daylight savings, 0 if not daylight savings, or
-1 if it couldn't be determined.
unix.localtime(unixts:int) unix.localtime(unixts:int)
├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
@ -2919,21 +2887,7 @@ UNIX MODULE
>: unix.localtime(unix.clock_gettime()) >: unix.localtime(unix.clock_gettime())
2022 4 28 2 14 22 -25200 4 117 1 "PDT" 2022 4 28 2 14 22 -25200 4 117 1 "PDT"
This follows the same API as gmtime() except it takes the `TZ` This follows the same API as gmtime() which has further details.
environment variable into consideration to determine the most
appropriate localization.
Please see the gmtime() function for documentaiton on the meaning of
the various returned values.
Here's an example of how you might format a localized timestamp:
>: unixsec, nanos = unix.clock_gettime()
>: year, mon, mday, hour, min, sec, gmtoffsec = unix.localtime(unixsec)
>: '%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.9d%+.2d%.2d' % {
year, mon, mday, hour, min, sec, nanos,
gmtoffsec / (60 * 60), math.abs(gmtoffsec) % 60}
"2022-05-11T15:46:32.160239978-0700"
Your redbean ships with a subset of the time zone database. Your redbean ships with a subset of the time zone database.