1
// Copyright 2011 the V8 project authors. All rights reserved.
2
// Redistribution and use in source and binary forms, with or without
3
// modification, are permitted provided that the following conditions are
6
// * Redistributions of source code must retain the above copyright
7
// notice, this list of conditions and the following disclaimer.
8
// * Redistributions in binary form must reproduce the above
9
// copyright notice, this list of conditions and the following
10
// disclaimer in the documentation and/or other materials provided
11
// with the distribution.
12
// * Neither the name of Google Inc. nor the names of its
13
// contributors may be used to endorse or promote products derived
14
// from this software without specific prior written permission.
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
#include "extensions/experimental/datetime-format.h"
32
#include "extensions/experimental/i18n-utils.h"
33
#include "unicode/dtfmtsym.h"
34
#include "unicode/dtptngen.h"
35
#include "unicode/locid.h"
36
#include "unicode/smpdtfmt.h"
40
v8::Persistent<v8::FunctionTemplate> DateTimeFormat::datetime_format_template_;
42
static icu::DateFormat* CreateDateTimeFormat(v8::Handle<v8::String>,
43
v8::Handle<v8::Object>);
44
static v8::Handle<v8::Value> GetSymbols(
46
const icu::UnicodeString*, int32_t,
47
const icu::UnicodeString*, int32_t,
48
const icu::UnicodeString*, int32_t);
49
static v8::Handle<v8::Value> ThrowUnexpectedObjectError();
50
static icu::DateFormat::EStyle GetDateTimeStyle(const icu::UnicodeString&);
52
icu::SimpleDateFormat* DateTimeFormat::UnpackDateTimeFormat(
53
v8::Handle<v8::Object> obj) {
54
if (datetime_format_template_->HasInstance(obj)) {
55
return static_cast<icu::SimpleDateFormat*>(
56
obj->GetPointerFromInternalField(0));
62
void DateTimeFormat::DeleteDateTimeFormat(v8::Persistent<v8::Value> object,
64
v8::Persistent<v8::Object> persistent_object =
65
v8::Persistent<v8::Object>::Cast(object);
67
// First delete the hidden C++ object.
68
// Unpacking should never return NULL here. That would only happen if
69
// this method is used as the weak callback for persistent handles not
70
// pointing to a date time formatter.
71
delete UnpackDateTimeFormat(persistent_object);
73
// Then dispose of the persistent handle to JS object.
74
persistent_object.Dispose();
77
v8::Handle<v8::Value> DateTimeFormat::Format(const v8::Arguments& args) {
78
v8::HandleScope handle_scope;
81
if (args.Length() != 1 || !args[0]->IsDate()) {
83
v8::TryCatch try_catch;
84
v8::Local<v8::Script> date_script =
85
v8::Script::Compile(v8::String::New("eval('new Date()')"));
86
millis = date_script->Run()->NumberValue();
87
if (try_catch.HasCaught()) {
88
return try_catch.ReThrow();
91
millis = v8::Date::Cast(*args[0])->NumberValue();
94
icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder());
96
return ThrowUnexpectedObjectError();
99
icu::UnicodeString result;
100
date_format->format(millis, result);
102
return v8::String::New(
103
reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length());
106
v8::Handle<v8::Value> DateTimeFormat::GetMonths(const v8::Arguments& args) {
107
icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder());
109
return ThrowUnexpectedObjectError();
112
const icu::DateFormatSymbols* symbols = date_format->getDateFormatSymbols();
114
int32_t narrow_count;
115
const icu::UnicodeString* narrow = symbols->getMonths(
117
icu::DateFormatSymbols::STANDALONE,
118
icu::DateFormatSymbols::NARROW);
119
int32_t abbrev_count;
120
const icu::UnicodeString* abbrev = symbols->getMonths(
122
icu::DateFormatSymbols::STANDALONE,
123
icu::DateFormatSymbols::ABBREVIATED);
125
const icu::UnicodeString* wide = symbols->getMonths(
127
icu::DateFormatSymbols::STANDALONE,
128
icu::DateFormatSymbols::WIDE);
131
args, narrow, narrow_count, abbrev, abbrev_count, wide, wide_count);
134
v8::Handle<v8::Value> DateTimeFormat::GetWeekdays(const v8::Arguments& args) {
135
icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder());
137
return ThrowUnexpectedObjectError();
140
const icu::DateFormatSymbols* symbols = date_format->getDateFormatSymbols();
142
int32_t narrow_count;
143
const icu::UnicodeString* narrow = symbols->getWeekdays(
145
icu::DateFormatSymbols::STANDALONE,
146
icu::DateFormatSymbols::NARROW);
147
int32_t abbrev_count;
148
const icu::UnicodeString* abbrev = symbols->getWeekdays(
150
icu::DateFormatSymbols::STANDALONE,
151
icu::DateFormatSymbols::ABBREVIATED);
153
const icu::UnicodeString* wide = symbols->getWeekdays(
155
icu::DateFormatSymbols::STANDALONE,
156
icu::DateFormatSymbols::WIDE);
158
// getXXXWeekdays always returns 8 elements - ICU stable API.
159
// We can't use ASSERT_EQ(8, narrow_count) because ASSERT is internal to v8.
160
if (narrow_count != 8 || abbrev_count != 8 || wide_count != 8) {
161
return v8::ThrowException(v8::Exception::Error(
162
v8::String::New("Failed to get weekday information.")));
165
// ICU documentation says we should ignore element 0 of the returned array.
166
return GetSymbols(args, narrow + 1, narrow_count - 1, abbrev + 1,
167
abbrev_count -1 , wide + 1, wide_count - 1);
170
v8::Handle<v8::Value> DateTimeFormat::GetEras(const v8::Arguments& args) {
171
icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder());
173
return ThrowUnexpectedObjectError();
176
const icu::DateFormatSymbols* symbols = date_format->getDateFormatSymbols();
178
int32_t narrow_count;
179
const icu::UnicodeString* narrow = symbols->getNarrowEras(narrow_count);
180
int32_t abbrev_count;
181
const icu::UnicodeString* abbrev = symbols->getEras(abbrev_count);
183
const icu::UnicodeString* wide = symbols->getEraNames(wide_count);
186
args, narrow, narrow_count, abbrev, abbrev_count, wide, wide_count);
189
v8::Handle<v8::Value> DateTimeFormat::GetAmPm(const v8::Arguments& args) {
190
icu::SimpleDateFormat* date_format = UnpackDateTimeFormat(args.Holder());
192
return ThrowUnexpectedObjectError();
195
const icu::DateFormatSymbols* symbols = date_format->getDateFormatSymbols();
197
// In this case narrow == abbreviated == wide
199
const icu::UnicodeString* wide = symbols->getAmPmStrings(count);
201
return GetSymbols(args, wide, count, wide, count, wide, count);
204
v8::Handle<v8::Value> DateTimeFormat::JSDateTimeFormat(
205
const v8::Arguments& args) {
206
v8::HandleScope handle_scope;
208
if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsObject()) {
209
return v8::ThrowException(v8::Exception::SyntaxError(
210
v8::String::New("Locale and date/time options are required.")));
213
icu::SimpleDateFormat* date_format = static_cast<icu::SimpleDateFormat*>(
214
CreateDateTimeFormat(args[0]->ToString(), args[1]->ToObject()));
216
if (datetime_format_template_.IsEmpty()) {
217
v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New());
219
raw_template->SetClassName(v8::String::New("v8Locale.DateTimeFormat"));
221
// Define internal field count on instance template.
222
v8::Local<v8::ObjectTemplate> object_template =
223
raw_template->InstanceTemplate();
225
// Set aside internal field for icu date time formatter.
226
object_template->SetInternalFieldCount(1);
228
// Define all of the prototype methods on prototype template.
229
v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate();
230
proto->Set(v8::String::New("format"),
231
v8::FunctionTemplate::New(Format));
232
proto->Set(v8::String::New("getMonths"),
233
v8::FunctionTemplate::New(GetMonths));
234
proto->Set(v8::String::New("getWeekdays"),
235
v8::FunctionTemplate::New(GetWeekdays));
236
proto->Set(v8::String::New("getEras"),
237
v8::FunctionTemplate::New(GetEras));
238
proto->Set(v8::String::New("getAmPm"),
239
v8::FunctionTemplate::New(GetAmPm));
241
datetime_format_template_ =
242
v8::Persistent<v8::FunctionTemplate>::New(raw_template);
245
// Create an empty object wrapper.
246
v8::Local<v8::Object> local_object =
247
datetime_format_template_->GetFunction()->NewInstance();
248
v8::Persistent<v8::Object> wrapper =
249
v8::Persistent<v8::Object>::New(local_object);
251
// Set date time formatter as internal field of the resulting JS object.
252
wrapper->SetPointerInInternalField(0, date_format);
254
// Set resolved pattern in options.pattern.
255
icu::UnicodeString pattern;
256
date_format->toPattern(pattern);
257
v8::Local<v8::Object> options = v8::Object::New();
258
options->Set(v8::String::New("pattern"),
259
v8::String::New(reinterpret_cast<const uint16_t*>(
260
pattern.getBuffer()), pattern.length()));
261
wrapper->Set(v8::String::New("options"), options);
263
// Make object handle weak so we can delete iterator once GC kicks in.
264
wrapper.MakeWeak(NULL, DeleteDateTimeFormat);
269
// Returns SimpleDateFormat.
270
static icu::DateFormat* CreateDateTimeFormat(
271
v8::Handle<v8::String> locale, v8::Handle<v8::Object> settings) {
272
v8::HandleScope handle_scope;
274
v8::String::AsciiValue ascii_locale(locale);
275
icu::Locale icu_locale(*ascii_locale);
277
// Make formatter from skeleton.
278
icu::SimpleDateFormat* date_format = NULL;
279
UErrorCode status = U_ZERO_ERROR;
280
icu::UnicodeString skeleton;
281
if (I18NUtils::ExtractStringSetting(settings, "skeleton", &skeleton)) {
282
v8::Local<icu::DateTimePatternGenerator> generator(
283
icu::DateTimePatternGenerator::createInstance(icu_locale, status));
284
icu::UnicodeString pattern =
285
generator->getBestPattern(skeleton, status);
287
date_format = new icu::SimpleDateFormat(pattern, icu_locale, status);
288
if (U_SUCCESS(status)) {
295
// Extract date style and time style from settings.
296
icu::UnicodeString date_style;
297
icu::DateFormat::EStyle icu_date_style = icu::DateFormat::kNone;
298
if (I18NUtils::ExtractStringSetting(settings, "dateStyle", &date_style)) {
299
icu_date_style = GetDateTimeStyle(date_style);
302
icu::UnicodeString time_style;
303
icu::DateFormat::EStyle icu_time_style = icu::DateFormat::kNone;
304
if (I18NUtils::ExtractStringSetting(settings, "timeStyle", &time_style)) {
305
icu_time_style = GetDateTimeStyle(time_style);
308
// Try all combinations of date/time styles.
309
if (icu_date_style == icu::DateFormat::kNone &&
310
icu_time_style == icu::DateFormat::kNone) {
311
// Return default short date, short
312
return icu::DateFormat::createDateTimeInstance(
313
icu::DateFormat::kShort, icu::DateFormat::kShort, icu_locale);
314
} else if (icu_date_style != icu::DateFormat::kNone &&
315
icu_time_style != icu::DateFormat::kNone) {
316
return icu::DateFormat::createDateTimeInstance(
317
icu_date_style, icu_time_style, icu_locale);
318
} else if (icu_date_style != icu::DateFormat::kNone) {
319
return icu::DateFormat::createDateInstance(icu_date_style, icu_locale);
321
// icu_time_style != icu::DateFormat::kNone
322
return icu::DateFormat::createTimeInstance(icu_time_style, icu_locale);
326
// Creates a v8::Array of narrow, abbrev or wide symbols.
327
static v8::Handle<v8::Value> GetSymbols(const v8::Arguments& args,
328
const icu::UnicodeString* narrow,
329
int32_t narrow_count,
330
const icu::UnicodeString* abbrev,
331
int32_t abbrev_count,
332
const icu::UnicodeString* wide,
333
int32_t wide_count) {
334
v8::HandleScope handle_scope;
336
// Make wide width default.
337
const icu::UnicodeString* result = wide;
338
int32_t count = wide_count;
340
if (args.Length() == 1 && args[0]->IsString()) {
341
v8::String::AsciiValue ascii_value(args[0]);
342
if (strcmp(*ascii_value, "abbreviated") == 0) {
344
count = abbrev_count;
345
} else if (strcmp(*ascii_value, "narrow") == 0) {
347
count = narrow_count;
351
v8::Handle<v8::Array> symbols = v8::Array::New();
352
for (int32_t i = 0; i < count; ++i) {
353
symbols->Set(i, v8::String::New(
354
reinterpret_cast<const uint16_t*>(result[i].getBuffer()),
355
result[i].length()));
358
return handle_scope.Close(symbols);
361
// Throws a JavaScript exception.
362
static v8::Handle<v8::Value> ThrowUnexpectedObjectError() {
363
// Returns undefined, and schedules an exception to be thrown.
364
return v8::ThrowException(v8::Exception::Error(
365
v8::String::New("DateTimeFormat method called on an object "
366
"that is not a DateTimeFormat.")));
369
// Returns icu date/time style.
370
static icu::DateFormat::EStyle GetDateTimeStyle(
371
const icu::UnicodeString& type) {
372
if (type == UNICODE_STRING_SIMPLE("medium")) {
373
return icu::DateFormat::kMedium;
374
} else if (type == UNICODE_STRING_SIMPLE("long")) {
375
return icu::DateFormat::kLong;
376
} else if (type == UNICODE_STRING_SIMPLE("full")) {
377
return icu::DateFormat::kFull;
380
return icu::DateFormat::kShort;
383
} // namespace v8_i18n