Skip to content

Instantly share code, notes, and snippets.

@deepfryed
Created August 27, 2010 00:14
Show Gist options
  • Save deepfryed/552507 to your computer and use it in GitHub Desktop.
Save deepfryed/552507 to your computer and use it in GitHub Desktop.
#include <ruby/ruby.h>
#include <time.h>
#include <unistd.h>
#define CONST_GET(scope, constant) rb_const_get(scope, rb_intern(constant))
VALUE day_secs;
VALUE cDateTime;
VALUE fNewBang;
uint64_t epoch_ajd_n, epoch_ajd_d;
// calculates local offset at a given time, including any DST variations.
size_t client_tzoffset(struct tm *given) {
struct tm tm;
uint64_t utc, local;
memcpy(&tm, given, sizeof(tm));
tm.tm_isdst = -1;
local = mktime(&tm);
gmtime_r(&local, &tm);
tm.tm_isdst = -1;
utc = mktime(&tm);
return local-utc;
}
VALUE rb_datetime_parse_iso8601(VALUE self, VALUE str) {
struct tm tm;
uint64_t epoch, adjust, offset, tzoffset;
const char* data = RSTRING_PTR(str);
double usec = 0;
char tzsign = 0;
int tzhour = 0, tzmin = 0;
memset(&tm, 0, sizeof(struct tm));
if (strchr(data, '.')) {
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%lf%c%02d:%02d",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
&usec, &tzsign, &tzhour, &tzmin);
}
else {
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%c%02d:%02d",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
&tzsign, &tzhour, &tzmin);
}
tm.tm_year -= 1900;
tm.tm_mon -= 1;
tm.tm_isdst = -1;
if (tm.tm_mday > 0) {
epoch = mktime(&tm);
adjust = client_tzoffset(&tm);
offset = adjust;
if (tzsign == '+' || tzsign == '-') {
offset = tzsign == '+' ?
(time_t)tzhour * 3600 + (time_t)tzmin * 60
: (time_t)tzhour * -3600 + (time_t)tzmin * -60;
}
VALUE ajd = rb_rational_new(ULONG2NUM(epoch_ajd_n + epoch + adjust - offset), day_secs);
return rb_funcall(cDateTime, fNewBang, 3, ajd, rb_rational_new(INT2FIX(offset), day_secs), INT2NUM(2299161));
}
return Qnil;
}
void Init_parser(void) {
rb_require("date");
// astronomical julian date for 1970-01-01 00:00:00 is 2440587.5
// epoch_ajd = Rational(epoch_ajd_n, epoch_ajd_d)
epoch_ajd_d = 86400;
epoch_ajd_n = (2440587L*2+1) * 43200L;
day_secs = INT2FIX(86400);
fNewBang = rb_intern("new!");
cDateTime = CONST_GET(rb_mKernel, "DateTime");
rb_define_module_function(cDateTime, "parse_iso8601", RUBY_METHOD_FUNC(rb_datetime_parse_iso8601), 1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment