Skip to content

Commit

Permalink
Add date builtins (fix #364)
Browse files Browse the repository at this point in the history
Windows support for strptime() is missing.  We can add a copy of one
from a BSD later.
  • Loading branch information
nicowilliams committed Mar 6, 2015
1 parent b82c231 commit a4b9552
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 2 deletions.
73 changes: 73 additions & 0 deletions builtin.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#define _XOPEN_SOURCE
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#ifdef HAVE_ONIGURUMA
#include <oniguruma.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "builtin.h"
#include "compile.h"
#include "jq_parser.h"
Expand Down Expand Up @@ -912,6 +915,74 @@ static jv f_stderr(jq_state *jq, jv input) {
return input;
}

#ifdef HAVE_STRPTIME
static jv f_strptime(jq_state *jq, jv a, jv b) {
if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING)
return jv_invalid_with_msg(jv_string("strptime/2 requires string inputs and arguments"));

struct tm tm;
const char *input = jv_string_value(a);
const char *fmt = jv_string_value(b);
const char *end = strptime(input, fmt, &tm);

if (end == NULL || (*end != '\0' && !isspace(*end))) {
jv e = jv_invalid_with_msg(jv_string_fmt("date \"%s\" does not match format \"%s\"", input, fmt));
jv_free(a);
jv_free(b);
return e;
}
jv_free(a);
jv_free(b);
jv r = JV_ARRAY(jv_number(tm.tm_year + 1900),
jv_number(tm.tm_mon),
jv_number(tm.tm_mday),
jv_number(tm.tm_hour),
jv_number(tm.tm_min),
jv_number(tm.tm_sec),
jv_number(tm.tm_wday),
jv_number(tm.tm_yday));
if (*end != '\0')
r = jv_array_append(r, jv_string(end));
return r;
}
#else
static jv f_strptime(jq_state *jq, jv a, jv b) {
return jv_invalid_with_msg(jv_string("strptime/2 not implemented on this platform"));
}
#endif

#define TO_TM_FIELD(t, j, i, k) \
do { \
jv n = jv_array_get(jv_copy(j), (i)); \
if (jv_get_kind(n) != (k)) \
return jv_invalid_with_msg(jv_string("mktime() requires a 'gmtime' input (an array inputs of 8 numeric values)")); \
t = jv_number_value(n); \
jv_free(n); \
} while (0)

static jv f_mktime(jq_state *jq, jv a) {
if (jv_get_kind(a) != JV_KIND_ARRAY)
return jv_invalid_with_msg(jv_string("mktime() requires array inputs"));
if (jv_array_length(jv_copy(a)) < 6)
return jv_invalid_with_msg(jv_string("mktime() requires a 'gmtime' input (an array inputs of 8 numeric values)"));
struct tm tm;
memset(&tm, 0, sizeof(tm));
TO_TM_FIELD(tm.tm_year, a, 0, JV_KIND_NUMBER);
TO_TM_FIELD(tm.tm_mon, a, 1, JV_KIND_NUMBER);
TO_TM_FIELD(tm.tm_mday, a, 2, JV_KIND_NUMBER);
TO_TM_FIELD(tm.tm_hour, a, 3, JV_KIND_NUMBER);
TO_TM_FIELD(tm.tm_min, a, 4, JV_KIND_NUMBER);
TO_TM_FIELD(tm.tm_sec, a, 5, JV_KIND_NUMBER);
tm.tm_year -= 1900;
jv_free(a);
time_t t = mktime(&tm);
if (t == (time_t)-1)
return jv_invalid_with_msg(jv_string("invalid gmtime representation"));
return jv_number(t);
}

#undef TO_TM_FIELD


#define LIBM_DD(name) \
{(cfunction_ptr)f_ ## name, "_" #name, 1},
Expand Down Expand Up @@ -971,6 +1042,8 @@ static const struct cfunction function_list[] = {
{(cfunction_ptr)f_input, "_input", 1},
{(cfunction_ptr)f_debug, "debug", 1},
{(cfunction_ptr)f_stderr, "stderr", 1},
{(cfunction_ptr)f_strptime, "strptime", 2},
{(cfunction_ptr)f_mktime, "mktime", 1},
};
#undef LIBM_DD

Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ AM_CONDITIONAL([ENABLE_DOCS], [test "x$enable_docs" != xno])

AC_FIND_FUNC([isatty], [c], [#include <unistd.h>], [0])
AC_FIND_FUNC([_isatty], [c], [#include <io.h>], [0])
AC_FIND_FUNC([strptime], [c], [#include <time.h>], [0])

AC_ARG_ENABLE([pthread-tls],
[AC_HELP_STRING([--enable-pthread-tls],
Expand Down
22 changes: 22 additions & 0 deletions docs/content/3.manual/manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1591,6 +1591,28 @@ sections:
input: "\"O'Hara's Ale\""
output: ["\"echo 'O'\\\\''Hara'\\\\''s Ale'\""]

- title: "Dates"
body: |
The `strptime(fmt)` function parses input strings matching the
`fmt` argument. The output is an array of eight numbers: the
year, the month, the day of the month, the hour of the day,
the minute of the hour, the second of the minute, the day of
the week, and the day of the year, all zero-based except for
the year and the month, which are one-based.
The `mktime` function consumes outputs from `strptime/1` and
produces the time in seconds since the Unix epoch.
examples:
- program: 'strptime'
input: '"2015-03-05 23:51:47Z"'
output: ['[2015,2,5,23,51,47,4,63]']

- program: 'strptime|mktime'
input: '"2015-03-05 23:51:47Z"'
output: ['1425621107']

- title: Conditionals and Comparisons
entries:
- title: "`==`, `!=`"
Expand Down
6 changes: 4 additions & 2 deletions jv.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,11 @@ jv jv_array_indexes(jv, jv);
#define JV_ARRAY_5(e1,e2,e3,e4,e5) (jv_array_append(JV_ARRAY_4(e1,e2,e3,e4),e5))
#define JV_ARRAY_6(e1,e2,e3,e4,e5,e6) (jv_array_append(JV_ARRAY_5(e1,e2,e3,e4,e5),e6))
#define JV_ARRAY_7(e1,e2,e3,e4,e5,e6,e7) (jv_array_append(JV_ARRAY_6(e1,e2,e3,e4,e5,e6),e7))
#define JV_ARRAY_IDX(_1,_2,_3,_4,_5,_6,_7,NAME,...) NAME
#define JV_ARRAY_8(e1,e2,e3,e4,e5,e6,e7,e8) (jv_array_append(JV_ARRAY_7(e1,e2,e3,e4,e5,e6,e7),e8))
#define JV_ARRAY_9(e1,e2,e3,e4,e5,e6,e7,e8,e9) (jv_array_append(JV_ARRAY_8(e1,e2,e3,e4,e5,e6,e7,e8),e9))
#define JV_ARRAY_IDX(_1,_2,_3,_4,_5,_6,_7,_8,_9,NAME,...) NAME
#define JV_ARRAY(...) \
JV_ARRAY_IDX(__VA_ARGS__, JV_ARRAY_7, JV_ARRAY_6, JV_ARRAY_5, JV_ARRAY_4, JV_ARRAY_3, JV_ARRAY_2, JV_ARRAY_1)(__VA_ARGS__)
JV_ARRAY_IDX(__VA_ARGS__, JV_ARRAY_9, JV_ARRAY_8, JV_ARRAY_7, JV_ARRAY_6, JV_ARRAY_5, JV_ARRAY_4, JV_ARRAY_3, JV_ARRAY_2, JV_ARRAY_1)(__VA_ARGS__)

#ifdef __GNUC__
#define JV_PRINTF_LIKE(fmt_arg_num, args_num) \
Expand Down
4 changes: 4 additions & 0 deletions tests/all.test
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,10 @@ bsearch(4)
[1,2,3]
-4

[strptime("%Y-%m-%d %H:%M:%SZ")|(.,mktime)]
"2015-03-05 23:51:47Z"
[[2015,2,5,23,51,47,4,63],1425621107]

# module system
import "a" as foo; import "b" as bar; def fooa: foo::a; [fooa, bar::a, bar::b, foo::a]
null
Expand Down

0 comments on commit a4b9552

Please sign in to comment.