Skip to content

Commit 90c240c

Browse files
committed
We now support your native language date/time formats automatically!
1 parent 0b073a7 commit 90c240c

File tree

6 files changed

+70
-11
lines changed

6 files changed

+70
-11
lines changed

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11

22
* 3.1.0 *
33

4+
* Quote most time objects to use ISO8601 format to be multi-language dateformat compatible. The [datetime] data type is
5+
automatically limited to milliseconds while [time] & [datetimeoffset] have full support. Even included a Date/Time
6+
ActiveSupport formatter that is used per the language settings of the connection.
7+
48
* Include a visit_Arel_Nodes_UpdateStatement method in our Arel visitor to add a limit/top for update
59
that has order and no limit/top. https://github.com/rails/rails/commit/787194ee43ab1fb0a7dc8bfbbfbd5079b047d833
610

README.rdoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server
77
== What's New
88

99
* Rails 3.1 prepared statement support leverages cached query plans.
10+
* We now support your native language date/time formats automatically!
1011
* Default unicode datatypes! Disable with #enable_default_unicode_types to false.
1112
* New #lowercase_schema_reflection configuration option for legacy DBs.
1213
* New dblib connection mode using TinyTDS! Default mode too!

lib/active_record/connection_adapters/sqlserver/quoting.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ def quote(value, column = nil)
1717
else
1818
super
1919
end
20+
when Date, Time
21+
if column && column.sql_type == 'datetime'
22+
"'#{quoted_datetime(value)}'"
23+
elsif column && (column.sql_type == 'datetimeoffset' || column.sql_type == 'time')
24+
"'#{quoted_full_iso8601(value)}'"
25+
else
26+
super
27+
end
2028
when nil
2129
column.respond_to?(:sql_type) && column.sql_type == 'timestamp' ? 'DEFAULT' : super
2230
else
@@ -53,14 +61,39 @@ def quoted_false
5361
QUOTED_FALSE
5462
end
5563

64+
def quoted_datetime(value)
65+
if value.acts_like?(:time)
66+
value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(3).to(22)
67+
else
68+
quoted_date(value)
69+
end
70+
end
71+
72+
def quoted_full_iso8601(value)
73+
if value.acts_like?(:time)
74+
value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(7).to(22)
75+
else
76+
quoted_date(value)
77+
end
78+
end
79+
5680
def quoted_date(value)
5781
if value.acts_like?(:time) && value.respond_to?(:usec)
5882
"#{super}.#{sprintf("%03d",value.usec/1000)}"
83+
elsif value.acts_like?(:date)
84+
value.to_s(:_sqlserver_dateformat)
5985
else
6086
super
6187
end
6288
end
6389

90+
protected
91+
92+
def quoted_value_acts_like_time_filter(value)
93+
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
94+
value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
95+
end
96+
6497
end
6598
end
6699
end

lib/active_record/connection_adapters/sqlserver_adapter.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ def initialize(logger,config)
188188
rescue
189189
0
190190
end
191+
initialize_dateformatter
191192
initialize_sqlserver_caches
192193
use_database
193194
unless SUPPORTED_VERSIONS.include?(@database_year)
@@ -410,6 +411,15 @@ def connect
410411
raise unless @auto_connecting
411412
end
412413

414+
def initialize_dateformatter
415+
@database_dateformat = user_options['dateformat']
416+
a, b, c = @database_dateformat.each_char.to_a
417+
[a,b,c].each { |f| f.upcase! if f == 'y' }
418+
dateformat = "%#{a}-%#{b}-%#{c}"
419+
::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
420+
::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
421+
end
422+
413423
def remove_database_connections_and_rollback(database=nil)
414424
database ||= current_database
415425
do_execute "ALTER DATABASE #{quote_table_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"

test/cases/adapter_test_sqlserver.rb

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,30 @@ def setup
138138
end
139139

140140
context 'with different language' do
141-
141+
142+
setup do
143+
@default_language = @connection.user_options['language']
144+
end
145+
142146
teardown do
147+
@connection.execute("SET LANGUAGE #{@default_language}") rescue nil
148+
@connection.send :initialize_dateformatter
149+
end
150+
151+
should 'memoize users dateformat' do
143152
@connection.execute("SET LANGUAGE us_english") rescue nil
153+
dateformat = @connection.instance_variable_get(:@database_dateformat)
154+
assert_equal 'mdy', dateformat
155+
end
156+
157+
should 'have a dateformatter' do
158+
assert Date::DATE_FORMATS[:_sqlserver_dateformat]
159+
assert Time::DATE_FORMATS[:_sqlserver_dateformat]
144160
end
145161

146-
should_eventually 'do a date insertion when language is german' do
162+
should 'do a date insertion when language is german' do
147163
@connection.execute("SET LANGUAGE deutsch")
164+
@connection.send :initialize_dateformatter
148165
assert_nothing_raised do
149166
Task.create(:starting => Time.utc(2000, 1, 31, 5, 42, 0), :ending => Date.new(2006, 12, 31))
150167
end
@@ -241,19 +258,13 @@ def setup
241258
context 'saving new datetime objects' do
242259

243260
should 'truncate 123456 usec to just 123 in the DB cast back to 123000' do
244-
@time.stubs(:usec).returns(123456)
261+
Time.any_instance.stubs :iso8601 => "2011-07-26T12:29:01.123-04:00"
245262
saved = SqlServerChronic.create!(:datetime => @time).reload
263+
saved.reload
246264
assert_equal '123', saved.datetime_before_type_cast.split('.')[1] if saved.datetime_before_type_cast.is_a?(String)
247265
assert_equal 123000, saved.datetime.usec
248266
end
249267

250-
should 'truncate 3001 usec to just 003 in the DB cast back to 3000' do
251-
@time.stubs(:usec).returns(3001)
252-
saved = SqlServerChronic.create!(:datetime => @time).reload
253-
assert_equal '003', saved.datetime_before_type_cast.split('.')[1] if saved.datetime_before_type_cast.is_a?(String)
254-
assert_equal 3000, saved.datetime.usec
255-
end
256-
257268
end
258269

259270
end

test/cases/persistence_test_sqlserver.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class PersistencesTest < ActiveRecord::TestCase
2727

2828
include SqlserverCoercedTest
2929

30-
def test_update_all_doesnt_ignore_order
30+
def test_coerced_update_all_doesnt_ignore_order
3131
assert_equal authors(:david).id + 1, authors(:mary).id
3232
test_update_with_order_succeeds = lambda do |order|
3333
begin

0 commit comments

Comments
 (0)