icu4r_19 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,87 @@
1
+ 25-May-2006
2
+ * ustring.c: preventing memory leaks when exception is thrown from block in UString#each_* methods
3
+
4
+ * fmt.cpp: preventing memory leaks when message can't be formatted (UString#fmt) or date parsed
5
+
6
+ 20-May-2006
7
+ * collator.c: a little docs added
8
+
9
+ * ustring.c, uregex.c: fixes in split(), UString#[regexp, index] now allows negative indices
10
+
11
+ * test/test_ustring.rb: more tests
12
+
13
+ 27-Apr-2006
14
+ * collator.c: added UCollator class
15
+
16
+ * converter.c: added UConverter class
17
+
18
+ 24-Apr-2006
19
+ * UString: added eql? method (fixes usage of UString as Hash key); fixed sentinel bug in normalize
20
+
21
+ * test/test_calendar: removing some dependencies from ICU content and user locale in test_default and test_time_zones (by report of Daigo Moriwaki)
22
+
23
+ 02-Feb-2006
24
+ * UString: moved parse_date to UCalendar.parse
25
+
26
+ * UResourceBundle: aref can accept additional parameter and return actual locale
27
+
28
+ * UCalendar: simplified formmating options
29
+
30
+ 26-Jan-2006
31
+ * UString: sub, gsub, scan, each_* now set "busy" flag before calling block, thus preventing scanned string to be modfied inside block
32
+
33
+ * ustring.c: fixed? bug with sentinels, also Array#to_u, and fixed again
34
+
35
+ 25-Jan-2006
36
+ * UString: addde foldcase method
37
+
38
+ * UString: added char_span(start,[len, locale]) method.
39
+
40
+ * ustring.c: fixed bug with incorrect sentinel set in splice_units
41
+
42
+ 24-Jan-2006:
43
+ * split source code to: uregex.c, ubundle.c, ucore_ext.c
44
+
45
+ * changed main lib name: icu4r.c
46
+
47
+ 23-Jan-2006:
48
+ * UCalendar: added <, ==, >, clone, equal? methods
49
+
50
+ * UResourceBundle: throw an exception on clone
51
+
52
+ 22-Jan-2006
53
+ * added tests for UCalendar
54
+
55
+ * calendar.c: UCalendar services added
56
+
57
+ 21-Jan-2006
58
+ * UString#to_s: removed NFC before conversion
59
+
60
+ * fmt.cpp: extern problem bugfix(?)
61
+
62
+ 20-Jan-2006:
63
+ * UString: strictly sticking with code unit ranges, cleanup
64
+
65
+ * Array#to_u: replace invalid chars with U+FFFD
66
+
67
+ 19-Jan-2006
68
+ * UString#chars: added NFC before split
69
+
70
+ * UString#to_s: added NFC before conversion
71
+
72
+ * UString#normalize: added quick checks before normalization
73
+
74
+ * UString#split: removed split(//) support
75
+
76
+ * UString: fixed bugs with ranges: slice, conv_unit_range, conv_point_range
77
+
78
+ * UString: added unescape method
79
+
80
+ * UString: cleanup initializations - now uniform icu_ustr_alloc_and_wrap
81
+
82
+ * UString#sub!: now calls gsub
83
+
84
+ * UResourceBundle: added open_direct
85
+
86
+ 18-Jan-2006
87
+ * initial release 0.1.0
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006 Nikolai Lugovoi
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,156 @@
1
+ == ICU4R - ICU Unicode bindings for Ruby
2
+
3
+ ICU4R is an attempt to provide better Unicode support for Ruby,
4
+ where it lacks for a long time.
5
+
6
+ Current code is mostly rewritten string.c from Ruby 1.8.3.
7
+
8
+ ICU4R is Ruby C-extension binding for ICU library[1]
9
+ and provides following classes and functionality:
10
+
11
+ * UString:
12
+ - String-like class with internal UTF16 storage;
13
+ - UCA rules for UString comparisons (<=>, casecmp);
14
+ - encoding(codepage) conversion;
15
+ - Unicode normalization;
16
+ - transliteration, also rule-based;
17
+
18
+ Bunch of locale-sensitive functions:
19
+ - upcase/downcase;
20
+ - string collation;
21
+ - string search;
22
+ - iterators over text line/word/char/sentence breaks;
23
+ - message formatting (number/currency/string/time);
24
+ - date and number parsing.
25
+
26
+ * URegexp - unicode regular expressions.
27
+
28
+ * UResourceBundle - access to resource bundles, including ICU locale data.
29
+
30
+ * UCalendar - date manipulation and timezone info.
31
+
32
+ * UConverter - codepage conversions API
33
+
34
+ * UCollator - locale-sensitive string comparison
35
+
36
+ == Install and usage
37
+
38
+ > ruby extconf.rb
39
+ > make && make check
40
+ > make install
41
+
42
+ Now, in your scripts just require 'icu4r'.
43
+
44
+ To create RDoc, run
45
+ > sh tools/doc.sh
46
+
47
+ == Requirements
48
+
49
+ To build and use ICU4R you will need GCC and ICU v3.4 libraries[2].
50
+
51
+ == Differences from Ruby String and Regexp classes
52
+
53
+ === UString vs String
54
+
55
+ 1. UString substring/index methods use UTF16 codeunit indexes, not code points.
56
+
57
+ 2. UString supports most methods from String class. Missing methods are:
58
+ capitalize, capitalize!, swapcase, swapcase!
59
+ %, center, ljust, rjust
60
+ chomp, chomp!, chop, chop!
61
+ count, delete, delete!, squeeze, squeeze!, tr, tr!, tr_s, tr_s!
62
+ crypt, intern, sum, unpack
63
+ dump, each_byte, each_line
64
+ hex, oct, to_i, to_sym
65
+ reverse, reverse!
66
+ succ, succ!, next, next!, upto
67
+
68
+ 3. Instead of String#% method, UString#format is provided. See FORMATTING for short reference.
69
+
70
+ 4. UStrings can be created via String.to_u(encoding='utf8') or global u(str,[encoding='utf8'])
71
+ calls. Note that +encoding+ parameter must be value of String class.
72
+
73
+ 5. There's difference between character grapheme, codepoint and codeunit. See UNICODE reports for
74
+ gory details, but in short: locale dependent notion of character can be presented using
75
+ more than one codepoint - base letter and combining (accents) (also possible more than one!), and
76
+ each codepoint can require more than one codeunit to store (for UTF8 codeunit size is 8bit, though
77
+ some codepoints require up to 4bytes). So, UString has normalization and locale dependent break
78
+ iterators.
79
+
80
+ 6. Currently UString doesn't include Enumerable module.
81
+
82
+ 7. UString index/[] methods which accept URegexp, throw exception if Regexp passed.
83
+
84
+ 8. UString#<=>, UString#casecmp use UCA rules.
85
+
86
+ === URegexp
87
+
88
+ UString uses ICU regexp library. Pattern syntax is described in [./docs/UNICODE_REGEXPS] and ICU docs.
89
+
90
+ There are some differences between processing in Ruby Regexp and URegexp:
91
+
92
+ 1. When UString#sub, UString#gsub are called with block, special vars ($~, $&, $1, ...) aren't
93
+ set, as their values are processed through deep ruby core code. Instead, block receives UMatch object,
94
+ which is essentially immutable array of matching groups:
95
+ "test".u.gsub(ure("(e)(.)")) do |match|
96
+ puts match[0] # => 'es' <--> $&
97
+ puts match[1] # => 'e' <--> $1
98
+ puts match[2] # => 's' <--> $2
99
+ end
100
+
101
+ 2. In URegexp search pattern backreferences are in form \n (\1, \2, ...),
102
+ in replacement string - in form $1, $2, ...
103
+
104
+ NOTE: URegexp considers char to be a digit NOT ONLY ASCII (0x0030-0x0039), but
105
+ any Unicode char, which has property Decimal digit number (Nd), e.g.:
106
+ a = [?$, 0x1D7D9].pack("U*").u * 2
107
+ puts a.inspect_names
108
+ <U000024>DOLLAR SIGN
109
+ <U01D7D9>MATHEMATICAL DOUBLE-STRUCK DIGIT ONE
110
+ <U000024>DOLLAR SIGN
111
+ <U01D7D9>MATHEMATICAL DOUBLE-STRUCK DIGIT ONE
112
+ puts "abracadabra".u.gsub(/(b)/.U, a)
113
+ abbracadabbra
114
+
115
+
116
+ 3. One can create URegexp using global Kernel#ure function, Regexp#U, Regexp#to_u, or
117
+ from UString using URegexp.new, e.g:
118
+ /pattern/.U =~ "string".u
119
+
120
+ 4. There are differences about Regexp and URegexp multiline matching options:
121
+ t = "text\ntest"
122
+ # ^,$ handling : URegexp multiline <-> Ruby default
123
+ t.u =~ ure('^\w+$', URegexp::MULTILINE)
124
+ => #<UMatch:0xf6f7de04 @ranges=[0..3], @cg=[\u0074\u0065\u0078\u0074]>
125
+ t =~ /^\w+$/
126
+ => 0
127
+ # . matches \n : URegexp DOTALL <-> /m
128
+ t.u =~ ure('.+test', URegexp::DOTALL)
129
+ => #<UMatch:0xf6fa4d88 ...
130
+ t.u =~ /.+test/m
131
+
132
+ 5. UMatch.range(idx) returns range for capturing group idx. This range is in codeunits.
133
+
134
+ === References
135
+
136
+ 1. ICU Official Homepage http://ibm.com/software/globalization/icu/
137
+ 2. ICU downloads http://ibm.com/software/globalization/icu/downloads.jsp
138
+ 3. ICU Home Page http://icu.sf.net
139
+ 4. Unicode Home Page http://www.unicode.org
140
+
141
+ ==== BUGS, DOCS, TO DO
142
+
143
+ The code is slow and inefficient yet, is still highly experimental,
144
+ so can have many security and memory leaks, bugs, inconsistent
145
+ documentation, incomplete test suite. Use it at your own risk.
146
+
147
+ Bug reports and feature requests are welcome :)
148
+
149
+ === Copying
150
+
151
+ This extension module is copyrighted free software by Nikolai Lugovoi.
152
+
153
+ You can redistribute it and/or modify it under the terms of MIT License.
154
+
155
+ Nikolai Lugovoi <meadow.nnick@gmail.com>
156
+
@@ -0,0 +1,32 @@
1
+ require 'rake/gempackagetask'
2
+ spec = Gem::Specification.new do |s|
3
+ s.authors = [ "Nikolai Lugovoi", "Perry Smith" ]
4
+ s.description = File.read(File.join(File.dirname(__FILE__), 'README'))
5
+ s.email = "pedz@easesoftware.com"
6
+ s.extensions = [ 'extconf.rb' ]
7
+ s.extra_rdoc_files = [
8
+ 'README',
9
+ 'docs/FORMATTING',
10
+ 'docs/UNICODE_REGEXPS',
11
+ 'MIT-LICENSE',
12
+ 'calendar.c',
13
+ 'collator.c',
14
+ 'converter.c',
15
+ 'icu4r.c',
16
+ 'ubundle.c',
17
+ 'ucore_ext.c',
18
+ 'uregex.c',
19
+ 'ustring.c'
20
+ ]
21
+ s.files = Dir['**/**']
22
+ s.has_rdoc = true
23
+ s.homepage = "https://github.com/pedz/icu4r_19"
24
+ s.name = "icu4r_19"
25
+ s.platform = Gem::Platform::RUBY
26
+ s.rdoc_options = [ '-c', 'utf-8', '-x', 'dummy' ]
27
+ s.required_ruby_version = '>=1.9'
28
+ s.requirements = [ 'ICU libraries v 4.6.1' ]
29
+ s.summary = "Ruby extension for Unicode support using ICU - 1.9.2 compatible"
30
+ s.version = "1.0"
31
+ end
32
+ Rake::GemPackageTask.new(spec).define
@@ -0,0 +1,636 @@
1
+ #include <unicode/ucal.h>
2
+ #include <unicode/uenum.h>
3
+ #include <unicode/udat.h>
4
+ #include "icu_common.h"
5
+ extern VALUE rb_cUString;
6
+ extern VALUE icu_ustr_new(UChar * ptr, long len);
7
+ extern VALUE icu_ustr_new_set(UChar * ptr, long len, long capa);
8
+ static VALUE s_calendar_fields, s_calendar_formats;
9
+ extern VALUE rb_cUCalendar;
10
+ #define UCALENDAR(obj) ((UCalendar *)DATA_PTR(obj))
11
+ /**
12
+ * Document-class: UCalendar
13
+ *
14
+ * The UCalendar class provides methods for converting between a specific instant in
15
+ * time and a set of calendar fields such as YEAR, MONTH, DAY_OF_MONTH, HOUR, and so on,
16
+ * and for manipulating the calendar fields, such as getting the date of the next week.
17
+ * An instant in time can be represented by a millisecond value that is an offset from the
18
+ * Epoch, January 1, 1970 00:00:00.000 GMT (Gregorian).
19
+ *
20
+ * Also timezone info and methods are provided.
21
+ *
22
+ * NOTE: months are zero-based.
23
+ */
24
+
25
+ /**
26
+ * call-seq:
27
+ * UCalendar.time_zones
28
+ *
29
+ * Returns array with all time zones (as UString values).
30
+ */
31
+ VALUE icu4r_cal_all_tz (VALUE obj)
32
+ {
33
+ UErrorCode status = U_ZERO_ERROR;
34
+ UEnumeration * zones ;
35
+ VALUE ret ;
36
+ UChar * name;
37
+ int32_t len;
38
+ zones = ucal_openTimeZones (&status);
39
+ ICU_RAISE(status);
40
+ ret = rb_ary_new();
41
+ while( (name = (UChar*)uenum_unext(zones, &len, &status))) {
42
+ rb_ary_push(ret, icu_ustr_new(name, len));
43
+ }
44
+ uenum_close(zones);
45
+ return ret;
46
+ }
47
+
48
+
49
+ /**
50
+ * call-seq:
51
+ * UCalendar.tz_for_country(country)
52
+ *
53
+ * Returns array with all time zones associated with the given country.
54
+ * Note: <code>country</code> must be value of type String.
55
+ * Returned array content is UString's
56
+ *
57
+ * UCalendar.tz_for_country("GB") # => [ "Europe/Belfast", "Europe/London", "GB", "GB-Eire"]
58
+ *
59
+ */
60
+ VALUE icu4r_cal_country_tz (VALUE obj, VALUE ctry)
61
+ {
62
+ UErrorCode status = U_ZERO_ERROR;
63
+ UEnumeration * zones ;
64
+ VALUE ret ;
65
+ UChar * name;
66
+ int32_t len;
67
+ Check_Type(ctry, T_STRING);
68
+ zones = ucal_openCountryTimeZones (RSTRING_PTR(ctry), &status) ;
69
+ ICU_RAISE(status);
70
+ ret = rb_ary_new();
71
+ while( (name = (UChar*)uenum_unext(zones, &len, &status))) {
72
+ rb_ary_push(ret, icu_ustr_new(name, len));
73
+ }
74
+ uenum_close(zones);
75
+ return ret;
76
+ }
77
+
78
+ /**
79
+ * call-seq:
80
+ * UCalendar.default_tz => ustring
81
+ *
82
+ * Returns the default time zone name as UString.
83
+ *
84
+ * UCalendar.default_tz # "EET"
85
+ *
86
+ */
87
+ VALUE icu4r_cal_get_default_tz(VALUE obj)
88
+ {
89
+ UErrorCode status = U_ZERO_ERROR;
90
+ UChar * buf ;
91
+ long capa = 0;
92
+ capa = ucal_getDefaultTimeZone (buf, capa, &status);
93
+ if( U_BUFFER_OVERFLOW_ERROR == status) {
94
+ buf = ALLOC_N(UChar, capa+1);
95
+ status = U_ZERO_ERROR;
96
+ capa = ucal_getDefaultTimeZone (buf, capa, &status);
97
+ return icu_ustr_new_set(buf, capa, capa+1);
98
+ }
99
+ ICU_RAISE(status);
100
+ return Qnil;
101
+ }
102
+
103
+ /**
104
+ * call-seq:
105
+ * UCalendar.default_tz = ustring
106
+ *
107
+ * Set the default time zone.
108
+ *
109
+ * UCalendar.default_tz="GMT+00".u
110
+ * UCalendar.default_tz="Europe/Paris".u
111
+ */
112
+ VALUE icu4r_cal_set_default_tz(VALUE obj, VALUE tz)
113
+ {
114
+ UErrorCode status = U_ZERO_ERROR;
115
+ Check_Class(tz, rb_cUString);
116
+ ucal_setDefaultTimeZone (ICU_PTR(tz), &status);
117
+ ICU_RAISE(status);
118
+ return tz;
119
+ }
120
+ /**
121
+ * call-seq:
122
+ * UCalendar.dst_savings(zone_id)
123
+ *
124
+ * Return the amount of time in milliseconds that the clock is advanced
125
+ * during daylight savings time for the given time zone, or zero if the time
126
+ * zone does not observe daylight savings time.
127
+ *
128
+ * UCalendar.dst_savings("GMT+00".u) # => 3600000
129
+ *
130
+ */
131
+ VALUE icu4r_cal_dst_savings(VALUE obj, VALUE zone)
132
+ {
133
+ UErrorCode status = U_ZERO_ERROR;
134
+ int32_t dst;
135
+ Check_Class(zone, rb_cUString);
136
+ dst = ucal_getDSTSavings (ICU_PTR(zone), &status);
137
+ ICU_RAISE(status);
138
+ return INT2FIX(dst);
139
+ }
140
+
141
+ /**
142
+ * call-seq:
143
+ * UCalendar.now
144
+ *
145
+ * Get the current date and time. in millis
146
+ *
147
+ * UCalendar.now # => 1137934561000.0
148
+ *
149
+ */
150
+ VALUE icu4r_cal_now(VALUE obj){
151
+ return rb_float_new(ucal_getNow());
152
+ }
153
+ //-----------------
154
+
155
+ void icu4r_cal_free(UCalendar * cal){
156
+ ucal_close(cal);
157
+ }
158
+ static VALUE icu4r_cal_alloc(VALUE klass)
159
+ {
160
+ return Data_Wrap_Struct(klass, 0, icu4r_cal_free, 0);
161
+ }
162
+ /**
163
+ * call-seq:
164
+ * UCalendar.new(zone_id = nil, locale = nil, traditional = false)
165
+ *
166
+ * Creates new instance of UCalendar, for given time zone id (UString),
167
+ * locale (Ruby String), and kind , by default gregorian.
168
+ * New instance has current time.
169
+ *
170
+ */
171
+ VALUE icu4r_cal_init (int argc, VALUE * argv, VALUE self)
172
+ {
173
+ VALUE zone, loc, cal_type;
174
+ UChar * zone_id = NULL;
175
+ char * locale = NULL;
176
+ UCalendarType c_type = UCAL_GREGORIAN;
177
+ int32_t n, zone_len =0 , locale_len =0;
178
+ UCalendar * calendar;
179
+ UErrorCode status = U_ZERO_ERROR;
180
+ n = rb_scan_args(argc, argv, "03", &zone, &loc, &cal_type);
181
+ if( n >= 1) {
182
+ Check_Class(zone, rb_cUString);
183
+ zone_id = ICU_PTR(zone);
184
+ zone_len = ICU_LEN(zone);
185
+ }
186
+ if( n >= 2) {
187
+ Check_Type(loc, T_STRING);
188
+ locale = RSTRING_PTR(loc);
189
+ locale_len = RSTRING_LEN(loc);
190
+ }
191
+ if( n >= 3) {
192
+ if( Qtrue == cal_type ) {
193
+ c_type = UCAL_TRADITIONAL;
194
+ }
195
+ }
196
+ calendar = ucal_open(zone_id, zone_len, locale, c_type, &status);
197
+ ICU_RAISE(status);
198
+ DATA_PTR(self) = calendar;
199
+ return self;
200
+ }
201
+
202
+ int icu4r_get_cal_field_int(VALUE field)
203
+ {
204
+ VALUE field_const;
205
+ field_const = rb_hash_aref(s_calendar_fields, field);
206
+ if(field_const == Qnil)
207
+ rb_raise(rb_eArgError, "no such field %s", RSTRING_PTR(rb_obj_as_string(field)));
208
+ return NUM2INT(field_const);
209
+ }
210
+
211
+ /**
212
+ * call-seq:
213
+ * calendar.add(field, int_amount)
214
+ *
215
+ * Add a specified signed amount to a particular field in a UCalendar.
216
+ *
217
+ * c.add(:week_of_year, 2)
218
+ */
219
+ VALUE icu4r_cal_add(VALUE obj, VALUE field, VALUE amount)
220
+ {
221
+ UErrorCode status = U_ZERO_ERROR;
222
+ int date_field;
223
+ Check_Type(field, T_SYMBOL);
224
+ Check_Type(amount, T_FIXNUM);
225
+ date_field = icu4r_get_cal_field_int(field);
226
+ ucal_add(UCALENDAR(obj), date_field, FIX2INT(amount) , &status);
227
+ ICU_RAISE(status);
228
+ return Qnil;
229
+ }
230
+
231
+ /**
232
+ * call-seq:
233
+ * calendar.roll(field, int_amount)
234
+ *
235
+ * Adds a signed amount to the specified calendar field without changing larger fields.
236
+ * A negative roll amount means to subtract from field without changing larger fields.
237
+ * If the specified amount is 0, this method performs nothing.
238
+ *
239
+ * Example: Consider a GregorianCalendar originally set to August 31, 1999. Calling roll(:month, 8)
240
+ * sets the calendar to April 30, 1999. Using a Gregorian Calendar, the :day_of_month field cannot
241
+ * be 31 in the month April. :day_of_month is set to the closest possible value, 30. The :year field
242
+ * maintains the value of 1999 because it is a larger field than :month.
243
+ */
244
+ VALUE icu4r_cal_roll(VALUE obj, VALUE field, VALUE amount)
245
+ {
246
+ UErrorCode status = U_ZERO_ERROR;
247
+ int date_field;
248
+ Check_Type(field, T_SYMBOL);
249
+ Check_Type(amount, T_FIXNUM);
250
+ date_field = icu4r_get_cal_field_int(field);
251
+ ucal_roll(UCALENDAR(obj), date_field, FIX2INT(amount) , &status);
252
+ ICU_RAISE(status);
253
+ return Qnil;
254
+ }
255
+ /**
256
+ * call-seq:
257
+ * calendar[field]
258
+ *
259
+ * Get the current value of a field from a UCalendar.
260
+ *
261
+ */
262
+ VALUE icu4r_cal_aref(VALUE obj, VALUE field)
263
+ {
264
+ UErrorCode status = U_ZERO_ERROR;
265
+ int date_field;
266
+ int32_t value;
267
+ Check_Type(field, T_SYMBOL);
268
+ date_field = icu4r_get_cal_field_int(field);
269
+ value = ucal_get(UCALENDAR(obj), date_field, &status);
270
+ ICU_RAISE(status);
271
+ return INT2FIX(value);
272
+ }
273
+
274
+ /**
275
+ * call-seq:
276
+ * calendar[field]=int_value
277
+ *
278
+ * Set the value of a field in a UCalendar.
279
+ */
280
+ VALUE icu4r_cal_aset(VALUE obj, VALUE field, VALUE amount)
281
+ {
282
+ int date_field, ret;
283
+ UErrorCode status = U_ZERO_ERROR;
284
+ Check_Type(field, T_SYMBOL);
285
+ Check_Type(amount, T_FIXNUM);
286
+ date_field = icu4r_get_cal_field_int(field);
287
+ ucal_set(UCALENDAR(obj), date_field, FIX2INT(amount) );
288
+ ret = ucal_get(UCALENDAR(obj), date_field, &status);
289
+
290
+ return INT2FIX(ret);
291
+ }
292
+ /**
293
+ * call-seq:
294
+ * calendar.millis
295
+ *
296
+ * return this calendar value in milliseconds.
297
+ */
298
+ VALUE icu4r_cal_millis(VALUE obj)
299
+ {
300
+ UErrorCode status = U_ZERO_ERROR;
301
+ double millis;
302
+ millis = ucal_getMillis(UCALENDAR(obj), &status);
303
+ ICU_RAISE(status);
304
+ return rb_float_new(millis);
305
+ }
306
+ /**
307
+ * call-seq:
308
+ * calendar.millis = new_value
309
+ *
310
+ * Sets calendar's value in milliseconds.
311
+ */
312
+ VALUE icu4r_cal_set_millis(VALUE obj,VALUE milli)
313
+ {
314
+ UErrorCode status = U_ZERO_ERROR;
315
+ Check_Type(milli, T_FLOAT);
316
+ ucal_setMillis(UCALENDAR(obj), rb_num2dbl(milli), &status);
317
+ ICU_RAISE(status);
318
+ return Qnil;
319
+ }
320
+
321
+ /**
322
+ * call-seq:
323
+ * calendar.set_date(year, month, date)
324
+ *
325
+ * Set a UCalendar's current date.
326
+ */
327
+ VALUE icu4r_cal_set_date(VALUE obj,VALUE year, VALUE mon, VALUE date)
328
+ {
329
+ UErrorCode status = U_ZERO_ERROR;
330
+ Check_Type(year, T_FIXNUM);
331
+ Check_Type(mon, T_FIXNUM);
332
+ Check_Type(date, T_FIXNUM);
333
+ ucal_setDate(UCALENDAR(obj), FIX2INT(year), FIX2INT(mon), FIX2INT(date), &status);
334
+ ICU_RAISE(status);
335
+ return Qnil;
336
+ }
337
+ /**
338
+ * call-seq:
339
+ * calendar.set_date_time(year, month, date, hour, minute, second)
340
+ *
341
+ * Set a UCalendar's current date and time.
342
+ */
343
+ VALUE icu4r_cal_set_date_time(VALUE obj,VALUE year, VALUE mon, VALUE date, VALUE hour, VALUE min, VALUE sec)
344
+ {
345
+ UErrorCode status = U_ZERO_ERROR;
346
+ Check_Type(year, T_FIXNUM);
347
+ Check_Type(mon, T_FIXNUM);
348
+ Check_Type(date, T_FIXNUM);
349
+ Check_Type(hour, T_FIXNUM);
350
+ Check_Type(min, T_FIXNUM);
351
+ Check_Type(sec, T_FIXNUM);
352
+
353
+ ucal_setDateTime(UCALENDAR(obj),
354
+ FIX2INT(year), FIX2INT(mon), FIX2INT(date),
355
+ FIX2INT(hour), FIX2INT(min), FIX2INT(sec),
356
+ &status);
357
+ ICU_RAISE(status);
358
+ return Qnil;
359
+ }
360
+
361
+ /**
362
+ * call-seq:
363
+ * calendar.time_zone = zone_id
364
+ *
365
+ * Set the TimeZone used by a UCalendar.
366
+ */
367
+ VALUE icu4r_cal_set_tz(VALUE obj, VALUE zone)
368
+ {
369
+ UErrorCode status = U_ZERO_ERROR;
370
+ Check_Class(zone, rb_cUString);
371
+ ucal_setTimeZone(UCALENDAR(obj), ICU_PTR(zone), ICU_LEN(zone), &status);
372
+ ICU_RAISE(status);
373
+ return Qnil;
374
+
375
+ }
376
+
377
+ /**
378
+ * call-seq:
379
+ * calendar.in_daylight_time?
380
+ *
381
+ * Determine if a UCalendar is currently in daylight savings time.
382
+ *
383
+ * Daylight savings time is not used in all parts of the world
384
+ */
385
+ VALUE icu4r_cal_in_daylight(VALUE obj)
386
+ {
387
+ UErrorCode status = U_ZERO_ERROR;
388
+ int32_t answer;
389
+ answer = ucal_inDaylightTime(UCALENDAR(obj), &status);
390
+ ICU_RAISE(status);
391
+ return answer ? Qtrue : Qfalse;
392
+ }
393
+
394
+ /**
395
+ * call-seq:
396
+ * calendar.time_zone(locale = nil)
397
+ *
398
+ * Returns the TimeZone name used in this UCalendar. Name is returned in requested locale or default, if not set.
399
+ */
400
+ VALUE icu4r_cal_get_tz (int argc, VALUE * argv, VALUE obj)
401
+ {
402
+ UErrorCode status = U_ZERO_ERROR;
403
+ UChar * buf = NULL;
404
+ long capa = 0;
405
+ char *locale = NULL;
406
+ VALUE loc;
407
+ if( rb_scan_args(argc, argv, "01", &loc) == 1){
408
+ Check_Type(loc, T_STRING);
409
+ locale = RSTRING_PTR(loc);
410
+ }
411
+
412
+ capa = ucal_getTimeZoneDisplayName(UCALENDAR(obj), UCAL_STANDARD, locale, buf, capa, &status);
413
+ if( U_BUFFER_OVERFLOW_ERROR == status) {
414
+ buf = ALLOC_N(UChar, capa+1);
415
+ status = U_ZERO_ERROR;
416
+ capa = ucal_getTimeZoneDisplayName(UCALENDAR(obj), UCAL_STANDARD, locale, buf, capa, &status);
417
+ return icu_ustr_new_set(buf, capa, capa+1);
418
+ }
419
+ ICU_RAISE(status);
420
+ return Qnil;
421
+
422
+ }
423
+ int icu4r_get_cal_format_int(VALUE field)
424
+ {
425
+ VALUE field_const;
426
+ field_const = rb_hash_aref(s_calendar_formats, field);
427
+ if(field_const == Qnil) {
428
+ rb_warn("no such format %s , using default", RSTRING_PTR(rb_obj_as_string(field)));
429
+ return UDAT_DEFAULT;
430
+ }
431
+ return NUM2INT(field_const);
432
+ }
433
+ /** call-seq:
434
+ * calendar.format(pattern = nil , locale = nil)
435
+ *
436
+ * Formats this calendar time using given pattern and locale. Returns UString or nil on failure.
437
+ * Valid value types for pattern are:
438
+ * nil - long format for date and time
439
+ * UString - specification of format, as defined in docs/FORMATTING
440
+ * Symbol - one of :short, :medium, :long, :full, :none , sets format for both date and time
441
+ * Hash - {:time => aSymbol, :date => aSymbol} - sets separate formats for date and time, valid symbols see above
442
+ */
443
+ VALUE icu4r_cal_format(int argc, VALUE * argv, VALUE obj)
444
+ {
445
+ UErrorCode status = U_ZERO_ERROR;
446
+ UDateFormat * format;
447
+ UDate time_to_format;
448
+ UChar * buf = NULL, * pattern = NULL;
449
+ long capa = 0, pattern_len = 0;
450
+ char *locale = NULL;
451
+ VALUE loc, pat, ret = Qnil;
452
+ int n , def_d_format = UDAT_FULL, def_t_format = UDAT_FULL;
453
+
454
+ n = rb_scan_args(argc, argv, "02", &pat, &loc);
455
+ if( n == 2) {
456
+ Check_Type(loc, T_STRING);
457
+ locale = RSTRING_PTR(loc);
458
+ }
459
+ if (n >= 1 && pat != Qnil) {
460
+ switch(TYPE(pat)) {
461
+ case T_SYMBOL:
462
+ def_d_format = def_t_format = icu4r_get_cal_format_int(pat);
463
+ break;
464
+ case T_HASH:
465
+ def_d_format = icu4r_get_cal_format_int(rb_hash_aref(pat, ID2SYM(rb_intern("date"))));
466
+ def_t_format = icu4r_get_cal_format_int(rb_hash_aref(pat, ID2SYM(rb_intern("time"))));
467
+ break;
468
+ default:
469
+ Check_Class(pat, rb_cUString);
470
+ pattern = ICU_PTR(pat);
471
+ pattern_len = ICU_LEN(pat);
472
+ break;
473
+ }
474
+ }
475
+
476
+ format = udat_open(def_t_format, def_d_format, locale, NULL, 0, NULL, 0, &status);
477
+ if( pattern ) {
478
+ udat_applyPattern(format, 0, pattern, pattern_len);
479
+ }
480
+ ICU_RAISE(status);
481
+ udat_setCalendar(format, UCALENDAR(obj));
482
+ time_to_format = ucal_getMillis(UCALENDAR(obj), &status);
483
+
484
+ capa = udat_format(format, time_to_format, buf, capa, NULL, &status);
485
+ if( U_BUFFER_OVERFLOW_ERROR == status) {
486
+ buf = ALLOC_N(UChar, capa+1);
487
+ status = U_ZERO_ERROR;
488
+ capa = udat_format(format, time_to_format, buf, capa, NULL, &status);
489
+ ret = icu_ustr_new_set(buf, capa, capa+1);
490
+ }
491
+ udat_close(format);
492
+ ICU_RAISE(status);
493
+ return ret;
494
+ }
495
+
496
+ /**
497
+ * Document-method: clone
498
+ *
499
+ * call-seq:
500
+ * cal.clone => UCalendar
501
+ *
502
+ * Create and return a copy of this calendar.
503
+ */
504
+ extern VALUE icu4r_cal_clone(VALUE obj);
505
+ /**
506
+ * Document-method: eql?
507
+ *
508
+ * call-seq:
509
+ * cal.eql?(other)
510
+ *
511
+ * Compares the equality of two UCalendar objects.
512
+ *
513
+ * This comparison is very exacting; two UCalendar objects must be in exactly the
514
+ * same state to be considered equal.
515
+ */
516
+ extern VALUE icu4r_cal_equal(VALUE obj, VALUE other);
517
+
518
+ /**
519
+ * call-seq:
520
+ * cal <=> other_cal => -1, 0, +1
521
+ *
522
+ * Comparison---Returns -1 if <i>other_cal</i> is before than, 0 if
523
+ * <i>other_cal</i> is equal to, and +1 if <i>other_cal</i> is after than
524
+ * <i>str</i>.
525
+ *
526
+ * Value of calendar's milliseconds are compared.
527
+ */
528
+
529
+ VALUE icu4r_cal_cmp (VALUE c1, VALUE c2)
530
+ {
531
+ UErrorCode status = U_ZERO_ERROR;
532
+ double millis1, millis2;
533
+ Check_Class(c1, rb_cUCalendar);
534
+ Check_Class(c2, rb_cUCalendar);
535
+ millis1 = ucal_getMillis(UCALENDAR(c1), &status);
536
+ millis2 = ucal_getMillis(UCALENDAR(c2), &status);
537
+ ICU_RAISE(status);
538
+ if(millis1 < millis2) return INT2FIX(-1);
539
+ if(millis1 > millis2) return INT2FIX(1);
540
+ return INT2FIX(0);
541
+ }
542
+ /* parsing */
543
+ extern UCalendar * icu_date_parse(UChar * str, int32_t str_len, char * locale, UChar * val, int32_t len);
544
+
545
+ /**
546
+ * call-seq:
547
+ * UCalendar.parse( pattern, locale, value)
548
+ *
549
+ * Parses given value, using format pattern with respect to +locale+.
550
+ *
551
+ * UCalendar.parse("HH:mm:ss E dd/MM/yyyy".u, "en", "20:15:01 Fri 13/01/2006".u) # => Time.local(2006,"jan",13,20,15,1)
552
+ *
553
+ */
554
+
555
+ VALUE
556
+ icu4r_cal_parse( obj, str, locale, val)
557
+ VALUE obj, str, locale, val;
558
+ {
559
+ UCalendar * cal;
560
+ VALUE ret;
561
+ Check_Type(locale, T_STRING);
562
+ Check_Class(val, rb_cUString);
563
+ cal = icu_date_parse(ICU_PTR(str), ICU_LEN(str), RSTRING_PTR(locale), ICU_PTR(val), ICU_LEN(val));
564
+ ret = Data_Wrap_Struct(obj, 0, icu4r_cal_free, cal);
565
+ return ret;
566
+ }
567
+
568
+ void initialize_calendar(void) {
569
+
570
+ rb_cUCalendar = rb_define_class("UCalendar", rb_cObject);
571
+ rb_define_alloc_func(rb_cUCalendar, icu4r_cal_alloc);
572
+
573
+ s_calendar_fields = rb_hash_new();
574
+ /* Valid symbols to use as field reference in UCalendar#[], UCalendar#[]=, UCalendar#add are:
575
+ :era , :year , :month , :week_of_year , :week_of_month , :date , :day_of_year , :day_of_week, :day_of_week_in_month,
576
+ :am_pm , :hour , :hour_of_day , :minute , :second , :millisecond , :zone_offset , :dst_offset:
577
+ */
578
+ rb_define_const(rb_cUCalendar, "UCALENDAR_FIELDS", s_calendar_fields);
579
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("era")), INT2NUM(UCAL_ERA ));
580
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("year")), INT2NUM(UCAL_YEAR ));
581
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("month")), INT2NUM(UCAL_MONTH));
582
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("week_of_year")), INT2NUM(UCAL_WEEK_OF_YEAR ));
583
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("week_of_month")), INT2NUM(UCAL_WEEK_OF_MONTH));
584
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("date")), INT2NUM(UCAL_DATE));
585
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("day_of_year")), INT2NUM(UCAL_DAY_OF_YEAR));
586
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("day_of_week")), INT2NUM(UCAL_DAY_OF_WEEK));
587
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("day_of_week_in_month")), INT2NUM(UCAL_DAY_OF_WEEK_IN_MONTH));
588
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("am_pm")), INT2NUM(UCAL_AM_PM));
589
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("hour")), INT2NUM(UCAL_HOUR));
590
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("hour_of_day")), INT2NUM(UCAL_HOUR_OF_DAY));
591
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("minute")), INT2NUM(UCAL_MINUTE ));
592
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("second")), INT2NUM(UCAL_SECOND));
593
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("millisecond")), INT2NUM(UCAL_MILLISECOND ));
594
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("zone_offset")), INT2NUM(UCAL_ZONE_OFFSET));
595
+ rb_hash_aset(s_calendar_fields, ID2SYM(rb_intern("dst_offset")), INT2NUM(UCAL_DST_OFFSET));
596
+
597
+ s_calendar_formats = rb_hash_new();
598
+ rb_define_const(rb_cUCalendar, "UCALENDAR_FORMATS", s_calendar_formats);
599
+ rb_hash_aset(s_calendar_formats, ID2SYM(rb_intern("full")), INT2NUM(UDAT_FULL));
600
+ rb_hash_aset(s_calendar_formats, ID2SYM(rb_intern("long")), INT2NUM(UDAT_LONG));
601
+ rb_hash_aset(s_calendar_formats, ID2SYM(rb_intern("medium")), INT2NUM(UDAT_MEDIUM));
602
+ rb_hash_aset(s_calendar_formats, ID2SYM(rb_intern("short")), INT2NUM(UDAT_SHORT));
603
+ rb_hash_aset(s_calendar_formats, ID2SYM(rb_intern("default")), INT2NUM(UDAT_DEFAULT));
604
+ rb_hash_aset(s_calendar_formats, ID2SYM(rb_intern("none")), INT2NUM(UDAT_NONE));
605
+
606
+
607
+ rb_define_singleton_method(rb_cUCalendar, "now", icu4r_cal_now, 0);
608
+
609
+ rb_define_singleton_method(rb_cUCalendar, "default_tz=", icu4r_cal_set_default_tz, 1);
610
+ rb_define_singleton_method(rb_cUCalendar, "default_tz", icu4r_cal_get_default_tz, 0);
611
+ rb_define_singleton_method(rb_cUCalendar, "time_zones", icu4r_cal_all_tz, 0);
612
+ rb_define_singleton_method(rb_cUCalendar, "tz_for_country", icu4r_cal_country_tz, 1);
613
+ rb_define_singleton_method(rb_cUCalendar, "dst_savings", icu4r_cal_dst_savings, 1);
614
+ rb_define_singleton_method(rb_cUCalendar, "parse", icu4r_cal_parse, 3);
615
+
616
+ rb_define_method(rb_cUCalendar, "initialize", icu4r_cal_init, -1);
617
+ rb_define_method(rb_cUCalendar, "add", icu4r_cal_add, 2);
618
+ rb_define_method(rb_cUCalendar, "roll", icu4r_cal_roll, 2);
619
+ rb_define_method(rb_cUCalendar, "[]", icu4r_cal_aref, 1);
620
+ rb_define_method(rb_cUCalendar, "[]=", icu4r_cal_aset, 2);
621
+ rb_define_method(rb_cUCalendar, "millis=", icu4r_cal_set_millis, 1);
622
+ rb_define_method(rb_cUCalendar, "millis", icu4r_cal_millis,0);
623
+ rb_define_method(rb_cUCalendar, "set_date", icu4r_cal_set_date,3);
624
+ rb_define_method(rb_cUCalendar, "set_date_time", icu4r_cal_set_date_time,6);
625
+ rb_define_method(rb_cUCalendar, "time_zone=", icu4r_cal_set_tz,1);
626
+ rb_define_method(rb_cUCalendar, "time_zone", icu4r_cal_get_tz,-1);
627
+ rb_define_method(rb_cUCalendar, "in_daylight_time?", icu4r_cal_in_daylight,0);
628
+ rb_define_method(rb_cUCalendar, "format", icu4r_cal_format,-1);
629
+
630
+ rb_define_method(rb_cUCalendar, "clone", icu4r_cal_clone,0);
631
+ rb_define_method(rb_cUCalendar, "eql?", icu4r_cal_equal,1);
632
+ rb_define_method(rb_cUCalendar, "<=>", icu4r_cal_cmp,1);
633
+
634
+ rb_include_module(rb_cUCalendar, rb_mComparable);
635
+
636
+ }