mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
parent
cf73bbd678
commit
dd9ab01d25
13 changed files with 484 additions and 544 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
|
|
@ -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
|
@ -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 '+':
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 *);
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue