convert :timezone option into two new ones

:database_timezone - the timezone (:utc or :local) Mysql2 will assume time/datetime fields are stored in the db. This modifies what initial timezone your Time objects will be in when creating them from libmysql in C
and
:application_timezone - the timezone (:utc or :local) you'd finally like the Time objects converted to before you get them
This commit is contained in:
Brian Lopez 2010-08-05 22:50:45 -07:00
parent 1bdf44ce7f
commit ad34357e57
2 changed files with 57 additions and 25 deletions

View File

@ -8,9 +8,10 @@ VALUE cMysql2Result;
VALUE cBigDecimal, cDate, cDateTime; VALUE cBigDecimal, cDate, cDateTime;
extern VALUE mMysql2, cMysql2Client, cMysql2Error; extern VALUE mMysql2, cMysql2Client, cMysql2Error;
static VALUE intern_encoding_from_charset; static VALUE intern_encoding_from_charset;
static ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code; static ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code,
static ID sym_symbolize_keys, sym_as, sym_array, sym_timezone, sym_local, sym_utc, intern_localtime;
sym_cast_booleans; static ID sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone,
sym_local, sym_utc, sym_cast_booleans;
static ID intern_merge; static ID intern_merge;
static void rb_mysql_result_mark(void * wrapper) { static void rb_mysql_result_mark(void * wrapper) {
@ -87,7 +88,7 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int
return rb_field; return rb_field;
} }
static VALUE rb_mysql_result_fetch_row(VALUE self, ID timezone, int symbolizeKeys, int asArray, int castBool) { static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezone, int symbolizeKeys, int asArray, int castBool) {
VALUE rowVal; VALUE rowVal;
mysql2_result_wrapper * wrapper; mysql2_result_wrapper * wrapper;
MYSQL_ROW row; MYSQL_ROW row;
@ -154,7 +155,14 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID timezone, int symbolizeKey
case MYSQL_TYPE_TIME: { // TIME field case MYSQL_TYPE_TIME: { // TIME field
int hour, min, sec, tokens; int hour, min, sec, tokens;
tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec); tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec);
val = rb_funcall(rb_cTime, timezone, 6, INT2NUM(2000), INT2NUM(1), INT2NUM(1), INT2NUM(hour), INT2NUM(min), INT2NUM(sec)); val = rb_funcall(rb_cTime, db_timezone, 6, INT2NUM(2000), INT2NUM(1), INT2NUM(1), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
if (!NIL_P(app_timezone)) {
if (app_timezone == intern_local) {
val = rb_funcall(val, intern_localtime, 0);
} else { // utc
val = rb_funcall(val, intern_utc, 0);
}
}
break; break;
} }
case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field
@ -168,7 +176,14 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID timezone, int symbolizeKey
rb_raise(cMysql2Error, "Invalid date: %s", row[i]); rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
val = Qnil; val = Qnil;
} else { } else {
val = rb_funcall(rb_cTime, timezone, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec)); val = rb_funcall(rb_cTime, db_timezone, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
if (!NIL_P(app_timezone)) {
if (app_timezone == intern_local) {
val = rb_funcall(val, intern_localtime, 0);
} else { // utc
val = rb_funcall(val, intern_utc, 0);
}
}
} }
} }
break; break;
@ -267,8 +282,8 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
} }
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
VALUE defaults, opts, block, timezoneVal; VALUE defaults, opts, block;
ID timezone; ID db_timezone, app_timezone, dbTz, appTz;
mysql2_result_wrapper * wrapper; mysql2_result_wrapper * wrapper;
unsigned long i; unsigned long i;
int symbolizeKeys = 0, asArray = 0, castBool = 0; int symbolizeKeys = 0, asArray = 0, castBool = 0;
@ -294,14 +309,28 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
castBool = 1; castBool = 1;
} }
timezoneVal = rb_hash_aref(opts, sym_timezone); dbTz = rb_hash_aref(opts, sym_database_timezone);
if (timezoneVal == sym_local) { if (dbTz == sym_local) {
timezone = intern_local; db_timezone = intern_local;
} else if (timezoneVal == sym_utc) { } else if (dbTz == sym_utc) {
timezone = intern_utc; db_timezone = intern_utc;
} else { } else {
rb_warn(":timezone config option must be :utc or :local - defaulting to :local"); if (!NIL_P(dbTz)) {
timezone = intern_local; rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
}
db_timezone = intern_local;
}
appTz = rb_hash_aref(opts, sym_application_timezone);
if (appTz == sym_local) {
app_timezone = intern_local;
} else if (appTz == sym_utc) {
app_timezone = intern_utc;
} else {
if (!NIL_P(appTz)) {
rb_warn(":application_timezone option must be :utc or :local - defaulting to :local");
}
app_timezone = intern_local;
} }
if (wrapper->lastRowProcessed == 0) { if (wrapper->lastRowProcessed == 0) {
@ -327,7 +356,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
if (i < rowsProcessed) { if (i < rowsProcessed) {
row = rb_ary_entry(wrapper->rows, i); row = rb_ary_entry(wrapper->rows, i);
} else { } else {
row = rb_mysql_result_fetch_row(self, timezone, symbolizeKeys, asArray, castBool); row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool);
rb_ary_store(wrapper->rows, i, row); rb_ary_store(wrapper->rows, i, row);
wrapper->lastRowProcessed++; wrapper->lastRowProcessed++;
} }
@ -379,18 +408,20 @@ void init_mysql2_result() {
intern_encoding_from_charset = rb_intern("encoding_from_charset"); intern_encoding_from_charset = rb_intern("encoding_from_charset");
intern_encoding_from_charset_code = rb_intern("encoding_from_charset_code"); intern_encoding_from_charset_code = rb_intern("encoding_from_charset_code");
intern_new = rb_intern("new"); intern_new = rb_intern("new");
intern_utc = rb_intern("utc"); intern_utc = rb_intern("utc");
intern_local = rb_intern("local"); intern_local = rb_intern("local");
intern_merge = rb_intern("merge"); intern_merge = rb_intern("merge");
intern_localtime = rb_intern("localtime");
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys")); sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
sym_as = ID2SYM(rb_intern("as")); sym_as = ID2SYM(rb_intern("as"));
sym_array = ID2SYM(rb_intern("array")); sym_array = ID2SYM(rb_intern("array"));
sym_timezone = ID2SYM(rb_intern("timezone"));
sym_local = ID2SYM(rb_intern("local")); sym_local = ID2SYM(rb_intern("local"));
sym_utc = ID2SYM(rb_intern("utc")); sym_utc = ID2SYM(rb_intern("utc"));
sym_cast_booleans = ID2SYM(rb_intern("cast_booleans")); sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
sym_database_timezone = ID2SYM(rb_intern("database_timezone"));
sym_application_timezone = ID2SYM(rb_intern("application_timezone"));
#ifdef HAVE_RUBY_ENCODING_H #ifdef HAVE_RUBY_ENCODING_H
binaryEncoding = rb_enc_find("binary"); binaryEncoding = rb_enc_find("binary");

View File

@ -2,11 +2,12 @@ module Mysql2
class Client class Client
attr_reader :query_options attr_reader :query_options
@@default_query_options = { @@default_query_options = {
:symbolize_keys => false,
:async => false,
:as => :hash, :as => :hash,
:timezone => :local, :async => false,
:cast_booleans => false :cast_booleans => false,
:symbolize_keys => false,
:database_timezone => :local, # timezone Mysql2 will assume datetime objects are stored in
:application_timezone => :local # timezone Mysql2 will convert to before handing the object back to the caller
} }
def initialize(opts = {}) def initialize(opts = {})