arel_extensions 2.1.1 → 2.1.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: 9093dcc8b0e665c9bde4c86d7c1c81314ea3766ea8f3cb0774693fb8d8b08dbf
4
- data.tar.gz: 89512b1344dfdd0d5f1701f843c5297a24a23a606e105249f3e68c707f080201
3
+ metadata.gz: c069fafddb858e0acc7cee0bf1bc435c2127ccdf6e5d870b8e9caff0d31abdb1
4
+ data.tar.gz: 2d0898c4d35fbe0ac72ccb395ef79ae63f10b662ce6003cea0702d55c27b2729
5
5
  SHA512:
6
- metadata.gz: cda694a64773c8bce00f47842d2429b431db612cfd9e7bc899f53050d8ed8e4a2a07c087e29bb8ce615f42a4bbbcdcfcc4ef2ab19b17ff8a1bf744f85978ae68
7
- data.tar.gz: c583725ca48d87d6654ed16b086735fe73b1c12af5161b7cc7148fe7c7abdf42919456b2bcf95ef58d1cf18af3f1eda02101da8c9d1dc364a2648071954559fd
6
+ metadata.gz: aa4f685f2548b172488d6f3537754c51ad269773a2ff525b7e114e8d4da38bfe41c38cbeee20cb67d7557b46c39c10d218e80181ffa5ebe6be393763981d57d3
7
+ data.tar.gz: 664cf3c829c5df39b3ef8c9913cec516214482e328f01a8c71370aa937abc47194a20b31b8d930a297c322e22025d1af9b1b49384df08ec65860203b8bf27f78
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 = "2.1.1".freeze
2
+ VERSION = "2.1.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: 2.1.1
4
+ version: 2.1.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: activerecord