arel_extensions 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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