arel_extensions 1.3.1 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 632b32bb630753413ebde8d2ad0d9b567aab88697c64b5abbbf1f50e53789690
4
- data.tar.gz: 7d8c0d52c2c07ff36362dbe00b68c1e3216b3924dbd98fea99e5b643953b217e
3
+ metadata.gz: a07429d6f459dff77dc3fa56aa258f1fee94ecb7401772ec3ceaea8dc0a8c4d7
4
+ data.tar.gz: d1600677cf0e63777bd8d8b656f4e17f0fa534e23579cfe3c18044e855a6115b
5
5
  SHA512:
6
- metadata.gz: 54438691157a004658d1ce8f3e4c47e76bdaab4d0f569199f13add0bef389dd91c2cc99807625ef18b9e96cdf570069fa529d55720e9393e8ce3783470e24156
7
- data.tar.gz: cfc50270090b43b343444dbecba639b32966b1a45fb5f475338b140e5641c14353007f01cb7228a1870445e808244fa441bdf4fcab7a31fdd2e6e8618bb901bf
6
+ metadata.gz: 752646ca62337cff68c38bcda876d5aa1f7cf6cb8f31906b6d6335237aa1a074daf4891b94b45486d758f1af035d87aac2449951aab683d35f84c123b10c39f8
7
+ data.tar.gz: d3002160ab13dc1ecbdac0d863a72847a4b3f151cfe12ada6d694b4fb02d70a7e16facbe455d474a5e2ce51ca5686c8abfee26962b91f04023e2fdeb335e22e0
data/README.md CHANGED
@@ -152,7 +152,9 @@ t[:birthdate].format('%Y-%m-%d').to_sql
152
152
  ```
153
153
 
154
154
  Which formats the datetime without any time zone conversion.
155
- The second form is:
155
+ The second form accepts 2 kinds of values:
156
+
157
+ 1. String:
156
158
 
157
159
  ```ruby
158
160
  t[:birthdate].format('%Y/%m/%d %H:%M:%S', 'posix/Pacific/Tahiti')
@@ -162,7 +164,18 @@ t[:birthdate].format('%Y/%m/%d %H:%M:%S', 'posix/Pacific/Tahiti')
162
164
  # ^^^^^^^^^^^^^^^^^^^^ 🚨 Invalid timezone for SQL Server. Explanation below.
163
165
  ```
164
166
 
165
- This will convert the datetime field to the supplied time zone.
167
+ which will convert the datetime field to the supplied time zone. This generally
168
+ means that you're letting the RDBMS decide or infer what is the timezone of the
169
+ column before conversion to the supplied timezone.
170
+
171
+ 1. Hash of the form `{ src_time_zone => dst_time_zone }`:
172
+
173
+ ```ruby
174
+ t[:birthdate].format('%Y/%m/%d %H:%M:%S', { 'posix/Europe/Paris' => 'posix/Pacific/Tahiti' })
175
+ ```
176
+
177
+ which will explicitly indicate the original timestamp that should be considered
178
+ by the RDBMS.
166
179
 
167
180
  Warning:
168
181
 
@@ -176,6 +189,14 @@ Warning:
176
189
  - ☣️ Choosing abbreviate forms like `CET`, which stands for `Central European
177
190
  Time` will behave differently on `PostgreSQL` and `MySQL`. Don't assume
178
191
  uniform behavior, or even a _rational_ one.
192
+ - ⚠️ Pay attention to the type of the `datetime` column you're working with. For
193
+ example, in Postgres, a `datetime` can be one of the following types:
194
+ 1. `timestamp with time zone`
195
+ 2. `timestamp without time zone`
196
+ In the first case, you don't need to supply a conversion hash because postgres
197
+ knows how to convert it to the desired time zone. However, if you do the same
198
+ for the second case, you might get surprises, especially if your Postgres
199
+ installation's default timezone is not `UTC`.
179
200
  - ⚠️ SQLite is not supported.
180
201
  - 🚨 Always test against your setup 🚨
181
202
 
@@ -10,7 +10,7 @@ module ArelExtensions
10
10
  def initialize expr
11
11
  col = expr[0]
12
12
  @iso_format = convert_format(expr[1])
13
- @time_zone = Arel::Nodes.build_quoted(expr[2]) if expr[2]
13
+ @time_zone = expr[2]
14
14
  @col_type = type_of_attribute(col)
15
15
  super [col, convert_to_string_node(@iso_format)]
16
16
  end
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "1.3.1".freeze
2
+ VERSION = "1.3.2".freeze
3
3
  end
@@ -276,9 +276,16 @@ module ArelExtensions
276
276
  collector << ' '
277
277
  end
278
278
  collector = visit o.left, collector
279
- if o.time_zone
280
- collector << ") AT TIME ZONE 'UTC' AT TIME ZONE "
281
- collector = visit o.time_zone, collector
279
+ case o.time_zone
280
+ when Hash
281
+ src_tz, dst_tz = o.time_zone.first
282
+ collector << ') AT TIME ZONE '
283
+ collector = visit Arel::Nodes.build_quoted(src_tz), collector
284
+ collector << ' AT TIME ZONE '
285
+ collector = visit Arel::Nodes.build_quoted(dst_tz), collector
286
+ when String
287
+ collector << ') AT TIME ZONE '
288
+ collector = visit Arel::Nodes.build_quoted(o.time_zone), collector
282
289
  end
283
290
  collector << LOADED_VISITOR::COMMA
284
291
  collector << fmt.to_s
@@ -307,9 +314,16 @@ module ArelExtensions
307
314
  collector << ' '
308
315
  end
309
316
  collector = visit o.left, collector
310
- if o.time_zone
311
- collector << ") AT TIME ZONE 'UTC' AT TIME ZONE "
312
- collector = visit o.time_zone, collector
317
+ case o.time_zone
318
+ when Hash
319
+ src_tz, dst_tz = o.time_zone.first.first, o.time_zone.first.second
320
+ collector << ") AT TIME ZONE "
321
+ collector = visit Arel::Nodes.build_quoted(src_tz), collector
322
+ collector << " AT TIME ZONE "
323
+ collector = visit Arel::Nodes.build_quoted(dst_tz), collector
324
+ when String
325
+ collector << ") AT TIME ZONE "
326
+ collector = visit Arel::Nodes.build_quoted(o.time_zone), collector
313
327
  end
314
328
  collector << ')'
315
329
  collector << ')' if !fmt
@@ -211,9 +211,17 @@ module ArelExtensions
211
211
  collector << "DATE_FORMAT("
212
212
  collector << "CONVERT_TZ(" if o.time_zone
213
213
  collector = visit o.left, collector
214
- if o.time_zone
214
+ case o.time_zone
215
+ when Hash
216
+ src_tz, dst_tz = o.time_zone.first
217
+ collector << COMMA
218
+ collector = visit Arel::Nodes.build_quoted(src_tz), collector
219
+ collector << COMMA
220
+ collector = visit Arel::Nodes.build_quoted(dst_tz), collector
221
+ collector << ')'
222
+ when String
215
223
  collector << COMMA << "'UTC'" << COMMA
216
- collector = visit o.time_zone, collector
224
+ collector = visit Arel::Nodes.build_quoted(o.time_zone), collector
217
225
  collector << ')'
218
226
  end
219
227
  collector << COMMA
@@ -449,9 +449,16 @@ module ArelExtensions
449
449
  collector << "TO_CHAR("
450
450
  collector << "CAST(" if o.time_zone
451
451
  collector = visit o.left, collector
452
- if o.time_zone
453
- collector << " as timestamp) at time zone "
454
- collector = visit o.time_zone, collector
452
+ case o.time_zone
453
+ when Hash
454
+ src_tz, dst_tz = o.time_zone.first
455
+ collector << ' as timestamp) at time zone '
456
+ collector = visit Arel::Nodes.build_quoted(src_tz), collector
457
+ collecto < ' at time zone '
458
+ collector = visit Arel::Nodes.build_quoted(dst_tz), collector
459
+ when String
460
+ collector << ' as timestamp) at time zone '
461
+ collector = visit Arel::Nodes.build_quoted(o.time_zone), collector
455
462
  end
456
463
  collector << COMMA
457
464
  collector = visit Arel::Nodes.build_quoted(fmt), collector
@@ -175,10 +175,18 @@ module ArelExtensions
175
175
  def visit_ArelExtensions_Nodes_Format o, collector
176
176
  fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
177
177
  collector << "TO_CHAR("
178
+ collector << '(' if o.time_zone
178
179
  collector = visit o.left, collector
179
- if o.time_zone
180
- collector << " AT TIME ZONE "
181
- collector = visit o.time_zone, collector
180
+ case o.time_zone
181
+ when Hash
182
+ src_tz, dst_tz = o.time_zone.first
183
+ collector << ') AT TIME ZONE '
184
+ collector = visit Arel::Nodes.build_quoted(src_tz), collector
185
+ collector << ' AT TIME ZONE '
186
+ collector = visit Arel::Nodes.build_quoted(dst_tz), collector
187
+ when String
188
+ collector << ') AT TIME ZONE '
189
+ collector = visit Arel::Nodes.build_quoted(o.time_zone), collector
182
190
  end
183
191
  collector << COMMA
184
192
  collector = visit Arel::Nodes.build_quoted(fmt), collector
@@ -52,7 +52,6 @@ end
52
52
  load_lib(db_and_gem[ENV['DB']])
53
53
 
54
54
  require 'arel_extensions'
55
- Arel::Table.engine = FakeRecord::Base.new
56
55
 
57
56
  $arel_silence_type_casting_deprecation = true
58
57
 
@@ -403,35 +403,39 @@ module ArelExtensions
403
403
  'tahiti' => 'Hawaiian Standard Time',
404
404
  'paris' => 'Central European Standard Time'
405
405
  },
406
- 'mysql' => {
406
+ 'posix' => {
407
407
  'utc' => 'UTC',
408
408
  'sao_paulo' => 'America/Sao_Paulo',
409
409
  'tahiti' => 'Pacific/Tahiti',
410
410
  'paris' => 'Europe/Paris'
411
- },
412
- 'oracle' => {
413
- 'utc' => 'UTC',
414
- 'sao_paulo' => 'America/Sao_Paulo',
415
- 'tahiti' => 'Pacific/Tahiti',
416
- 'paris' => 'Europe/Paris'
417
- },
418
- 'postgresql' => {
419
- 'utc' => 'UTC',
420
- 'sao_paulo' => 'America/Sao Paulo (-03)',
421
- 'tahiti' => 'Pacific/Tahiti (-10)',
422
- 'paris' => 'Europe/Paris'
423
- },
411
+ }
424
412
  }
425
413
 
426
- tz = time_zones[ENV['DB']]
427
- skip "Unsupported timezone conversion for DB=#{ENV['DB']}" if tz.nil?
414
+ skip "Unsupported timezone conversion for DB=#{ENV['DB']}" if !['mssql', 'mysql', 'oracle', 'postgresql'].include?(ENV['DB'])
415
+
416
+ tz = ENV['DB'] == 'mssql' ? time_zones['mssql'] : time_zones['posix']
417
+
428
418
  assert_equal '2014/03/03 12:42:00', t(@lucas, @updated_at.format('%Y/%m/%d %H:%M:%S', tz['utc']))
429
- assert_equal '2014/03/03 09:42:00', t(@lucas, @updated_at.format('%Y/%m/%d %H:%M:%S', tz['sao_paulo']))
430
- assert_equal '2014/03/03 02:42:00', t(@lucas, @updated_at.format('%Y/%m/%d %H:%M:%S', tz['tahiti']))
419
+ assert_equal '2014/03/03 09:42:00', t(@lucas, @updated_at.format('%Y/%m/%d %H:%M:%S', { tz['utc'] => tz['sao_paulo'] }))
420
+ assert_equal '2014/03/03 02:42:00', t(@lucas, @updated_at.format('%Y/%m/%d %H:%M:%S', { tz['utc'] => tz['tahiti'] }))
421
+
422
+ # Skipping conversion from UTC to the desired timezones fails in SQL
423
+ # Server and Postgres. This is mainly due to the fact that timezone
424
+ # information is not preserved in the column itself.
425
+ #
426
+ # MySQL is happy to consider that times by default are in UTC.
427
+ assert_equal '2014/03/03 13:42:00', t(@lucas, @updated_at.format('%Y/%m/%d %H:%M:%S', { tz['utc'] => tz['paris'] }))
428
+ refute_equal '2014/03/03 13:42:00', t(@lucas, @updated_at.format('%Y/%m/%d %H:%M:%S', tz['paris'])) if !['mysql'].include?(ENV['DB'])
431
429
 
432
430
  # Winter/Summer time
433
- assert_equal '2022/02/01 11:42:00', t(@lucas, Arel::Nodes.build_quoted('2022-02-01 10:42:00').cast(:datetime).format('%Y/%m/%d %H:%M:%S', tz['paris']))
434
- assert_equal '2022/08/01 12:42:00', t(@lucas, Arel::Nodes.build_quoted('2022-08-01 10:42:00').cast(:datetime).format('%Y/%m/%d %H:%M:%S', tz['paris']))
431
+ assert_equal '2014/08/03 14:42:00', t(@lucas, (@updated_at + 5.months).format('%Y/%m/%d %H:%M:%S', { tz['utc'] => tz['paris'] }))
432
+ if ENV['DB'] == 'mssql'
433
+ assert_equal '2022/02/01 11:42:00', t(@lucas, Arel::Nodes.build_quoted('2022-02-01 10:42:00').cast(:datetime).format('%Y/%m/%d %H:%M:%S', { tz['utc'] => tz['paris'] }))
434
+ assert_equal '2022/08/01 12:42:00', t(@lucas, Arel::Nodes.build_quoted('2022-08-01 10:42:00').cast(:datetime).format('%Y/%m/%d %H:%M:%S', { tz['utc'] => tz['paris'] }))
435
+ else
436
+ assert_equal '2022/02/01 11:42:00', t(@lucas, Arel::Nodes.build_quoted('2022-02-01 10:42:00').cast(:datetime).format('%Y/%m/%d %H:%M:%S', tz['paris']))
437
+ assert_equal '2022/08/01 12:42:00', t(@lucas, Arel::Nodes.build_quoted('2022-08-01 10:42:00').cast(:datetime).format('%Y/%m/%d %H:%M:%S', tz['paris']))
438
+ end
435
439
  end
436
440
 
437
441
  def test_coalesce
data/version_v1.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "1.3.1".freeze
2
+ VERSION = "1.3.2".freeze
3
3
  end
data/version_v2.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "2.1.1".freeze
2
+ VERSION = "2.1.2".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arel_extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yann Azoury
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-03-03 00:00:00.000000000 Z
13
+ date: 2022-03-04 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: arel