[ruby-oci8-commit] [289] trunk/ruby-oci8: * ext/oci8/ocidatetime.c: use session time zone when time zone
nobody at rubyforge.org
nobody at rubyforge.org
Wed Sep 10 10:07:07 EDT 2008
Revision: 289
Author: kubo
Date: 2008-09-10 10:07:07 -0400 (Wed, 10 Sep 2008)
Log Message:
-----------
* ext/oci8/ocidatetime.c: use session time zone when time zone
is not specified to set bind objects of timestamp with time
zone datatype.
* lib/oci8/datetime.rb: add OCI8::BindType::Util.default_timezone
to get Time or DateTime objects from bind objects of date
datatype.
* test/test_datetime.rb: fix test_datetype_duck_typing and add
test_timezone for the above changes.
Modified Paths:
--------------
trunk/ruby-oci8/ChangeLog
trunk/ruby-oci8/ext/oci8/ocidatetime.c
trunk/ruby-oci8/lib/oci8/datetime.rb
trunk/ruby-oci8/test/test_datetime.rb
Modified: trunk/ruby-oci8/ChangeLog
===================================================================
--- trunk/ruby-oci8/ChangeLog 2008-09-09 08:05:28 UTC (rev 288)
+++ trunk/ruby-oci8/ChangeLog 2008-09-10 14:07:07 UTC (rev 289)
@@ -1,3 +1,13 @@
+2008-09-10 KUBO Takehiro <kubo at jiubao.org>
+ * ext/oci8/ocidatetime.c: use session time zone when time zone
+ is not specified to set bind objects of timestamp with time
+ zone datatype.
+ * lib/oci8/datetime.rb: add OCI8::BindType::Util.default_timezone
+ to get Time or DateTime objects from bind objects of date
+ datatype.
+ * test/test_datetime.rb: fix test_datetype_duck_typing and add
+ test_timezone for the above changes.
+
2008-09-09 KUBO Takehiro <kubo at jiubao.org>
* ext/oci8/apiwrap.rb, ext/oci8/apiwrap.yml, ext/oci8/oci8.h,
ext/oci8/ocidatetime.c, lib/oci8.rb.in, lib/oci8/datetime.rb,
Modified: trunk/ruby-oci8/ext/oci8/ocidatetime.c
===================================================================
--- trunk/ruby-oci8/ext/oci8/ocidatetime.c 2008-09-09 08:05:28 UTC (rev 288)
+++ trunk/ruby-oci8/ext/oci8/ocidatetime.c 2008-09-10 14:07:07 UTC (rev 289)
@@ -132,7 +132,7 @@
have_tz ? INT2FIX(tz_minute) : Qnil);
}
-OCIDateTime *oci8_set_ocitimestamp(OCIDateTime *dttm, VALUE val)
+OCIDateTime *oci8_set_ocitimestamp(OCIDateTime *dttm, VALUE val, VALUE svc)
{
long year;
long month;
@@ -141,7 +141,10 @@
long minute;
long sec;
long fsec;
+ OraText *tz;
+ size_t tzlen;
char tz_str[32];
+ OCISession *seshp = NULL;
Check_Type(val, T_ARRAY);
if (RARRAY_LEN(val) != 9) {
@@ -184,15 +187,22 @@
}
/* time zone */
if (NIL_P(RARRAY_PTR(val)[7]) && NIL_P(RARRAY_PTR(val)[8])) {
- /* use session timezone. */
- tz_str[0] = '\0';
+ if (!NIL_P(svc)) {
+ /* use session timezone. */
+ seshp = oci8_get_oci_session(svc);
+ }
+ tz = NULL;
+ tzlen = 0;
} else {
- sprintf(tz_str, "%+02ld:%02ld",
- NUM2LONG(RARRAY_PTR(val)[7]),
- NUM2LONG(RARRAY_PTR(val)[8]));
+ snprintf(tz_str, sizeof(tz_str), "%+02ld:%02ld",
+ NUM2LONG(RARRAY_PTR(val)[7]),
+ NUM2LONG(RARRAY_PTR(val)[8]));
+ tz_str[sizeof(tz_str) - 1] = '\0';
+ tz = (OraText*)tz_str;
+ tzlen = strlen(tz_str);
}
/* construct */
- oci_lc(OCIDateTimeConstruct(oci8_envhp, oci8_errhp, dttm,
+ oci_lc(OCIDateTimeConstruct(seshp ? (void*)seshp : (void*)oci8_envhp, oci8_errhp, dttm,
(sb2)year,
(ub1)month,
(ub1)day,
@@ -200,8 +210,7 @@
(ub1)minute,
(ub1)sec,
(ub4)fsec,
- (OraText *)tz_str,
- strlen(tz_str)));
+ tz, tzlen));
return dttm;
}
@@ -235,7 +244,22 @@
static void bind_ocitimestamp_set(oci8_bind_t *obind, void *data, void **null_structp, VALUE val)
{
- oci8_set_ocitimestamp(*(OCIDateTime **)data, val);
+ oci8_base_t *stmt;
+ oci8_base_t *svcctx;
+
+ stmt = obind->base.parent;
+ if (stmt == NULL || stmt->type != OCI_HTYPE_STMT) {
+ rb_raise(rb_eRuntimeError, "oci8lib.so internal error [%s:%d, %p, %d]",
+ __FILE__, __LINE__,
+ stmt, stmt ? stmt->type : -1);
+ }
+ svcctx = stmt->parent;
+ if (svcctx == NULL || svcctx->type != OCI_HTYPE_SVCCTX) {
+ rb_raise(rb_eRuntimeError, "oci8lib.so internal error [%s:%d, %p, %d]",
+ __FILE__, __LINE__,
+ svcctx, svcctx ? svcctx->type : -1);
+ }
+ oci8_set_ocitimestamp(*(OCIDateTime **)data, val, svcctx->self);
}
static void bind_ocitimestamp_init(oci8_bind_t *obind, VALUE svc, VALUE val, VALUE length)
Modified: trunk/ruby-oci8/lib/oci8/datetime.rb
===================================================================
--- trunk/ruby-oci8/lib/oci8/datetime.rb 2008-09-09 08:05:28 UTC (rev 288)
+++ trunk/ruby-oci8/lib/oci8/datetime.rb 2008-09-10 14:07:07 UTC (rev 289)
@@ -10,6 +10,24 @@
@@time_offset = ::Time.now.utc_offset
@@datetime_offset = ::DateTime.now.offset
+ @@default_timezone = :local
+ def self.default_timezone
+ @@default_timezone
+ end
+
+ # Determines default timezone of Time and DateTime retrived from Oracle.
+ # This accepts :local or :utc. The default is :local.
+ #
+ # This parameter is used when both or either of Oracle server and client
+ # version is Oracle 8i or lower. If both versions are Oracle 9i or upper,
+ # the default timezone is determined by session timezone.
+ def self.default_timezone=(tz)
+ if tz != :local and tz != :utc
+ raise ArgumentError, "expected :local or :utc but #{tz}"
+ end
+ @@default_timezone = tz
+ end
+
private
def datetime_to_array(val, full)
@@ -83,13 +101,18 @@
def ocidate_to_datetime(ary)
year, month, day, hour, minute, sec = ary
- ::DateTime.civil(year, month, day, hour, minute, sec, @@datetime_offset)
+ if @@default_timezone == :local
+ offset = @@datetime_offset
+ else
+ offset = 0
+ end
+ ::DateTime.civil(year, month, day, hour, minute, sec, offset)
end
def ocidate_to_time(ary)
year, month, day, hour, minute, sec = ary
begin
- ::Time.local(year, month, day, hour, minute, sec)
+ ::Time.send(@@default_timezone, year, month, day, hour, minute, sec)
rescue StandardError
ocidate_to_datetime(ary)
end
@@ -277,7 +300,11 @@
class DateTime
if OCI8.oracle_client_version >= ORAVER_9_0
def self.create(con, val, param, max_array_size)
- DateTimeViaOCITimestamp.new(con, val, param, max_array_size)
+ if true # TODO: check Oracle server version
+ DateTimeViaOCITimestamp.new(con, val, param, max_array_size)
+ else
+ DateTimeViaOCIDate.new(con, val, param, max_array_size)
+ end
end
else
def self.create(con, val, param, max_array_size)
@@ -289,7 +316,11 @@
class Time
if OCI8.oracle_client_version >= ORAVER_9_0
def self.create(con, val, param, max_array_size)
- TimeViaOCITimestamp.new(con, val, param, max_array_size)
+ if true # TODO: check Oracle server version
+ TimeViaOCITimestamp.new(con, val, param, max_array_size)
+ else
+ TimeViaOCIDate.new(con, val, param, max_array_size)
+ end
end
else
def self.create(con, val, param, max_array_size)
Modified: trunk/ruby-oci8/test/test_datetime.rb
===================================================================
--- trunk/ruby-oci8/test/test_datetime.rb 2008-09-09 08:05:28 UTC (rev 288)
+++ trunk/ruby-oci8/test/test_datetime.rb 2008-09-10 14:07:07 UTC (rev 289)
@@ -183,27 +183,31 @@
def obj.day; 31; end
cursor[:in] = obj
cursor.exec
- assert_equal(DateTime.parse('2006-12-31'), cursor[:out])
+ assert_equal(DateTime.parse('2006-12-31 00:00:00' + @local_timezone), cursor[:out])
# test hour
def obj.hour; 23; end
cursor[:in] = obj
cursor.exec
- assert_equal(DateTime.parse('2006-12-31 23:00:00'), cursor[:out])
+ assert_equal(DateTime.parse('2006-12-31 23:00:00' + @local_timezone), cursor[:out])
# test min
def obj.min; 59; end
cursor[:in] = obj
cursor.exec
- assert_equal(DateTime.parse('2006-12-31 23:59:00'), cursor[:out])
+ assert_equal(DateTime.parse('2006-12-31 23:59:00' + @local_timezone), cursor[:out])
# test sec
def obj.sec; 59; end
cursor[:in] = obj
cursor.exec
- assert_equal(DateTime.parse('2006-12-31 23:59:59'), cursor[:out])
+ assert_equal(DateTime.parse('2006-12-31 23:59:59' + @local_timezone), cursor[:out])
+
+ # sec_fraction and timezone are available on Oracle 9i or later
+ return if $oracle_version < OCI8::ORAVER_9_0
+
# test sec_fraction
def obj.sec_fraction; DateTime.parse('0001-01-01 00:00:00.000001').sec_fraction * 999999 ; end
cursor[:in] = obj
cursor.exec
- assert_equal(DateTime.parse('2006-12-31 23:59:59.999999'), cursor[:out])
+ assert_equal(DateTime.parse('2006-12-31 23:59:59.999999' + @local_timezone), cursor[:out])
# test utc_offset (Time)
def obj.utc_offset; @utc_offset; end
obj.instance_variable_set(:@utc_offset, 9 * 60 * 60)
@@ -226,6 +230,67 @@
assert_equal(DateTime.parse('2006-12-31 23:59:59.999999 -05:00'), cursor[:out])
end
+ def test_timezone
+ if $oracle_version >= OCI8::ORAVER_9_0
+ # temporarily change the mapping to test OCI8::BindType::Util.default_timezone.
+ OCI8::BindType::Mapping[:date] = OCI8::BindType::TimeViaOCIDate
+ end
+ begin
+ assert_raise(ArgumentError) do
+ OCI8::BindType::Util.default_timezone = :invalid_value
+ end
+
+ [:local, :utc].each do |tz|
+ OCI8::BindType::Util.default_timezone = tz
+ @conn.exec("select sysdate, to_date('2008-01-02', 'yyyy-mm-dd') from dual") do |row|
+ row.each do |dt|
+ assert_kind_of(Time, dt)
+ assert_equal(tz, dt.utc? ? :utc : :local)
+ end
+ assert_equal(2008, row[1].year)
+ assert_equal(1, row[1].month)
+ assert_equal(2, row[1].day)
+ end
+ end
+ ensure
+ OCI8::BindType::Util.default_timezone = :local
+ if $oracle_version >= OCI8::ORAVER_9_0
+ OCI8::BindType::Mapping[:date] = OCI8::BindType::Time
+ end
+ end
+
+ if $oracle_version >= OCI8::ORAVER_9_0
+ ses_tz = nil
+ @conn.exec('select sessiontimezone from dual') do |row|
+ ses_tz = row[0]
+ end
+
+ begin
+ ['+09:00', '+00:00', '-05:00'].each do |tz|
+ @conn.exec("alter session set time_zone = '#{tz}'")
+ @conn.exec("select current_timestamp, sysdate, to_timestamp('2008-01-02', 'yyyy-mm-dd') from dual") do |row|
+ row.each do |dt|
+ case dt
+ when Time
+ assert_equal(tz, timezone_string(*((dt.utc_offset / 60).divmod 60)))
+ when DateTime
+ assert_equal(tz, dt.zone)
+ else
+ flunk "unexpedted type #{dt.class}"
+ end
+ end
+ assert_equal(2008, row[2].year)
+ assert_equal(1, row[2].month)
+ assert_equal(2, row[2].day)
+ end
+ end
+ ensure
+ @conn.exec("alter session set time_zone = '#{ses_tz}'")
+ end
+ else
+ end
+ end
+
def test_interval_ym_select
return if $oracle_version < OCI8::ORAVER_9_0
More information about the ruby-oci8-commit
mailing list