Index: src/runtime/runtime-i18n.cc |
diff --git a/src/runtime/runtime-i18n.cc b/src/runtime/runtime-i18n.cc |
index 8b9d92ec006eb570bf14755087d97987b8d04e2d..fd569ff55a214e8a236e85b3d3f8b6331474bcaf 100644 |
--- a/src/runtime/runtime-i18n.cc |
+++ b/src/runtime/runtime-i18n.cc |
@@ -25,6 +25,8 @@ |
#include "unicode/decimfmt.h" |
#include "unicode/dtfmtsym.h" |
#include "unicode/dtptngen.h" |
+#include "unicode/fieldpos.h" |
+#include "unicode/fpositer.h" |
#include "unicode/locid.h" |
#include "unicode/normalizer2.h" |
#include "unicode/numfmt.h" |
@@ -393,6 +395,140 @@ RUNTIME_FUNCTION(Runtime_InternalDateFormat) { |
result.length()))); |
} |
+namespace { |
+// The list comes from third_party/icu/source/i18n/unicode/udat.h. |
+// They're mapped to |
+// https://tc39.github.io/ecma402/#table-datetimeformat-components |
+// TODO(jshin): Map UDAT.*FIELD to DateTimeFormatPartType enums |
+// for each of which return NewStringFromStaticChars or 'AtomicString' |
+// if available. |
+const char* kIcuDateFieldIdToDateType[] = { |
+ "era", // UDAT_ERA_FIELD = 0, |
+ "year", // UDAT_YEAR_FIELD = 1, |
Dan Ehrenberg
2016/08/25 16:54:46
Nit: Maybe use the '[UDAT] = "year",' syntax for c
|
+ "month", // UDAT_MONTH_FIELD = 2, |
+ "day", // UDAT_DATE_FIELD = 3, |
+ "hour", // UDAT_HOUR_OF_DAY1_FIELD = 4, |
+ "hour", // UDAT_HOUR_OF_DAY0_FIELD = 5, |
+ "minute", // UDAT_MINUTE_FIELD = 6, |
+ "second", // UDAT_SECOND_FIELD = 7, |
+ nullptr, // UDAT_FRACTIONAL_SECOND_FIELD = 8, |
+ "weekday", // UDAT_DAY_OF_WEEK_FIELD = 9, |
+ nullptr, // UDAT_DAY_OF_YEAR_FIELD = 10, |
+ nullptr, // UDAT_DAY_OF_WEEK_IN_MONTH_FIELD = 11, |
+ nullptr, // UDAT_WEEK_OF_YEAR_FIELD = 12, |
+ nullptr, // UDAT_WEEK_OF_MONTH_FIELD = 13, |
+ "dayperiod", // UDAT_AM_PM_FIELD = 14, |
+ "hour", // UDAT_HOUR1_FIELD = 15, |
+ "hour", // UDAT_HOUR0_FIELD = 16, |
+ "timeZoneName", // UDAT_TIMEZONE_FIELD = 17, |
+ nullptr, // UDAT_YEAR_WOY_FIELD = 18, |
+ "weekday", // UDAT_DOW_LOCAL_FIELD = 19, |
+ "year", // UDAT_EXTENDED_YEAR_FIELD = 20, |
+ "day", // UDAT_JULIAN_DAY_FIELD = 21, |
+ nullptr, // UDAT_MILLISECONDS_IN_DAY_FIELD = 22, |
+ "timeZoneName", // UDAT_TIMEZONE_RFC_FIELD = 23, |
+ "timeZoneName", // UDAT_TIMEZONE_GENERIC_FIELD = 24, |
+ "weekday", // UDAT_STANDALONE_DAY_FIELD = 25, |
+ "month", // UDAT_STANDALONE_MONTH_FIELD = 26, |
+ nullptr, // UDAT_QUARTER_FIELD = 27, |
+ nullptr, // UDAT_STANDALONE_QUARTER_FIELD = 28, |
+ "timeZoneName", // UDAT_TIMEZONE_SPECIAL_FIELD = 29, |
+ "year", // UDAT_YEAR_NAME_FIELD = 30, |
+ "timeZoneName", // UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD = 31, |
+ "timeZoneName", // UDAT_TIMEZONE_ISO_FIELD = 32, |
+ "timeZoneName", // UDAT_TIMEZONE_ISO_LOCAL_FIELD = 33, |
+ nullptr, // UDAT_RELATED_YEAR_FIELD = 34, |
+ nullptr, // UDAT_TIME_SEPARATOR_FIELD = 35, |
+ nullptr, // UDAT_FIELD_COUNT = 36 |
+}; |
+ |
+bool AddElement(Handle<JSArray> array, int index, const char* type, |
+ const icu::UnicodeString& formatted, int32_t begin, int32_t end, |
+ Handle<Name> type_literal, Handle<Name> value_literal, |
+ Isolate* isolate) { |
+ HandleScope scope(isolate); |
+ Factory* factory = isolate->factory(); |
+ Handle<JSObject> element = factory->NewJSObject(isolate->object_function()); |
+ Handle<String> value = factory->NewStringFromAsciiChecked(type); |
+ JSObject::AddProperty(element, type_literal, value, NONE); |
+ |
+ icu::UnicodeString field(formatted.tempSubStringBetween(begin, end)); |
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
+ isolate, value, factory->NewStringFromTwoByte(Vector<const uint16_t>( |
+ reinterpret_cast<const uint16_t*>(field.getBuffer()), |
+ field.length())), |
+ false); |
+ |
+ JSObject::AddProperty(element, value_literal, value, NONE); |
+ RETURN_ON_EXCEPTION_VALUE( |
+ isolate, JSObject::AddDataElement(array, index, element, NONE), false); |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+RUNTIME_FUNCTION(Runtime_InternalDateFormatToParts) { |
+ HandleScope scope(isolate); |
+ Factory* factory = isolate->factory(); |
+ |
+ DCHECK(args.length() == 2); |
+ |
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0); |
+ CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 1); |
+ |
+ Handle<Object> value; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(date)); |
+ |
+ icu::SimpleDateFormat* date_format = |
+ DateFormat::UnpackDateFormat(isolate, date_format_holder); |
+ if (!date_format) return isolate->ThrowIllegalOperation(); |
+ |
+ icu::UnicodeString formatted; |
+ icu::FieldPositionIterator fpIter; |
+ icu::FieldPosition fp; |
+ UErrorCode status = U_ZERO_ERROR; |
+ date_format->format(value->Number(), formatted, &fpIter, status); |
+ if (U_FAILURE(status)) return isolate->heap()->undefined_value(); |
+ |
+ Handle<JSArray> result = factory->NewJSArray(0); |
+ Handle<Name> type_literal = factory->NewStringFromStaticChars("type"); |
+ Handle<Name> value_literal = factory->NewStringFromStaticChars("value"); |
+ |
+ int index = 0; |
+ int32_t previous_end_pos = 0; |
+ while (fpIter.next(fp)) { |
+ int32_t fieldId = fp.getField(); |
+ const char* type = kIcuDateFieldIdToDateType[fieldId]; |
+ int32_t begin_pos = fp.getBeginIndex(); |
+ int32_t end_pos = fp.getEndIndex(); |
+ if (type == nullptr) { |
Dan Ehrenberg
2016/08/25 16:54:46
Omitting these fields seems like strange behavior.
|
+ previous_end_pos = end_pos; |
+ continue; |
+ } |
+ if (previous_end_pos < begin_pos) { |
+ if (!AddElement(result, index, "literal", formatted, previous_end_pos, |
+ begin_pos, type_literal, value_literal, isolate)) { |
+ return isolate->heap()->undefined_value(); |
+ } |
+ ++index; |
+ } |
+ if (!AddElement(result, index, type, formatted, begin_pos, end_pos, |
+ type_literal, value_literal, isolate)) { |
+ return isolate->heap()->undefined_value(); |
+ } |
+ previous_end_pos = end_pos; |
+ ++index; |
+ } |
+ int32_t length = formatted.length(); |
+ if (previous_end_pos < length) { |
+ if (!AddElement(result, index, "literal", formatted, previous_end_pos, |
+ length, type_literal, value_literal, isolate)) { |
+ return isolate->heap()->undefined_value(); |
+ } |
+ } |
+ JSObject::ValidateElements(result); |
+ return *result; |
+} |
RUNTIME_FUNCTION(Runtime_InternalDateParse) { |
HandleScope scope(isolate); |