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 +4 -4
- data/.travis.yml +9 -2
- data/README.md +15 -5
- data/lib/ffi-icu/lib/util.rb +14 -2
- data/lib/ffi-icu/lib.rb +22 -10
- data/lib/ffi-icu/number_formatting.rb +13 -4
- data/lib/ffi-icu/time_formatting.rb +162 -13
- data/lib/ffi-icu/uchar.rb +4 -0
- data/lib/ffi-icu/version.rb +1 -1
- data/spec/number_formatting_spec.rb +5 -0
- data/spec/time_spec.rb +100 -1
- data/spec/transliteration_spec.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 357d3d218075dcfd56e963cd261c5fbe947d8daeb5d5826aebce8f1bedfe7ffe
|
4
|
+
data.tar.gz: afb2c28c1730cf05652433a1a721447385de47fa0fd105cb9782fe7496c55d65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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:
|
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
|
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
|
-
|
113
|
-
|
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
|
-
|
119
|
-
|
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:
|
data/lib/ffi-icu/lib/util.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
43
|
-
|
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
|
-
:
|
429
|
-
:
|
430
|
-
:
|
431
|
-
:
|
432
|
-
:
|
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
|
72
|
-
|
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
|
70
|
-
|
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
|
-
|
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
|
-
|
89
|
+
tz_len = Lib.ucal_getDefaultTimeZone(time_zone, i_len, error)
|
79
90
|
}
|
80
91
|
end
|
81
92
|
|
82
|
-
|
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
|
90
|
-
date_style
|
91
|
-
locale = options[:locale] || 'C'
|
92
|
-
tz_style
|
93
|
-
time_zone
|
94
|
-
|
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 =
|
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
data/lib/ffi-icu/version.rb
CHANGED
@@ -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
|
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
|
-
["
|
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
|