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