Skip to content

Implement Date.prototype.toDateString and Date.prototype.toTimeString #360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 100 additions & 5 deletions jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ ecma_date_insert_leading_zeros (ecma_string_t **str_p, /**< input/output string
JERRY_ASSERT (length >= 1);

/* If the length is bigger than the number of digits in num, then insert leding zeros. */
for (uint32_t i = length - 1; i > 0 && num < pow (10,i); i--)
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the range of length? I mean what happens if power_i cannot precisely represent the value? Is that possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it is not possible, the range is 1-4.

{
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);
Expand Down Expand Up @@ -103,9 +105,10 @@ 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_object_get_class_name (ecma_get_object_from_value (this_arg)) != LIT_MAGIC_STRING_DATE_UL)
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 ("Incomplete Date type");
ret_value = ecma_raise_type_error ("Incompatible type");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As actions under the conditions of the if and else if blocks are the same, and conditions are not complex, it is better to merge the blocks.

}
else
{
Expand Down Expand Up @@ -174,7 +177,54 @@ ecma_builtin_date_prototype_to_string (ecma_value_t this_arg) /**< this argument
static ecma_completion_value_t
ecma_builtin_date_prototype_to_date_string (ecma_value_t this_arg) /**< this argument */
{
ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg);
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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise.

}
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 day = ecma_date_date_from_time (*prim_value_num_p);
ecma_string_t *output_str_p = ecma_new_ecma_string_from_number (day);
ecma_date_insert_leading_zeros (&output_str_p, day, 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_builtin_date_prototype_to_date_string */

/**
Expand All @@ -189,7 +239,52 @@ ecma_builtin_date_prototype_to_date_string (ecma_value_t this_arg) /**< this arg
static ecma_completion_value_t
ecma_builtin_date_prototype_to_time_string (ecma_value_t this_arg) /**< this argument */
{
ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg);
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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise.

}
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);

ret_value = ecma_make_normal_completion_value (ecma_make_string_value (output_str_p));
}

ECMA_FINALIZE (obj_this);
}

return ret_value;
} /* ecma_builtin_date_prototype_to_time_string */

/**
Expand Down
47 changes: 44 additions & 3 deletions tests/jerry/date-tostring.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,47 @@
// See the License for the specific language governing permissions and
// limitations under the License.

assert (new Date(NaN) == "Invalid Date");
assert (new Date("2015-02-13") == "2015-02-13T00:00:00.000");
assert (new Date("2015-07-08T11:29:05.023") == "2015-07-08T11:29:05.023");
assert (new Date (NaN) == "Invalid Date");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't we use 3 equal signs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always write tests like this. :) Should I change this? Why would be 3 equal signs better?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it also checks the type, so it is more strict than the two. I would recommend to use it in the future.

Later you can make a style patch to change these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks for explain. I'll raise an issue for this.

assert (new Date ("2015-02-13") == "2015-02-13T00:00:00.000");
assert (new Date ("2015-07-08T11:29:05.023") == "2015-07-08T11:29:05.023");

try
{
Date.prototype.toString.call(-1);
assert (false);
}
catch (e)
{
assert (e instanceof TypeError);
assert (e.message === "Incompatible type");
}

assert (new Date (NaN).toDateString () == "Invalid Date");
assert (new Date ("2015-02-13").toDateString () == "2015-02-13");
assert (new Date ("2015-07-08T11:29:05.023").toDateString () == "2015-07-08");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need test cases for the TypeError also.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

try
{
Date.prototype.toDateString.call(-1);
assert (false);
}
catch (e)
{
assert (e instanceof TypeError);
assert (e.message === "Incompatible type");
}

assert (new Date (NaN).toTimeString () == "Invalid Date");
assert (new Date ("2015-02-13").toTimeString () == "00:00:00.000");
assert (new Date ("2015-07-08T11:29:05.023").toTimeString () == "11:29:05.023");

try
{
Date.prototype.toTimeString.call(-1);
assert (false);
}
catch (e)
{
assert (e instanceof TypeError);
assert (e.message === "Incompatible type");
}