ffi-icu 0.3.0 → 0.4.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: 3e1817bf83d85197ea62937c274ff98c71984ef4a432a6f76fc3cca764bf45dc
4
- data.tar.gz: 2a21f2177dc151831fe89782be8cfe53d2f13a6d1038961eb1efe5c3970a27a0
3
+ metadata.gz: 357d3d218075dcfd56e963cd261c5fbe947d8daeb5d5826aebce8f1bedfe7ffe
4
+ data.tar.gz: afb2c28c1730cf05652433a1a721447385de47fa0fd105cb9782fe7496c55d65
5
5
  SHA512:
6
- metadata.gz: 6fb2d659678226632c485ea24de6e79f7d20ccf7b80b03833f680bb295ae07736957f3a9037ac1d3639b432f6974c279b580767d08315ad005c57cc6be79ed8b
7
- data.tar.gz: 9c49ddef23ae27f88d4eaffbaf0cf42b670e7f07939b7c19b6255a4ac157faae25e8eb5bc973764e767b3b32d40226c4b1cc95772c629fbe4b678d4cff5ebccb
6
+ metadata.gz: a257ab16df440695f42f6db486950e62fa1c08655324ad59d347eeb0e02d46c37581f04845cf035c528c7223a8e3d092a9bac877f7da5da10daee18e701d9aa3
7
+ data.tar.gz: be241d1659c60288c84f7844efaec71d368c1c650b4c1be410cbb0ff99cf6f08f468af30fd02a5ac050d853b18e243250dcc199c81815a5d2f7b1bf9bd783559
data/.travis.yml CHANGED
@@ -1,13 +1,20 @@
1
1
  language: ruby
2
2
  os: linux
3
- dist: xenial
3
+ dist: focal
4
+
5
+ arch:
6
+ - amd64
7
+ - arm64
4
8
 
5
9
  rvm:
6
- - 2.5
7
10
  - 2.6
8
11
  - 2.7
12
+ - 3.0
9
13
  - ruby-head
10
14
 
11
15
  before_script:
12
16
  - sudo apt-get install -y libicu-dev
13
17
 
18
+ jobs:
19
+ allow_failures:
20
+ - arch: arm64
data/README.md CHANGED
@@ -16,7 +16,7 @@ Dependencies
16
16
  ICU.
17
17
 
18
18
  If you get messages that the library or functions are not found, you can
19
- set some environment varibles to tell ffi-icu where to find it, e.g.:
19
+ set some environment variables to tell ffi-icu where to find it, e.g.:
20
20
 
21
21
  $ export FFI_ICU_LIB="icui18n.so"
22
22
  $ export FFI_ICU_VERSION_SUFFIX="_3_8"
@@ -109,14 +109,24 @@ Examples:
109
109
  f #=> "12.11.15 15:21"
110
110
 
111
111
  # reusable formatting objects
112
- formater = ICU::TimeFormatting.create(:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :long , :time => :none)
113
- formater.format(Time.now) #=> "25. února 2015"
112
+ formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :long, :time => :none)
113
+ formatter.format(Time.now) #=> "25. února 2015"
114
114
  ```
115
115
 
116
116
  ```ruby
117
117
  # reusable formatting objects
118
- formater = ICU::TimeFormatting.create(:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :long , :time => :none)
119
- formater.parse("25. února 2015") #=> Wed Feb 25 00:00:00 +0100 2015
118
+ formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :zone => 'Europe/Prague', :date => :long, :time => :none)
119
+ formatter.parse("25. února 2015") #=> Wed Feb 25 00:00:00 +0100 2015
120
+ ```
121
+
122
+ For skeleton formatting, visit the [Unicode date field symbol table](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#date-field-symbol-table) page to help find the pattern characters to use.
123
+
124
+ ```ruby
125
+ formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :date => :pattern, :time => :pattern, :skeleton => 'MMMMY')
126
+ formatter.format(Time.now) #=> "únor 2015"
127
+
128
+ formatter = ICU::TimeFormatting.create(:locale => 'cs_CZ', :date => :pattern, :time => :pattern, :skeleton => 'Y')
129
+ formatter.format(Time.now) #=> "2015"
120
130
  ```
121
131
 
122
132
  Tested on:
@@ -28,7 +28,19 @@ module ICU
28
28
  result.read_string(length)
29
29
  end
30
30
 
31
- def self.read_uchar_buffer(length)
31
+ def self.read_uchar_buffer(length, &blk)
32
+ buf, len = read_uchar_buffer_as_ptr_impl(length, &blk)
33
+ buf.string(len)
34
+ end
35
+
36
+ def self.read_uchar_buffer_as_ptr(length, &blk)
37
+ buf, _ = read_uchar_buffer_as_ptr_impl(length, &blk)
38
+ buf
39
+ end
40
+
41
+ private
42
+
43
+ def self.read_uchar_buffer_as_ptr_impl(length)
32
44
  attempts = 0
33
45
 
34
46
  begin
@@ -40,7 +52,7 @@ module ICU
40
52
  raise BufferOverflowError, "needed: #{length}"
41
53
  end
42
54
 
43
- result.string(length)
55
+ [result, length]
44
56
  end
45
57
  end
46
58
  end
data/lib/ffi-icu/lib.rb CHANGED
@@ -19,9 +19,7 @@ module ICU
19
19
  '/usr/local/{lib64,lib}',
20
20
  '/opt/local/{lib64,lib}',
21
21
  '/usr/{lib64,lib}',
22
- '/usr/lib/x86_64-linux-gnu', # for Debian Multiarch http://wiki.debian.org/Multiarch
23
- '/usr/lib/i386-linux-gnu', # for Debian Multiarch
24
- ]
22
+ ] + Dir['/usr/lib/*-linux-gnu'] # for Debian Multiarch http://wiki.debian.org/Multiarch
25
23
  end
26
24
  end
27
25
  end
@@ -39,8 +37,12 @@ module ICU
39
37
  [find_lib("libicui18n.#{FFI::Platform::LIBSUFFIX}.??"),
40
38
  find_lib("libicutu.#{FFI::Platform::LIBSUFFIX}.??")]
41
39
  when :osx
42
- # See https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-release-notes (62986286)
43
- if Gem::Version.new(`sw_vers -productVersion`) >= Gem::Version.new('11')
40
+ if ENV.key?('FFI_ICU_LIB')
41
+ # Ensure we look in the user-supplied override dir for a user-compiled libicu
42
+ [find_lib("libicui18n.??.#{FFI::Platform::LIBSUFFIX}"),
43
+ find_lib("libicutu.??.#{FFI::Platform::LIBSUFFIX}")]
44
+ elsif Gem::Version.new(`sw_vers -productVersion`) >= Gem::Version.new('11')
45
+ # See https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-release-notes (62986286)
44
46
  ["libicucore.#{FFI::Platform::LIBSUFFIX}"]
45
47
  else
46
48
  [find_lib("libicucore.#{FFI::Platform::LIBSUFFIX}")]
@@ -425,11 +427,16 @@ module ICU
425
427
  attach_function :unum_set_attribute, "unum_setAttribute#{suffix}", [:pointer, :number_format_attribute, :int32_t], :void
426
428
  # date
427
429
  enum :date_format_style, [
428
- :none, -1,
429
- :full, 0,
430
- :long, 1,
431
- :medium, 2,
432
- :short, 3,
430
+ :pattern, -2,
431
+ :none, -1,
432
+ :full, 0,
433
+ :long, 1,
434
+ :medium, 2,
435
+ :short, 3,
436
+ ]
437
+ enum :uloc_data_locale_type, [
438
+ :actual_locale, 0,
439
+ :valid_locale, 1,
433
440
  ]
434
441
  attach_function :udat_open, "udat_open#{suffix}", [:date_format_style, :date_format_style, :string, :pointer, :int32_t, :pointer, :int32_t, :pointer ], :pointer
435
442
  attach_function :udat_close, "unum_close#{suffix}", [:pointer], :void
@@ -437,6 +444,11 @@ module ICU
437
444
  attach_function :udat_parse, "udat_parse#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :pointer], :double
438
445
  attach_function :udat_toPattern, "udat_toPattern#{suffix}", [:pointer, :bool , :pointer, :int32_t , :pointer], :int32_t
439
446
  attach_function :udat_applyPattern, "udat_applyPattern#{suffix}", [:pointer, :bool , :pointer, :int32_t ], :void
447
+ # skeleton pattern
448
+ attach_function :udatpg_open, "udatpg_open#{suffix}", [:string, :pointer], :pointer
449
+ attach_function :udatpg_close, "udatpg_close#{suffix}", [:pointer], :void
450
+ attach_function :udatpg_getBestPattern, "udatpg_getBestPattern#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :int32_t, :pointer], :int32_t
451
+ attach_function :udatpg_getSkeleton, "udatpg_getSkeleton#{suffix}", [:pointer, :pointer, :int32_t, :pointer, :int32_t, :pointer], :int32_t
440
452
  # tz
441
453
  attach_function :ucal_setDefaultTimeZone, "ucal_setDefaultTimeZone#{suffix}", [:pointer, :pointer], :int32_t
442
454
  attach_function :ucal_getDefaultTimeZone, "ucal_getDefaultTimeZone#{suffix}", [:pointer, :int32_t, :pointer], :int32_t
@@ -68,8 +68,19 @@ module ICU
68
68
  case number
69
69
  when Float
70
70
  needed_length = Lib.unum_format_double(@f, number, out_ptr, needed_length, nil, error)
71
- when Fixnum
72
- needed_length = Lib.unum_format_int32(@f, number, out_ptr, needed_length, nil, error)
71
+ when Integer
72
+ begin
73
+ # Try doing it fast, for integers that can be marshaled into an int64_t
74
+ needed_length = Lib.unum_format_int64(@f, number, out_ptr, needed_length, nil, error)
75
+ rescue RangeError
76
+ # Fall back to stringifying in Ruby and passing that to ICU
77
+ unless defined? Lib.unum_format_decimal
78
+ raise RangeError,"Number #{number} is too big to fit in int64_t and your "\
79
+ "ICU version is too old to have unum_format_decimal"
80
+ end
81
+ string_version = number.to_s
82
+ needed_length = Lib.unum_format_decimal(@f, string_version, string_version.bytesize, out_ptr, needed_length, nil, error)
83
+ end
73
84
  when BigDecimal
74
85
  string_version = number.to_s('F')
75
86
  if Lib.respond_to? :unum_format_decimal
@@ -77,8 +88,6 @@ module ICU
77
88
  else
78
89
  needed_length = Lib.unum_format_double(@f, number.to_f, out_ptr, needed_length, nil, error)
79
90
  end
80
- when Bignum
81
- needed_length = Lib.unum_format_int64(@f, number, out_ptr, needed_length, nil, error)
82
91
  end
83
92
  end
84
93
  out_ptr.string needed_length
@@ -38,6 +38,14 @@ module ICU
38
38
  # (for example, "Unknown City"), such as Los Angeles
39
39
  # see: http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
40
40
  }
41
+
42
+ HOUR_CYCLE_SYMS = {
43
+ 'h11' => 'K',
44
+ 'h12' => 'h',
45
+ 'h23' => 'H',
46
+ 'h24' => 'k',
47
+ :locale => 'j',
48
+ }
41
49
  @default_options = {}
42
50
 
43
51
  def self.create(options = {})
@@ -65,33 +73,50 @@ module ICU
65
73
 
66
74
  private
67
75
 
68
- def make_formatter(time_style, date_style, locale, time_zone_str)
69
- time_zone = nil
70
- d_len = 0
76
+ def make_formatter(time_style, date_style, locale, time_zone_str, skeleton)
77
+ time_zone = nil
78
+ tz_len = 0
79
+ pattern_len = -1
80
+ pattern_ptr = FFI::MemoryPointer.new(4)
81
+
71
82
  if time_zone_str
72
83
  time_zone = UCharPointer.from_string(time_zone_str)
73
- d_len = time_zone_str.size
84
+ tz_len = time_zone_str.size
74
85
  else
75
86
  Lib.check_error { | error|
76
87
  i_len = 150
77
88
  time_zone = UCharPointer.new(i_len)
78
- d_len = Lib.ucal_getDefaultTimeZone(time_zone, i_len, error)
89
+ tz_len = Lib.ucal_getDefaultTimeZone(time_zone, i_len, error)
79
90
  }
80
91
  end
81
92
 
82
- ptr = Lib.check_error { | error| Lib.udat_open(time_style, date_style, locale, time_zone, d_len, FFI::MemoryPointer.new(4), -1, error) }
93
+ if skeleton
94
+ date_style = :pattern
95
+ time_style = :pattern
96
+
97
+ pattern_len, pattern_ptr = skeleton_format(skeleton, locale)
98
+ end
99
+
100
+ ptr = Lib.check_error { | error| Lib.udat_open(time_style, date_style, locale, time_zone, tz_len, pattern_ptr, pattern_len, error) }
83
101
  FFI::AutoPointer.new(ptr, Lib.method(:udat_close))
84
102
  end
85
103
  end
86
104
 
87
105
  class DateTimeFormatter < BaseFormatter
88
106
  def initialize(options={})
89
- time_style = options[:time] || :short
90
- date_style = options[:date] || :short
91
- locale = options[:locale] || 'C'
92
- tz_style = options[:tz_style]
93
- time_zone = options[:zone]
94
- @f = make_formatter(time_style, date_style, locale, time_zone)
107
+ time_style = options[:time] || :short
108
+ date_style = options[:date] || :short
109
+ @locale = options[:locale] || 'C'
110
+ tz_style = options[:tz_style]
111
+ time_zone = options[:zone]
112
+ skeleton = options[:skeleton]
113
+ @hour_cycle = options[:hour_cycle]
114
+
115
+ if @hour_cycle && !HOUR_CYCLE_SYMS.keys.include?(@hour_cycle)
116
+ raise ICU::Error.new("Unknown hour cycle #{@hour_cycle}")
117
+ end
118
+
119
+ @f = make_formatter(time_style, date_style, @locale, time_zone, skeleton)
95
120
  if tz_style
96
121
  f0 = date_format(true)
97
122
  f1 = update_tz_format(f0, tz_style)
@@ -99,11 +124,13 @@ module ICU
99
124
  set_date_format(true, f1)
100
125
  end
101
126
  end
127
+
128
+ replace_hour_symbol!
102
129
  end
103
130
 
104
131
  def parse(str)
105
132
  str_u = UCharPointer.from_string(str)
106
- str_l = str_u.size
133
+ str_l = str.size
107
134
  Lib.check_error do |error|
108
135
  ret = Lib.udat_parse(@f, str_u, str_l, nil, error)
109
136
  Time.at(ret / 1000.0)
@@ -170,6 +197,128 @@ module ICU
170
197
  end
171
198
 
172
199
  def set_date_format(localized, pattern_str)
200
+ set_date_format_impl(localized, pattern_str)
201
+
202
+ # After setting the date format string, we need to ensure that any hour
203
+ # symbols were properly localised according to @hour_cycle.
204
+ replace_hour_symbol!
205
+ end
206
+
207
+ def skeleton_format(skeleton_pattern_str, locale)
208
+ skeleton_pattern_ptr = UCharPointer.from_string(skeleton_pattern_str)
209
+ skeleton_pattern_len = skeleton_pattern_str.size
210
+
211
+ needed_length = 0
212
+ pattern_ptr = UCharPointer.new(needed_length)
213
+
214
+ udatpg_ptr = Lib.check_error { |error| Lib.udatpg_open(locale, error) }
215
+ generator = FFI::AutoPointer.new(udatpg_ptr, Lib.method(:udatpg_close))
216
+
217
+ retried = false
218
+
219
+ begin
220
+ Lib.check_error do |error|
221
+ needed_length = Lib.udatpg_getBestPattern(generator, skeleton_pattern_ptr, skeleton_pattern_len, pattern_ptr, needed_length, error)
222
+ end
223
+
224
+ return needed_length, pattern_ptr
225
+ rescue BufferOverflowError
226
+ raise BufferOverflowError, "needed: #{needed_length}" if retried
227
+ pattern_ptr = pattern_ptr.resized_to needed_length
228
+ retried = true
229
+ retry
230
+ end
231
+ end
232
+
233
+ private
234
+
235
+ # Converts the current pattern to a pattern that takes the desired hour cycle
236
+ # into account. This is needed because most of the standard patterns in ICU
237
+ # contain either h (12 hour) or H (23 hour) in them, instead of j (locale-
238
+ # specified hour cycle). This means if you use a locale with an @hours=h12
239
+ # keyword in it, for example, it would normally be totally ignored by ICU.
240
+ #
241
+ # This is the same fixup done by Firefox:
242
+ # https://github.com/tc39/ecma402/issues/665#issuecomment-1084833809
243
+ # https://searchfox.org/mozilla-central/rev/625c3d0c8ae46502aed83f33bd530cb93e926e9f/intl/components/src/DateTimeFormat.cpp#282-323
244
+ def replace_hour_symbol!
245
+ # Short circuit this case - nil means "use whatever is in the pattern already", so
246
+ # no need to actually run any of this implementation.
247
+ return unless @hour_cycle
248
+
249
+ # Get the current pattern and convert to a skeleton
250
+ skeleton_str = pattern_to_skeleton_uchar(current_pattern_as_uchar).string
251
+
252
+ # Manipulate the skeleton to make it work with the correct hour cycle.
253
+ skeleton_str.gsub!(/[hHkKjJ]/, HOUR_CYCLE_SYMS[@hour_cycle])
254
+
255
+ # Either ensure the skeleton has, or does not have, am/pm, as appropriate
256
+ if ['h11', 'h12'].include?(@hour_cycle)
257
+ skeleton_str << 'a' unless skeleton_str.include? 'a'
258
+ else
259
+ skeleton_str.gsub!('a', '')
260
+ end
261
+
262
+ # Convert the skeleton back to a pattern
263
+ new_pattern_str = skeleton_to_pattern_uchar(UCharPointer.from_string(skeleton_str)).string
264
+
265
+ # We also need to manipulate the _pattern_, a little bit, because (according to Firefox source):
266
+ #
267
+ # Input skeletons don't differentiate between "K" and "h" resp. "k" and "H".
268
+ #
269
+ # https://searchfox.org/mozilla-central/rev/625c3d0c8ae46502aed83f33bd530cb93e926e9f/intl/components/src/DateTimeFormat.cpp#183
270
+ # So, if we put a skeleton with a k in it into getBestPattern, it comes out with a H (and a
271
+ # skeleton with a K in it comes out with a h). Need to fix this in the generated pattern.
272
+ resolved_hour_cycle = @hour_cycle == :locale ? Locale.new(@locale).keyword('hours') : @hour_cycle
273
+
274
+ if HOUR_CYCLE_SYMS.keys.include?(resolved_hour_cycle)
275
+ new_pattern_str.gsub!(/[hHkK]/, HOUR_CYCLE_SYMS[resolved_hour_cycle])
276
+ end
277
+
278
+ # Finally, set the new pattern onto the date time formatter
279
+ set_date_format_impl(false, new_pattern_str)
280
+ end
281
+
282
+ # Load up the date formatter locale and make a generator
283
+ # Note that we _MUST_ actually use @locale as passed to us, rather than calling
284
+ # udat_getLocaleByType to look it up from @f, because the latter will throw away
285
+ # any @hours specifier in the locale, and we need it.
286
+ def datetime_pattern_generator
287
+ @datetime_pattern_generator ||= FFI::AutoPointer.new(
288
+ Lib.check_error { |error| Lib.udatpg_open(@locale, error) },
289
+ Lib.method(:udatpg_close)
290
+ )
291
+ end
292
+
293
+ def current_pattern_as_uchar
294
+ Lib::Util.read_uchar_buffer_as_ptr(0) do |buf, error|
295
+ Lib.udat_toPattern(@f, false, buf, buf.length_in_uchars, error)
296
+ end
297
+ end
298
+
299
+ def pattern_to_skeleton_uchar(pattern_uchar)
300
+ Lib::Util.read_uchar_buffer_as_ptr(0) do |buf, error|
301
+ Lib.udatpg_getSkeleton(
302
+ datetime_pattern_generator,
303
+ pattern_uchar, pattern_uchar.length_in_uchars,
304
+ buf, buf.length_in_uchars,
305
+ error
306
+ )
307
+ end
308
+ end
309
+
310
+ def skeleton_to_pattern_uchar(skeleton_uchar)
311
+ Lib::Util.read_uchar_buffer_as_ptr(0) do |buf, error|
312
+ Lib.udatpg_getBestPattern(
313
+ datetime_pattern_generator,
314
+ skeleton_uchar, skeleton_uchar.length_in_uchars,
315
+ buf, buf.length_in_uchars,
316
+ error
317
+ )
318
+ end
319
+ end
320
+
321
+ def set_date_format_impl(localized, pattern_str)
173
322
  pattern = UCharPointer.from_string(pattern_str)
174
323
  pattern_len = pattern_str.size
175
324
 
data/lib/ffi-icu/uchar.rb CHANGED
@@ -43,6 +43,10 @@ module ICU
43
43
  wstring.pack("U*")
44
44
  end
45
45
 
46
+ def length_in_uchars
47
+ size / type_size
48
+ end
49
+
46
50
 
47
51
  end # UCharPointer
48
52
  end # ICU
@@ -1,3 +1,3 @@
1
1
  module ICU
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.2"
3
3
  end
@@ -69,6 +69,11 @@ module ICU
69
69
  expect { NumberFormatting.create('en-US', :currency, style: :iso) }.to raise_error(StandardError)
70
70
  end
71
71
  end
72
+
73
+ it 'should format a bignum' do
74
+ str = NumberFormatting.format_number("en", 1_000_000_000_000_000_000_000_000_000_000_000_000_000)
75
+ expect(str).to eq('1,000,000,000,000,000,000,000,000,000,000,000,000,000')
76
+ end
72
77
  end
73
78
  end # NumberFormatting
74
79
  end # ICU
data/spec/time_spec.rb CHANGED
@@ -59,7 +59,7 @@ module ICU
59
59
  expect(f2.format(t8)).to eq("3/29/08#{en_sep} 6:38:24 AM #{en_tz}")
60
60
  end
61
61
 
62
- f3 = TimeFormatting.create(:locale => 'de_DE', :zone => 'Africa/Dakar ', :date => :short , :time => :long)
62
+ f3 = TimeFormatting.create(:locale => 'de_DE', :zone => 'Africa/Dakar', :date => :short , :time => :long)
63
63
  ge_sep = ""
64
64
  if cldr_version >= "27.0.1"
65
65
  ge_sep = ","
@@ -82,6 +82,105 @@ module ICU
82
82
  expect(f3.format(t7)).to eq("29.03.08#{ge_sep} 02:37:23 GMT")
83
83
  expect(f3.format(t8)).to eq("29.03.08#{ge_sep} 03:38:24 GMT")
84
84
  end
85
+
86
+ context 'skeleton pattern' do
87
+ f4 = TimeFormatting.create(:locale => 'fr_FR', :date => :pattern, :time => :pattern, :skeleton => 'MMMy')
88
+
89
+ it 'check format' do
90
+ expect(f4.format(t0)).to eq("nov. 2008")
91
+ expect(f4.format(t1)).to eq("oct. 2008")
92
+ end
93
+
94
+ it 'check date_format' do
95
+ expect(f4.date_format(true)).to eq("MMM y")
96
+ end
97
+ end
98
+
99
+ context 'hour cycle' do
100
+ # en_AU normally is 12 hours, fr_FR is normally 23 hours
101
+ ['en_AU', 'fr_FR', 'zh_CN'].each do |locale_name|
102
+ context "with locale #{locale_name}" do
103
+ it 'works with hour_cycle: h11' do
104
+ t = Time.new(2021, 04, 01, 12, 05, 0, "+00:00")
105
+ str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', hour_cycle: 'h11')
106
+ expect(str).to match(/0:05/i)
107
+ expect(str).to match(/(pm|下午)/i)
108
+ end
109
+
110
+ it 'works with hour_cycle: h12' do
111
+ t = Time.new(2021, 04, 01, 12, 05, 0, "+00:00")
112
+ str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', hour_cycle: 'h12')
113
+ expect(str).to match(/12:05/i)
114
+ expect(str).to match(/(pm|下午)/i)
115
+ end
116
+
117
+ it 'works with hour_cycle: h23' do
118
+ t = Time.new(2021, 04, 01, 00, 05, 0, "+00:00")
119
+ str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', hour_cycle: 'h23')
120
+ expect(str).to match(/0:05/i)
121
+ expect(str).to_not match(/(am|pm)/i)
122
+ end
123
+
124
+ it 'works with hour_cycle: h24' do
125
+ t = Time.new(2021, 04, 01, 00, 05, 0, "+00:00")
126
+ str = TimeFormatting.format(t, time: :short, date: :none, locale: locale_name, zone: 'UTC', hour_cycle: 'h24')
127
+ expect(str).to match(/24:05/i)
128
+ expect(str).to_not match(/(am|pm)/i)
129
+ end
130
+
131
+ context '@hours keyword' do
132
+ before(:each) do
133
+ skip("Only works on ICU >= 67") if Lib.version.to_a[0] < 67
134
+ end
135
+
136
+ it 'works with @hours=h11 keyword' do
137
+ t = Time.new(2021, 04, 01, 12, 05, 0, "+00:00")
138
+ locale = Locale.new(locale_name).with_keyword('hours', 'h11').to_s
139
+ str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', hour_cycle: :locale)
140
+ expect(str).to match(/0:05/i)
141
+ expect(str).to match(/(pm|下午)/i)
142
+ end
143
+ it 'works with @hours=h12 keyword' do
144
+ t = Time.new(2021, 04, 01, 12, 05, 0, "+00:00")
145
+ locale = Locale.new(locale_name).with_keyword('hours', 'h12').to_s
146
+ str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', hour_cycle: :locale)
147
+ expect(str).to match(/12:05/i)
148
+ expect(str).to match(/(pm|下午)/i)
149
+ end
150
+
151
+ it 'works with @hours=h23 keyword' do
152
+ t = Time.new(2021, 04, 01, 00, 05, 0, "+00:00")
153
+ locale = Locale.new(locale_name).with_keyword('hours', 'h23').to_s
154
+ str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', hour_cycle: :locale)
155
+ expect(str).to match(/0:05/i)
156
+ expect(str).to_not match(/(am|pm)/i)
157
+ end
158
+
159
+ it 'works with @hours=h24 keyword' do
160
+ t = Time.new(2021, 04, 01, 00, 05, 0, "+00:00")
161
+ locale = Locale.new(locale_name).with_keyword('hours', 'h24').to_s
162
+ str = TimeFormatting.format(t, time: :short, date: :none, locale: locale, zone: 'UTC', hour_cycle: :locale)
163
+ expect(str).to match(/24:05/i)
164
+ expect(str).to_not match(/(am|pm)/i)
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ it 'works with defaults on a h12 locale' do
171
+ t = Time.new(2021, 04, 01, 13, 05, 0, "+00:00")
172
+ str = TimeFormatting.format(t, time: :short, date: :none, locale: 'en_AU', zone: 'UTC', hour_cycle: :locale)
173
+ expect(str).to match(/1:05/i)
174
+ expect(str).to match(/pm/i)
175
+ end
176
+
177
+ it 'works with defaults on a h23 locale' do
178
+ t = Time.new(2021, 04, 01, 0, 05, 0, "+00:00")
179
+ str = TimeFormatting.format(t, time: :short, date: :none, locale: 'fr_FR', zone: 'UTC', hour_cycle: :locale)
180
+ expect(str).to match(/0:05/i)
181
+ expect(str).to_not match(/(am|pm)/i)
182
+ end
183
+ end
85
184
  end
86
185
  end
87
186
  end
@@ -9,7 +9,7 @@ module ICU
9
9
  [
10
10
  ["Any-Hex", "abcde", "\\u0061\\u0062\\u0063\\u0064\\u0065"],
11
11
  ["Lower", "ABC", "abc"],
12
- ["en", "雙屬性集合之空間分群演算法-應用於地理資料", "shuāng shǔ xìng jí hé zhī kōng jiān fēn qún yǎn suàn fǎ-yīng yòng yú de lǐ zī liào"],
12
+ ["Han-Latin", "雙屬性集合之空間分群演算法-應用於地理資料", "shuāng shǔ xìng jí hé zhī kōng jiān fēn qún yǎn suàn fǎ-yīng yòng yú de lǐ zī liào"],
13
13
  ["Devanagari-Latin", "दौलत", "daulata"]
14
14
  ].each do |id, input, output|
15
15
  it "should transliterate #{id}" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ffi-icu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jari Bakken