I'm looking for a plain C counterpart for date.js date.parse()
.
That is, something that understands "week ago" or "yesterday" as input. English-only is OK.
Note: a library should not be licensed under GPL, so Git's date.c
or parser for GNU date -d
wouldn't do. BTW, if you wonder why wouldn't I just sit down and code this, go and look at the source of mentioned libraries...
2 Answers 2
The following solution is not exactly what you've asked for but I hope that despite not being a plain C answer it will cover your needs. Reinventing the wheel isn't a way to go so let's use date.js in C by running it with SpiderMonkey, the Mozilla JavaScript engine.
Here's how I did it. I've begun with downloading date.js and translating it into a const char*
named code
defined in date.js.h
.
( \
echo 'const char *code =' ; \
curl https://datejs.googlecode.com/files/date.js | \
sed -e 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/\r\?$/\\n"/'; \
echo ';' \
) > date.js.h
Then I took the JSAPI's Hello, World! as a starting point.
#include "jsapi.h"
#include "date.js.h"
static JSClass global_class = { "global", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS };
void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
fprintf(stderr, "%s:%u:%s\n",
report->filename ? report->filename : "<no filename>",
(unsigned int) report->lineno, message);
}
int main(int argc, const char *argv[]) {
JSRuntime *rt;
JSContext *cx;
JSObject *global;
rt = JS_NewRuntime(8L * 1024L * 1024L);
if (rt == NULL) return 1;
cx = JS_NewContext(rt, 8192);
if (cx == NULL) return 1;
JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT | JSOPTION_METHODJIT);
JS_SetVersion(cx, JSVERSION_LATEST);
JS_SetErrorReporter(cx, reportError);
global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
if (global == NULL) return 1;
if (!JS_InitStandardClasses(cx, global)) return 1;
/* Here's where the interesting stuff is starting to take place.
* Begin by evaluating sources of date.js */
jsval out;
if (!JS_EvaluateScript(cx, global, code, strlen(code), "code", 1, &out))
return 1;
/* Now create a call to Date.parse and evaluate it. The return value should
* be a timestamp of a given date. If no errors occur convert the timestamp
* to a double and print it. */
const int buflen = 1024;
char parse[buflen + 1];
snprintf(parse, buflen, "Date.parse(\"%s\").getTime();", argv[1]);
if (!JS_EvaluateScript(cx, global, parse, strlen(parse), "parse", 1, &out))
return 1;
double val;
JS_ValueToNumber(cx, out, &val);
printf("%i\n", (int) (val / 1000));
/* Finally, clean everything up. */
JS_DestroyContext(cx);
JS_DestroyRuntime(rt);
JS_ShutDown();
return 0;
}
Here's how it works in practice.
$ time ./parse "week ago"
1331938800
0.01user 0.00system 0:00.02elapsed 92%CPU (0avgtext+0avgdata 6168maxresident)k
0inputs+0outputs (0major+1651minor)pagefaults 0swaps
$ time ./parse yesterday
1332457200
0.01user 0.00system 0:00.02elapsed 84%CPU (0avgtext+0avgdata 6168maxresident)k
0inputs+0outputs (0major+1653minor)pagefaults 0swaps
As you can see it's quite fast and you could significantly increase its performance by reusing the initially created context for all subsequent calls to Date.parse
.
Speaking of licensing issues, date.js is available under terms of MIT and SpiderMonkey is available under MPL 1.1, GPL 2.0 or LGPL 2.1. Linking it dynamically satisfies the non-GPL requirement.
TL;DR: git clone https://gist.github.com/2180739.git && cd 2180739 && make && ./parse yesterday
-
3Hey, clever trick, thanks. I think it will even work for my case, as I do not need high performance. I'm leaving the question open in case a honest solution pops up :-)Alexander Gladysh– Alexander Gladysh03/24/2012 16:13:07Commented Mar 24, 2012 at 16:13
Date formatting is pretty gruesome, there is no easy way of doing this. You need to take into account day and month names of the selected language, then make sure you receive the data in a specific format: "dd/mm/yyyy", "day mon, yyyy" and so on. Also, as you say, you need to interpret some specific keywords, thus you need access to the current timestamp (date, time, or date&time) on the machine.
Hoping you need that for Linux, I think you can start reading from here: Convert textual time and date information back
Or you can simply tokenize your input string, by splitting it using some predefined separators (comma, slash, minus, space etc.), trimming the spaces of the tokens, and then implement an automata to handle the list of tokens and build your date variable. Make sure you add some restrictions for the input date format, and throw errors for wrong or incompatible tokens.
-
Thanks, but
getdate
does not understandyesterday
etc. As for how to parse — I get that, but this question is about an existing solution. I'd hate to do it myself and hit all pitfalls — judging from existing GPL-ed code there are plenty.Alexander Gladysh– Alexander Gladysh03/24/2012 07:01:34Commented Mar 24, 2012 at 7:01
date -d
sources, for example, you'll see that the most complex thing is not a parser.