aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--package.json1
-rw-r--r--src/loslib.js302
3 files changed, 283 insertions, 21 deletions
diff --git a/NEWS b/NEWS
index fbb983b..2e13a83 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,7 @@
UNRELEASED
- More node 6 compatibility fixes
+ - Use own strftime code (drops strftime npm package dependency)
0.1.1 - 2018-04-03
diff --git a/package.json b/package.json
index 9988883..ca4c18f 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,6 @@
"dependencies": {
"readline-sync": "^1.4.9",
"sprintf-js": "^1.1.1",
- "strftime": "^0.10.0",
"tmp": "^0.0.33"
},
"sideEffects": false,
diff --git a/src/loslib.js b/src/loslib.js
index 6240ed9..5768113 100644
--- a/src/loslib.js
+++ b/src/loslib.js
@@ -42,16 +42,13 @@ const {
} = require('./lauxlib.js');
const {
luastring_eq,
- luastring_indexOf,
to_jsstring,
to_luastring
} = require("./fengaricore.js");
-const strftime = require('strftime');
-
/* options for ANSI C 89 (only 1-char options) */
-const L_STRFTIMEC89 = to_luastring("aAbBcdHIjmMpSUwWxXyYZ%");
-const LUA_STRFTIMEOPTIONS = L_STRFTIMEC89;
+// const L_STRFTIMEC89 = to_luastring("aAbBcdHIjmMpSUwWxXyYZ%");
+// const LUA_STRFTIMEOPTIONS = L_STRFTIMEC89;
/* options for ISO C 99 and POSIX */
// const L_STRFTIMEC99 = to_luastring("aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%||EcECExEXEyEYOdOeOHOIOmOMOSOuOUOVOwOWOy"); /* two-char options */
@@ -61,6 +58,18 @@ const LUA_STRFTIMEOPTIONS = L_STRFTIMEC89;
// const L_STRFTIMEWIN = to_luastring("aAbBcdHIjmMpSUwWxXyYzZ%||#c#x#d#H#I#j#m#M#S#U#w#W#y#Y"); /* two-char options */
// const LUA_STRFTIMEOPTIONS = L_STRFTIMEWIN;
+/* options for our own strftime implementation
+ - should be superset of C89 options for compat
+ - missing from C99:
+ - ISO 8601 week specifiers: gGV
+ - > single char specifiers
+ - beyond C99:
+ - %k: TZ extension: space-padded 24-hour
+ - %l: TZ extension: space-padded 12-hour
+ - %P: GNU extension: lower-case am/pm
+*/
+const LUA_STRFTIMEOPTIONS = to_luastring("aAbBcCdDeFhHIjklmMnpPrRStTuUwWxXyYzZ%");
+
const setfield = function(L, key, value) {
lua_pushinteger(L, value);
@@ -100,7 +109,272 @@ const getfield = function(L, key, d, delta) {
return res;
};
-const checkoption = function(L, conv, i, buff) {
+
+const locale = {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ].map((s) => to_luastring(s)),
+ shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((s) => to_luastring(s)),
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"].map((s) => to_luastring(s)),
+ shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].map((s) => to_luastring(s)),
+ AM: to_luastring("AM"),
+ PM: to_luastring("PM"),
+ am: to_luastring("am"),
+ pm: to_luastring("pm"),
+ formats: {
+ c: to_luastring("%a %b %e %H:%M:%S %Y"),
+ D: to_luastring("%m/%d/%y"),
+ F: to_luastring("%Y-%m-%d"),
+ R: to_luastring("%H:%M"),
+ r: to_luastring("%I:%M:%S %p"),
+ T: to_luastring("%H:%M:%S"),
+ X: to_luastring("%T"),
+ x: to_luastring("%D")
+ }
+};
+
+const week_number = function(date, start_of_week) {
+ // This works by shifting the weekday back by one day if we
+ // are treating Monday as the first day of the week.
+ let weekday = date.getDay();
+ if (start_of_week === 'monday') {
+ if (weekday === 0) // Sunday
+ weekday = 6;
+ else
+ weekday--;
+ }
+ let yday = (date - new Date(date.getFullYear(), 0, 1)) / 86400000;
+ return Math.floor((yday + 7 - weekday) / 7);
+};
+
+const push_pad_2 = function(b, n, pad) {
+ if (n < 10)
+ luaL_addchar(b, pad);
+ luaL_addstring(b, to_luastring(String(n)));
+};
+
+const strftime = function(L, b, s, date) {
+ let i = 0;
+ while (i < s.length) {
+ if (s[i] !== 37 /* % */) { /* not a conversion specifier? */
+ luaL_addchar(b, s[i++]);
+ } else {
+ i++; /* skip '%' */
+ let len = checkoption(L, s, i);
+ /* each `case` has an example output above it for the UTC epoch */
+ switch(s[i]) {
+ // '%'
+ case 37 /* % */:
+ luaL_addchar(b, 37);
+ break;
+
+ // 'Thursday'
+ case 65 /* A */:
+ luaL_addstring(b, locale.days[date.getDay()]);
+ break;
+
+ // 'January'
+ case 66 /* B */:
+ luaL_addstring(b, locale.months[date.getMonth()]);
+ break;
+
+ // '19'
+ case 67 /* C */:
+ push_pad_2(b, Math.floor(date.getFullYear() / 100), 48 /* 0 */);
+ break;
+
+ // '01/01/70'
+ case 68 /* D */:
+ strftime(L, b, locale.formats.D, date);
+ break;
+
+ // '1970-01-01'
+ case 70 /* F */:
+ strftime(L, b, locale.formats.F, date);
+ break;
+
+ // '00'
+ case 72 /* H */:
+ push_pad_2(b, date.getHours(), 48 /* 0 */);
+ break;
+
+ // '12'
+ case 73 /* I */:
+ push_pad_2(b, (date.getHours() + 11) % 12 + 1, 48 /* 0 */);
+ break;
+
+ // '00'
+ case 77 /* M */:
+ push_pad_2(b, date.getMinutes(), 48 /* 0 */);
+ break;
+
+ // 'am'
+ case 80 /* P */:
+ luaL_addstring(b, date.getHours() < 12 ? locale.am : locale.pm);
+ break;
+
+ // '00:00'
+ case 82 /* R */:
+ strftime(L, b, locale.formats.R, date);
+ break;
+
+ // '00'
+ case 83 /* S */:
+ push_pad_2(b, date.getSeconds(), 48 /* 0 */);
+ break;
+
+ // '00:00:00'
+ case 84 /* T */:
+ strftime(L, b, locale.formats.T, date);
+ break;
+
+ // '00'
+ case 85 /* U */:
+ push_pad_2(b, week_number(date, "sunday"), 48 /* 0 */);
+ break;
+
+ // '00'
+ case 87 /* W */:
+ push_pad_2(b, week_number(date, "monday"), 48 /* 0 */);
+ break;
+
+ // '16:00:00'
+ case 88 /* X */:
+ strftime(L, b, locale.formats.X, date);
+ break;
+
+ // '1970'
+ case 89 /* Y */:
+ luaL_addstring(b, to_luastring(String(date.getFullYear())));
+ break;
+
+ // 'GMT'
+ case 90 /* Z */: {
+ let tzString = date.toString().match(/\(([\w\s]+)\)/);
+ if (tzString)
+ luaL_addstring(b, to_luastring(tzString[1]));
+ break;
+ }
+
+ // 'Thu'
+ case 97 /* a */:
+ luaL_addstring(b, locale.shortDays[date.getDay()]);
+ break;
+
+ // 'Jan'
+ case 98 /* b */:
+ case 104 /* h */:
+ luaL_addstring(b, locale.shortMonths[date.getMonth()]);
+ break;
+
+ // ''
+ case 99 /* c */:
+ strftime(L, b, locale.formats.c, date);
+ break;
+
+ // '01'
+ case 100 /* d */:
+ push_pad_2(b, date.getDate(), 48 /* 0 */);
+ break;
+
+ // ' 1'
+ case 101 /* e */:
+ push_pad_2(b, date.getDate(), 32 /* space */);
+ break;
+
+ // '000'
+ case 106 /* j */: {
+ let yday = Math.floor((date - new Date(date.getFullYear(), 0, 1)) / 86400000);
+ if (yday < 100) {
+ if (yday < 10)
+ luaL_addchar(b, 48 /* 0 */);
+ luaL_addchar(b, 48 /* 0 */);
+ }
+ luaL_addstring(b, to_luastring(String(yday)));
+ break;
+ }
+
+ // ' 0'
+ case 107 /* k */:
+ push_pad_2(b, date.getHours(), 32 /* space */);
+ break;
+
+ // '12'
+ case 108 /* l */:
+ push_pad_2(b, (date.getHours() + 11) % 12 + 1, 32 /* space */);
+ break;
+
+ // '01'
+ case 109 /* m */:
+ push_pad_2(b, date.getMonth() + 1, 48 /* 0 */);
+ break;
+
+ // '\n'
+ case 110 /* n */:
+ luaL_addchar(b, 10);
+ break;
+
+ // 'AM'
+ case 112 /* p */:
+ luaL_addstring(b, date.getHours() < 12 ? locale.AM : locale.PM);
+ break;
+
+ // '12:00:00 AM'
+ case 114 /* r */:
+ strftime(L, b, locale.formats.r, date);
+ break;
+
+ // '0'
+ case 115 /* s */:
+ luaL_addstring(b, to_luastring(String(Math.floor(date / 1000))));
+ break;
+
+ // '\t'
+ case 116 /* t */:
+ luaL_addchar(b, 8);
+ break;
+
+ // '4'
+ case 117 /* u */: {
+ let day = date.getDay();
+ luaL_addstring(b, to_luastring(String(day === 0 ? 7 : day)));
+ break;
+ }
+
+ // '4'
+ case 119 /* w */:
+ luaL_addstring(b, to_luastring(String(date.getDay())));
+ break;
+
+ // '12/31/69'
+ case 120 /* x */:
+ strftime(L, b, locale.formats.x, date);
+ break;
+
+ // '70'
+ case 121 /* y */:
+ push_pad_2(b, date.getFullYear() % 100, 48 /* 0 */);
+ break;
+
+ // '+0000'
+ case 122 /* z */: {
+ let off = date.getTimezoneOffset();
+ if (off > 0) {
+ luaL_addchar(b, 45 /* - */);
+ } else {
+ off = -off;
+ luaL_addchar(b, 43 /* + */);
+ }
+ push_pad_2(b, Math.floor(off/60), 48 /* 0 */);
+ push_pad_2(b, off % 60, 48 /* 0 */);
+ break;
+ }
+ }
+ i += len;
+ }
+ }
+};
+
+
+const checkoption = function(L, conv, i) {
let option = LUA_STRFTIMEOPTIONS;
let o = 0;
let oplen = 1; /* length of options being checked */
@@ -108,8 +382,7 @@ const checkoption = function(L, conv, i, buff) {
if (option[o] === '|'.charCodeAt(0)) /* next block? */
oplen++; /* will check options with next length (+1) */
else if (luastring_eq(conv.subarray(i, i+oplen), option.subarray(o, o+oplen))) { /* match? */
- buff.set(conv.subarray(i, i+oplen)); /* copy valid option to buffer */
- return i + oplen; /* return next item */
+ return oplen; /* return length */
}
}
luaL_argerror(L, 1,
@@ -137,18 +410,7 @@ const os_date = function(L) {
cc[0] = "%".charCodeAt(0);
let b = new luaL_Buffer();
luaL_buffinit(L, b);
- while (i < s.length) {
- if (s[i] !== '%'.charCodeAt(0)) { /* not a conversion specifier? */
- luaL_addchar(b, s[i++]);
- } else {
- i++; /* skip '%' */
- i = checkoption(L, s, i, cc.subarray(1)); /* copy specifier to 'cc' */
- let len = luastring_indexOf(cc, 0);
- if (len === -1) len = void 0;
- let buff = strftime(to_jsstring(cc, 0, len), stm);
- luaL_addstring(b, to_luastring(buff));
- }
- }
+ strftime(L, b, s, stm);
luaL_pushresult(b);
}
return 1;