diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.cpp index 634702e129..b2dd50f7fd 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.cpp @@ -42,55 +42,6 @@ * @{ */ -/** - * Insert leading zeros to a string of a number if needed. - */ -static void -ecma_date_insert_leading_zeros (ecma_string_t **str_p, /**< input/output string */ - ecma_number_t num, /**< input number */ - uint32_t length) /**< length of string of number */ -{ - JERRY_ASSERT (length >= 1); - - /* If the length is bigger than the number of digits in num, then insert leding zeros. */ - uint32_t first_index = length - 1u; - ecma_number_t power_i = (ecma_number_t) pow (10, first_index); - for (uint32_t i = first_index; i > 0 && num < power_i; i--, power_i /= 10) - { - ecma_string_t *zero_str_p = ecma_new_ecma_string_from_uint32 (0); - ecma_string_t *concat_p = ecma_concat_ecma_strings (zero_str_p, *str_p); - ecma_deref_ecma_string (zero_str_p); - ecma_deref_ecma_string (*str_p); - *str_p = concat_p; - } -} /* ecma_date_insert_leading_zeros */ - -/** - * Insert a number to the start of a string with a specific separator character and - * fix length. If the length is bigger than the number of digits in num, then insert leding zeros. - */ -static void -ecma_date_insert_num_with_sep (ecma_string_t **str_p, /**< input/output string */ - ecma_number_t num, /**< input number */ - lit_magic_string_id_t magic_str_id, /**< separator character id */ - uint32_t length) /**< length of string of number */ -{ - ecma_string_t *magic_string_p = ecma_get_magic_string (magic_str_id); - - ecma_string_t *concat_p = ecma_concat_ecma_strings (magic_string_p, *str_p); - ecma_deref_ecma_string (magic_string_p); - ecma_deref_ecma_string (*str_p); - *str_p = concat_p; - - ecma_string_t *num_str_p = ecma_new_ecma_string_from_number (num); - concat_p = ecma_concat_ecma_strings (num_str_p, *str_p); - ecma_deref_ecma_string (num_str_p); - ecma_deref_ecma_string (*str_p); - *str_p = concat_p; - - ecma_date_insert_leading_zeros (str_p, num, length); -} /* ecma_date_insert_num_with_sep */ - /** * The Date.prototype object's 'toString' routine * @@ -103,66 +54,7 @@ ecma_date_insert_num_with_sep (ecma_string_t **str_p, /**< input/output string * static ecma_completion_value_t ecma_builtin_date_prototype_to_string (ecma_value_t this_arg) /**< this argument */ { - ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - - if (!ecma_is_value_object (this_arg) - || ecma_object_get_class_name (ecma_get_object_from_value (this_arg)) != LIT_MAGIC_STRING_DATE_UL) - { - ret_value = ecma_raise_type_error ("Incompatible type"); - } - else - { - ECMA_TRY_CATCH (obj_this, - ecma_op_to_object (this_arg), - ret_value); - - ecma_object_t *obj_p = ecma_get_object_from_value (obj_this); - ecma_property_t *prim_value_prop_p; - prim_value_prop_p = ecma_get_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_PRIMITIVE_NUMBER_VALUE); - ecma_number_t *prim_value_num_p = ECMA_GET_NON_NULL_POINTER (ecma_number_t, - prim_value_prop_p->u.internal_property.value); - - if (ecma_number_is_nan (*prim_value_num_p)) - { - ecma_string_t *magic_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_INVALID_DATE_UL); - ret_value = ecma_make_normal_completion_value (ecma_make_string_value (magic_str_p)); - } - else - { - ecma_number_t milliseconds = ecma_date_ms_from_time (*prim_value_num_p); - ecma_string_t *output_str_p = ecma_new_ecma_string_from_number (milliseconds); - ecma_date_insert_leading_zeros (&output_str_p, milliseconds, 3); - - ecma_number_t seconds = ecma_date_sec_from_time (*prim_value_num_p); - ecma_date_insert_num_with_sep (&output_str_p, seconds, LIT_MAGIC_STRING_DOT_CHAR, 2); - - ecma_number_t minutes = ecma_date_min_from_time (*prim_value_num_p); - ecma_date_insert_num_with_sep (&output_str_p, minutes, LIT_MAGIC_STRING_COLON_CHAR, 2); - - ecma_number_t hours = ecma_date_hour_from_time (*prim_value_num_p); - ecma_date_insert_num_with_sep (&output_str_p, hours, LIT_MAGIC_STRING_COLON_CHAR, 2); - - ecma_number_t day = ecma_date_date_from_time (*prim_value_num_p); - ecma_date_insert_num_with_sep (&output_str_p, day, LIT_MAGIC_STRING_TIME_SEP_U, 2); - - /* - * Note: - * 'ecma_date_month_from_time' (ECMA 262 v5, 15.9.1.4) returns a number from 0 to 11, - * but we have to print the month from 1 to 12 for ISO 8601 standard (ECMA 262 v5, 15.9.1.15). - */ - ecma_number_t month = ecma_date_month_from_time (*prim_value_num_p) + 1; - ecma_date_insert_num_with_sep (&output_str_p, month, LIT_MAGIC_STRING_MINUS_CHAR, 2); - - ecma_number_t year = ecma_date_year_from_time (*prim_value_num_p); - ecma_date_insert_num_with_sep (&output_str_p, year, LIT_MAGIC_STRING_MINUS_CHAR, 4); - - ret_value = ecma_make_normal_completion_value (ecma_make_string_value (output_str_p)); - } - - ECMA_FINALIZE (obj_this); - } - - return ret_value; + return ecma_date_to_string (this_arg, ECMA_DATE_LOCAL); } /* ecma_builtin_date_prototype_to_string */ /** @@ -1192,7 +1084,7 @@ ecma_builtin_date_prototype_set_utc_full_year (ecma_value_t this_arg, /**< this static ecma_completion_value_t ecma_builtin_date_prototype_to_utc_string (ecma_value_t this_arg) /**< this argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg); + return ecma_date_to_string (this_arg, ECMA_DATE_UTC); } /* ecma_builtin_date_prototype_to_utc_string */ /** @@ -1207,7 +1099,7 @@ ecma_builtin_date_prototype_to_utc_string (ecma_value_t this_arg) /**< this argu static ecma_completion_value_t ecma_builtin_date_prototype_to_iso_string (ecma_value_t this_arg) /**< this argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg); + return ecma_date_to_string (this_arg, ECMA_DATE_UTC); } /* ecma_builtin_date_prototype_to_iso_string */ /** diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.cpp index 1b99d69f3b..6efe0bec81 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.cpp @@ -16,8 +16,11 @@ #include "ecma-alloc.h" #include "ecma-builtin-helpers.h" +#include "ecma-exceptions.h" #include "ecma-globals.h" #include "ecma-helpers.h" +#include "ecma-objects.h" +#include "ecma-try-catch-macro.h" #include "fdlibm-math.h" #ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_DATE_BUILTIN @@ -847,6 +850,143 @@ ecma_date_set_internal_property (ecma_value_t this_arg, /**< this argument */ return ecma_make_normal_completion_value (ecma_make_number_value (value_p)); } /* ecma_date_set_internal_property */ +/** + * Insert leading zeros to a string of a number if needed. + */ +void +ecma_date_insert_leading_zeros (ecma_string_t **str_p, /**< input/output string */ + ecma_number_t num, /**< input number */ + uint32_t length) /**< length of string of number */ +{ + JERRY_ASSERT (length >= 1); + + /* If the length is bigger than the number of digits in num, then insert leding zeros. */ + uint32_t first_index = length - 1u; + ecma_number_t power_i = (ecma_number_t) pow (10, first_index); + for (uint32_t i = first_index; i > 0 && num < power_i; i--, power_i /= 10) + { + ecma_string_t *zero_str_p = ecma_new_ecma_string_from_uint32 (0); + ecma_string_t *concat_p = ecma_concat_ecma_strings (zero_str_p, *str_p); + ecma_deref_ecma_string (zero_str_p); + ecma_deref_ecma_string (*str_p); + *str_p = concat_p; + } +} /* ecma_date_insert_leading_zeros */ + +/** + * Insert a number to the start of a string with a specific separator character and + * fix length. If the length is bigger than the number of digits in num, then insert leding zeros. + */ +void +ecma_date_insert_num_with_sep (ecma_string_t **str_p, /**< input/output string */ + ecma_number_t num, /**< input number */ + lit_magic_string_id_t magic_str_id, /**< separator character id */ + uint32_t length) /**< length of string of number */ +{ + ecma_string_t *magic_string_p = ecma_get_magic_string (magic_str_id); + + ecma_string_t *concat_p = ecma_concat_ecma_strings (magic_string_p, *str_p); + ecma_deref_ecma_string (magic_string_p); + ecma_deref_ecma_string (*str_p); + *str_p = concat_p; + + ecma_string_t *num_str_p = ecma_new_ecma_string_from_number (num); + concat_p = ecma_concat_ecma_strings (num_str_p, *str_p); + ecma_deref_ecma_string (num_str_p); + ecma_deref_ecma_string (*str_p); + *str_p = concat_p; + + ecma_date_insert_leading_zeros (str_p, num, length); +} /* ecma_date_insert_num_with_sep */ + +/** + * Common function to create a time zone specific string. + * + * Used by: + * - The Date.prototype.toString routine. + * - The Date.prototype.toISOString routine. + * - The Date.prototype.toUTCString routine. + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +ecma_completion_value_t +ecma_date_to_string (ecma_value_t this_arg, /**< this argument */ + ecma_date_timezone_t timezone) /**< timezone */ +{ + TODO ("Add support for local time zone output."); + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + if (!ecma_is_value_object (this_arg) + || ecma_object_get_class_name (ecma_get_object_from_value (this_arg)) != LIT_MAGIC_STRING_DATE_UL) + { + ret_value = ecma_raise_type_error ("Incompatible type"); + } + else + { + ECMA_TRY_CATCH (obj_this, + ecma_op_to_object (this_arg), + ret_value); + + ecma_object_t *obj_p = ecma_get_object_from_value (obj_this); + ecma_property_t *prim_value_prop_p; + prim_value_prop_p = ecma_get_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_PRIMITIVE_NUMBER_VALUE); + ecma_number_t *prim_value_num_p = ECMA_GET_NON_NULL_POINTER (ecma_number_t, + prim_value_prop_p->u.internal_property.value); + + if (ecma_number_is_nan (*prim_value_num_p)) + { + ecma_string_t *magic_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_INVALID_DATE_UL); + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (magic_str_p)); + } + else + { + ecma_string_t *output_str_p; + ecma_number_t milliseconds = ecma_date_ms_from_time (*prim_value_num_p); + + if (timezone == ECMA_DATE_UTC) + { + output_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + ecma_date_insert_num_with_sep (&output_str_p, milliseconds, LIT_MAGIC_STRING_Z_CHAR, 3); + } + else + { + output_str_p = ecma_new_ecma_string_from_number (milliseconds); + ecma_date_insert_leading_zeros (&output_str_p, milliseconds, 3); + } + + ecma_number_t seconds = ecma_date_sec_from_time (*prim_value_num_p); + ecma_date_insert_num_with_sep (&output_str_p, seconds, LIT_MAGIC_STRING_DOT_CHAR, 2); + + ecma_number_t minutes = ecma_date_min_from_time (*prim_value_num_p); + ecma_date_insert_num_with_sep (&output_str_p, minutes, LIT_MAGIC_STRING_COLON_CHAR, 2); + + ecma_number_t hours = ecma_date_hour_from_time (*prim_value_num_p); + ecma_date_insert_num_with_sep (&output_str_p, hours, LIT_MAGIC_STRING_COLON_CHAR, 2); + + ecma_number_t day = ecma_date_date_from_time (*prim_value_num_p); + ecma_date_insert_num_with_sep (&output_str_p, day, LIT_MAGIC_STRING_TIME_SEP_U, 2); + + /* + * Note: + * 'ecma_date_month_from_time' (ECMA 262 v5, 15.9.1.4) returns a number from 0 to 11, + * but we have to print the month from 1 to 12 for ISO 8601 standard (ECMA 262 v5, 15.9.1.15). + */ + ecma_number_t month = ecma_date_month_from_time (*prim_value_num_p) + 1; + ecma_date_insert_num_with_sep (&output_str_p, month, LIT_MAGIC_STRING_MINUS_CHAR, 2); + + ecma_number_t year = ecma_date_year_from_time (*prim_value_num_p); + ecma_date_insert_num_with_sep (&output_str_p, year, LIT_MAGIC_STRING_MINUS_CHAR, 4); + + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (output_str_p)); + } + + ECMA_FINALIZE (obj_this); + } + + return ret_value; +} /* ecma_date_create_formatted_string */ + /** * @} * @} diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h index ca21336cea..68f1c1d52f 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -103,6 +103,14 @@ extern ecma_completion_value_t ecma_date_set_internal_property (ecma_value_t thi ecma_number_t day, ecma_number_t time, ecma_date_timezone_t is_utc); +extern void ecma_date_insert_leading_zeros (ecma_string_t **str_p, + ecma_number_t num, + uint32_t length); +extern void ecma_date_insert_num_with_sep (ecma_string_t **str_p, + ecma_number_t num, + lit_magic_string_id_t magic_str_id, + uint32_t length); +extern ecma_completion_value_t ecma_date_to_string (ecma_value_t this_arg, ecma_date_timezone_t timezone); #endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_DATE_BUILTIN */ typedef struct diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 6f0437936e..fc2b953b79 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -219,6 +219,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_G_CHAR, "g") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_I_CHAR, "i") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_M_CHAR, "m") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TIME_SEP_U, "T") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_Z_CHAR, "Z") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SLASH_CHAR, "/") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BACKSLASH_CHAR, "\\") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EMPTY_NON_CAPTURE_GROUP, "(?:)") diff --git a/tests/jerry/date-tostring.js b/tests/jerry/date-tostring.js index ab3fea66ca..0ca04a86b3 100644 --- a/tests/jerry/date-tostring.js +++ b/tests/jerry/date-tostring.js @@ -58,3 +58,33 @@ catch (e) assert (e instanceof TypeError); assert (e.message === "Incompatible type"); } + +assert (new Date (NaN).toISOString () == "Invalid Date"); +assert (new Date ("2015-07-16").toISOString () == "2015-07-16T00:00:00.000Z"); +assert (new Date ("2015-07-16T11:29:05.023").toISOString () == "2015-07-16T11:29:05.023Z"); + +try +{ + Date.prototype.toISOString.call(-1); + assert (false); +} +catch (e) +{ + assert (e instanceof TypeError); + assert (e.message === "Incompatible type"); +} + +assert (new Date (NaN).toUTCString () == "Invalid Date"); +assert (new Date ("2015-07-16").toUTCString () == "2015-07-16T00:00:00.000Z"); +assert (new Date ("2015-07-16T11:29:05.023").toUTCString () == "2015-07-16T11:29:05.023Z"); + +try +{ + Date.prototype.toUTCString.call(-1); + assert (false); +} +catch (e) +{ + assert (e instanceof TypeError); + assert (e.message === "Incompatible type"); +}