1
0
mirror of https://github.com/stedolan/jq.git synced 2024-05-11 05:55:39 +00:00

Add date builtins (fix #364)

Windows support for strptime() is missing.  We can add a copy of one
from a BSD later.
This commit is contained in:
Nicolas Williams
2015-03-06 00:14:15 -06:00
parent b82c231900
commit a4b9552c82
5 changed files with 104 additions and 2 deletions

View File

@@ -1,4 +1,6 @@
#define _XOPEN_SOURCE
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#ifdef HAVE_ONIGURUMA
@@ -6,6 +8,7 @@
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "builtin.h"
#include "compile.h"
#include "jq_parser.h"
@@ -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},
@@ -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

View File

@@ -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],

View File

@@ -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: "`==`, `!=`"

6
jv.h
View File

@@ -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) \

View File

@@ -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