[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