5
|
|
|
|
|
|
|
#include "moment.h"
#include "dt_core.h"
#include "dt_accessor.h"
typedef enum {
PAD_DEFAULT,
PAD_NONE,
PAD_ZERO,
PAD_SPACE,
} pad_t;
static const char *aDoW[] = {
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"Sun",
};
static const char *fDoW[] = {
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
};
static const char *aMonth[] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
};
static const char *fMonth[] = {
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
};
static const char *Meridiem[] = {
"AM",
"PM",
};
/*
* The first Sunday of January is the first day of week 1; days in the new
* year before this are in week 0.
*/
static int
dt_week_number_sun(dt_t dt) {
int sunday = dt_doy(dt) - dt_dow(dt) % 7;
return (sunday + 6) / 7;
}
/*
* The first Monday of January is the first day of week 1; days in the new
* year before this are in week 0.
*/
static int
dt_week_number_mon(dt_t dt) {
int monday = dt_doy(dt) - (dt_dow(dt) + 6) % 7;
return (monday + 6) / 7;
}
static int
moment_hour_12(const moment_t *mt) {
int h = moment_hour(mt) % 12;
if (h == 0)
h = 12;
return h;
}
static const char *
moment_hour_meridiem(const moment_t *mt) {
return Meridiem[moment_hour(mt) / 12];
}
static bool
supports_padding_flag(const char c) {
switch (c) {
case 'C':
case 'd':
case 'e':
case 'g':
case 'G':
case 'H':
case 'I':
case 'j':
case 'k':
case 'l':
case 'm':
case 'M':
case 'S':
case 'U':
case 'V':
case 'W':
case 'y':
case 'Y':
return TRUE;
}
return FALSE;
}
static void
THX_format_num(pTHX_ SV *dsv, size_t width, pad_t want, pad_t def, unsigned int v) {
char buf[20], *p, *e, *d, c;
size_t nlen, plen, dlen;
p = e = buf + sizeof(buf);
do {
*--p = '0' + (v % 10);
} while (v /= 10);
if (want == PAD_DEFAULT)
want = def;
if (want == PAD_ZERO) c = '0';
else if (want == PAD_SPACE) c = ' ';
else width = 0;
nlen = e - p;
plen = (width > nlen) ? width - nlen : 0;
dlen = nlen + plen;
(void)SvGROW(dsv, SvCUR(dsv) + dlen + 1);
d = SvPVX(dsv) + SvCUR(dsv);
if (plen) {
memset(d, c, plen);
d += plen;
}
memcpy(d, p, nlen);
SvCUR_set(dsv, SvCUR(dsv) + dlen);
*SvEND(dsv) = '\0';
}
#define CHR(n, d) (char)('0' + ((n) / (d)) % 10)
static void
THX_format_f(pTHX_ SV *dsv, const moment_t *mt, int len) {
char buf[9];
int ns;
if (len > 9) len = 9;
else if (len < 0) len = 0;
ns = moment_nanosecond(mt);
if (len == 0) {
if ((ns % 1000000) == 0) len = 3;
else if ((ns % 1000) == 0) len = 6;
else len = 9;
}
switch (len) {
case 9: buf[8] = CHR(ns, 1);
case 8: buf[7] = CHR(ns, 10);
case 7: buf[6] = CHR(ns, 100);
case 6: buf[5] = CHR(ns, 1000);
case 5: buf[4] = CHR(ns, 10000);
case 4: buf[3] = CHR(ns, 100000);
case 3: buf[2] = CHR(ns, 1000000);
case 2: buf[1] = CHR(ns, 10000000);
case 1: buf[0] = CHR(ns, 100000000);
}
sv_catpvn(dsv, buf, len);
}
#undef CHR
static void
THX_format_s(pTHX_ SV *dsv, const moment_t *mt) {
char buf[30], *p, *e;
int64_t v;
v = moment_epoch(mt);
p = e = buf + sizeof(buf);
if (v < 0) {
do {
*--p = '0' - (v % 10);
} while (v /= 10);
*--p = '-';
}
else {
do {
*--p = '0' + (v % 10);
} while (v /= 10);
}
sv_catpvn(dsv, p, e - p);
}
static void
THX_format_z(pTHX_ SV *dsv, const moment_t *mt, int extended) {
int offset, sign;
offset = moment_offset(mt);
if (offset < 0)
sign = '-', offset = -offset;
else
sign = '+';
if (extended)
sv_catpvf(dsv, "%c%02d:%02d", sign, offset / 60, offset % 60);
else
sv_catpvf(dsv, "%c%04d", sign, (offset / 60) * 100 + (offset % 60));
}
static void
THX_format_Z(pTHX_ SV *dsv, const moment_t *mt) {
int offset, sign;
offset = moment_offset(mt);
if (offset == 0)
sv_catpvn(dsv, "Z", 1);
else {
if (offset < 0)
sign = '-', offset = -offset;
else
sign = '+';
sv_catpvf(dsv, "%c%02d:%02d", sign, offset / 60, offset % 60);
}
}
#define format_num(dsv, width, wanted, def, num) \
THX_format_num(aTHX_ dsv, width, wanted, def, num)
#define format_f(dsv, mt, len) \
THX_format_f(aTHX_ dsv, mt, len)
#define format_s(dsv, mt) \
THX_format_s(aTHX_ dsv, mt)
#define format_z(dsv, mt, extended) \
THX_format_z(aTHX_ dsv, mt, extended)
#define format_Z(dsv, mt) \
THX_format_Z(aTHX_ dsv, mt)
SV *
THX_moment_strftime(pTHX_ const moment_t *mt, const char *s, STRLEN len) {
const char *e, *p;
char c;
SV *dsv;
dt_t dt;
pad_t pad;
int year, month, day, width, zextd;
dsv = sv_2mortal(newSV(16));
SvCUR_set(dsv, 0);
SvPOK_only(dsv);
dt = moment_local_dt(mt);
dt_to_ymd(dt, &year, &month, &day);
e = s + len;
while (s < e) {
p = (const char *)memchr(s, '%', e - s);
if (p == NULL || p + 1 == e)
p = e;
sv_catpvn(dsv, s, p - s);
if (p == e)
break;
pad = PAD_DEFAULT;
width = -1;
zextd = 0;
s = p + 1;
label:
switch (c = *s++) {
case 'a': /* locale's abbreviated day of the week name */
sv_catpv(dsv, aDoW[dt_dow(dt) - 1]);
break;
case 'A': /* locale's full day of the week name */
sv_catpv(dsv, fDoW[dt_dow(dt) - 1]);
break;
case 'b': /* locale's abbreviated month name */
case 'h':
sv_catpv(dsv, aMonth[month - 1]);
break;
case 'B': /* locale's full month name */
sv_catpv(dsv, fMonth[month - 1]);
break;
case 'c': /* locale's date and time (C locale: %a %b %e %H:%M:%S %Y) */
sv_catpvf(dsv, "%s %s %2d %02d:%02d:%02d %04d",
aDoW[dt_dow(dt) - 1],
aMonth[month - 1],
day,
moment_hour(mt),
moment_minute(mt),
moment_second(mt),
year);
break;
case 'C':
format_num(dsv, 2, pad, PAD_ZERO, year / 100);
break;
case 'd':
format_num(dsv, 2, pad, PAD_ZERO, day);
break;
case 'x': /* locale's time representation (C locale: %m/%d/%y) */
case 'D':
sv_catpvf(dsv, "%02d/%02d/%02d", month, day, year % 100);
break;
case 'e':
format_num(dsv, 2, pad, PAD_SPACE, day);
break;
case 'f': /* extended conversion specification */
if (moment_nanosecond(mt)) {
sv_catpvn(dsv, ".", 1);
format_f(dsv, mt, width);
}
break;
case 'F':
sv_catpvf(dsv, "%04d-%02d-%02d", year, month, day);
break;
case 'g':
format_num(dsv, 2, pad, PAD_ZERO, dt_yow(dt) % 100);
break;
case 'G':
format_num(dsv, 4, pad, PAD_ZERO, dt_yow(dt));
break;
case 'H':
format_num(dsv, 2, pad, PAD_ZERO, moment_hour(mt));
break;
case 'I':
format_num(dsv, 2, pad, PAD_ZERO, moment_hour_12(mt));
break;
case 'j':
format_num(dsv, 3, pad, PAD_ZERO, dt_doy(dt));
break;
case 'k': /* extended conversion specification */
format_num(dsv, 2, pad, PAD_SPACE, moment_hour(mt));
break;
case 'l': /* extended conversion specification */
format_num(dsv, 2, pad, PAD_SPACE, moment_hour_12(mt));
break;
case 'm':
format_num(dsv, 2, pad, PAD_ZERO, month);
break;
case 'M':
format_num(dsv, 2, pad, PAD_ZERO, moment_minute(mt));
break;
case 'n':
sv_catpvn(dsv, "\n", 1);
break;
case 'N': /* extended conversion specification */
format_f(dsv, mt, width);
break;
case 'p': /* locale's equivalent of either a.m. or p.m (C locale: AM or PM) */
sv_catpv(dsv, moment_hour_meridiem(mt));
break;
case 'r': /* locale's time in a.m. and p.m. notation (C locale: %I:%M:%S %p) */
sv_catpvf(dsv, "%02d:%02d:%02d %s",
moment_hour_12(mt),
moment_minute(mt),
moment_second(mt),
moment_hour_meridiem(mt));
break;
case 'R':
sv_catpvf(dsv, "%02d:%02d",
moment_hour(mt),
moment_minute(mt));
break;
case 's': /* extended conversion specification */
format_s(dsv, mt);
break;
case 'S':
format_num(dsv, 2, pad, PAD_ZERO, moment_second(mt));
break;
case 't':
sv_catpvn(dsv, "\t", 1);
break;
case 'X': /* locale's date representation (C locale: %H:%M:%S) */
case 'T':
sv_catpvf(dsv, "%02d:%02d:%02d",
moment_hour(mt),
moment_minute(mt),
moment_second(mt));
break;
case 'u':
sv_catpvf(dsv, "%d", dt_dow(dt));
break;
case 'U':
format_num(dsv, 2, pad, PAD_ZERO, dt_week_number_sun(dt));
break;
case 'V':
format_num(dsv, 2, pad, PAD_ZERO, dt_woy(dt));
break;
case 'w':
sv_catpvf(dsv, "%d", dt_dow(dt) % 7);
break;
case 'W':
format_num(dsv, 2, pad, PAD_ZERO, dt_week_number_mon(dt));
break;
case 'y':
format_num(dsv, 2, pad, PAD_ZERO, year % 100);
break;
case 'Y':
format_num(dsv, 4, pad, PAD_ZERO, year);
break;
case 'z':
format_z(dsv, mt, zextd);
break;
case 'Z':
format_Z(dsv, mt);
break;
case '%':
sv_catpvn(dsv, "%", 1);
break;
case ':':
if (s < e && *s == 'z') {
zextd = 1;
goto label;
}
goto unknown;
case '_':
if (s < e && supports_padding_flag(*s)) {
pad = PAD_SPACE;
goto label;
}
goto unknown;
case '-':
if (s < e && supports_padding_flag(*s)) {
pad = PAD_NONE;
goto label;
}
goto unknown;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (s < e && (*s == 'f' || *s == 'N')) {
width = c - '0';
goto label;
}
if (s < e && c == '0' && supports_padding_flag(*s)) {
pad = PAD_ZERO;
goto label;
}
/* FALLTROUGH */
default:
unknown:
sv_catpvn(dsv, p, s - p);
break;
}
}
return dsv;
}
SV *
THX_moment_to_string(pTHX_ const moment_t *mt, bool reduced) {
SV *dsv;
dt_t dt;
int year, month, day, sec, ns, offset, sign;
dsv = sv_2mortal(newSV(16));
SvCUR_set(dsv, 0);
SvPOK_only(dsv);
dt = moment_local_dt(mt);
dt_to_ymd(dt, &year, &month, &day);
sv_catpvf(dsv, "%04d-%02d-%02dT%02d:%02d",
year, month, day, moment_hour(mt), moment_minute(mt));
sec = moment_second(mt);
ns = moment_nanosecond(mt);
if (!reduced || sec || ns) {
sv_catpvf(dsv, ":%02d", sec);
if (ns) {
if ((ns % 1000000) == 0) sv_catpvf(dsv, ".%03d", ns / 1000000);
else if ((ns % 1000) == 0) sv_catpvf(dsv, ".%06d", ns / 1000);
else sv_catpvf(dsv, ".%09d", ns);
}
}
offset = moment_offset(mt);
if (offset == 0)
sv_catpvn(dsv, "Z", 1);
else {
if (offset < 0)
sign = '-', offset = -offset;
else
sign = '+';
if (reduced && (offset % 60) == 0)
sv_catpvf(dsv, "%c%02d", sign, offset / 60);
else
sv_catpvf(dsv, "%c%02d:%02d", sign, offset / 60, offset % 60);
}
return dsv;
}
|