ffi-icu 0.3.0 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|