How does Date's constructor overload the unary +? - javascript

I was reading this answer to Hidden Features of JavaScript? and was perplexed by this behaviour:
> new Date
< Fri Feb 26 2016 21:15:43 GMT-0500 (EST)
> +new Date
< 1456539382581
< 0 + new Date + 0
"0Fri Feb 26 2016 21:17:39 GMT-0500 (EST)0"
So, the Date constructor returns an object which, when converted to a number via unary (but not via addition) returns a number representing the Unix Epoch.
How is this possible? Reading about the topic, it seems like JS has no support for operator overloading, or only a small amount if overloading non-operator functions (add, toString, whatever) counts.
JS doesn't seem to have any such function for unary + specifically (or number coersion at all, it seems), so how is this done?

Unary operator + converts its operand to a number if it isn't already.
So +new Date is similar to Number(new Date).
new Date() // Fri Feb 26 2016 21:45:32 GMT-0500 (EST)
Number(new Date()) // 1456541132899
+new Date() // 1456541132899
Update:
What's special about the Date object that makes calling Number on it return the Unix time?
Date objects are based on a time value that is the number of milliseconds since 1 January, 1970 UTC (From MDN). So, it makes sense when we ask it to convert it to a number, it returns us milliseconds.

Automatic type conversion of Dates.
The primitive numeric value of a Date object, returned by Date.prototype.getValue() is a Unix time stamp, the number of milliseconds since 1 January, 1970 UTC and is the value of a Date object converted to a number.
When converting an object to number following a unary + operator, the javascript engine explicitly asks the object to return a number, by calling the objects internal toPrimitive method with a hint value of "number" as described in section 7.1.1 of the ES6 standard. This mechanism converts Date objects to milliseconds after a unary + sign in fairly standard fashion.
So how to explain how Date objects become converted to a string when used as the operand of a non unary plus?. It would seem that for the non-unary case, type conversion requests an object's primitive value without supplying a result type hint and . . . Date provides an exotic toPrimitive function which converts dates to string by default instead of to number. Date and Symbol are special in this regard and differ from most other objects.

Here is a little description about unary plus from MDN:
The unary plus operator precedes its operand and evaluates to its operand but attempts to converts it into a number, if it isn't already.
If the operator cannot parse a string/value, it returns NaN.

Related

Date comparison with primitive value. When it is bigger than primitive?

new Date('Sep 23, 2019') > 0 ----> true
new Date('Sep 23, 2019') > 's' ----> false
When date compared with primitive is greater or lower than primitive?
F.ex. I need to compare any date with a primitive which is lower than the date.
When Date is compared to Number, using one of the <, <=, > or >= operators, the Date is converted to milliseconds (as if .getTime() is invoked on it).
When Date is compared to String like "1569177000000", the string is converted to the number too.
When Date is compared to String like "s" that cannot be converted to a number, there is no ordering: you will always get false (any comparison to NaN is false).
See MDN:
For relational abstract comparisons (e.g., <=), the operands are first converted to primitives, then to the same type, before comparison.
Specifically, for >, see here in the specification, where LeftFirst is false.
It's pretty complicated, but both sides are converted to a primitive (preferably a number) if they aren't already. When this is done with a Date, it returns the date's Unix timestamp.
Because both sides are then numbers, it's straightforward after that - the interpreter just checks which number is larger.
You can use the valueOf function on date
// Convert date into primitive
new Date('Sep 23, 2019').valueOf() //1569177000000
you can use getTime() or date functions to convert date into primitive value and then use a comparison operator.

Parse Date "01/01/2016 Fri"

I am trying to parse a date in JavaScript, but the particular format is giving me fits. I have exported data from my credit card company and the format of the date field is not compatible with Date.parse or moment().isValid().
E.g.
Date.parse("01/01/2016 Fri") // NaN
moment("01/01/2016 Fri") // false
I'm not sure if I should do something with a RegEx .test() or .matches() because this is being used for a CSV import utility where dates may be in different formats. I was surprised the utility functions above didn't work.
Look in the Moment docs to see how to parse a date in any format. The first argument is the date string, the second is the format string. Alphanumeric characters are ignored, so you don't need to worry about slashes vs. dashes.
moment("01/01/2016 Fri", "MM-DD-YYYY ddd)
Check out the Mozilla MDN on Date.parse():
The parse() method takes a date string (such as "Dec 25, 1995") and
returns the number of milliseconds since January 1, 1970, 00:00:00
UTC. This function is useful for setting date values based on string
values, for example in conjunction with the setTime() method and the
Date object.
Given a string representing a time, parse() returns the time value. It
accepts the RFC2822 / IETF date syntax (RFC2822 Section 3.3), e.g.
"Mon, 25 Dec 1995 13:30:00 GMT". It understands the continental US
time zone abbreviations, but for general use, use a time zone offset,
for example, "Mon, 25 Dec 1995 13:30:00 +0430" (4 hours, 30 minutes
east of the Greenwich meridian).
From this, it looks like your problem is that you're giving the date in the improper format:
It
accepts the RFC2822 / IETF date syntax (RFC2822 Section 3.3), e.g.
"Mon, 25 Dec 1995 13:30:00 GMT".
Check this out:
Invalid values in date strings not recognized as ISO format as defined by ECMA-262 may or may not result in NaN, depending on the browser and values provided, e.g.:
// Non-ISO string with invalid date values
new Date('23/25/2014');
TL;DR - you're passing the values in a format that is not recognized, which is why it's returning NaN.
Try this source for Regexes for dates: Regexlib.com. The site is a little out of date, but the info is great. It has tons of different Regexes for different date formats.

Unexpected Javascript Date object implicit conversion

Why Javascript's Date object returns different value for implicit conversions?
Number conversion:
+new Date()
// returns 1456293356618 as expected
String conversion:
''+new Date()
// returns "Wed Feb 24 2016 09:26:28 GMT+0" but "1456293356618" as a string was expected
Where can I find the documentation on ECMAScript and the implementation on v8's source?
Edit: I'm not looking for the solution for the expected result. I want to find the documentation in the specs.
The + operator is overloaded. In:
+new Date()
it is treated as the unary + operator and coerces the value to Number. In:
'' + new Date() // note one value is a string
it is treated as the string concatenation operator and coerces the values to String. In:
5 + 6 // note both values are number
it is treated as the addition operator. Since the values are numbers, no coercion is necessary.
Note that whether + does addition or concatenation depends on the values and is described in ECMAScript 2015 §12.7.3.1 step 11.
I think you are refering to this Overview of Date Objects and Definitions of Abstract Operators, specifically Section 20.3.1.1
A Date object contains a Number indicating a particular instant in time to within a millisecond. Such a Number is called a time value. A time value may also be NaN, indicating that the Date object does not represent a specific instant of time.
This means that using math operations on a Date object will extract its Number value to work. That's why statements like +new Date() and Math.floor(new Date()) returns a Number.
As for '' + new Date(), the Date object returns its String value perhaps using its toString() function.

Why does 'new Date() * 1' produce a Javascript timestamp?

I have the following methods of obtaining a timestamp:
new Date().valueOf()
new Date().getTime()
Date.parse(new Date())
new Date() * 1
But I'm confused: why am I able to get a timestamp using the last method?
This is because the Date() object can be converted directly to a number (the timestamp), and when applying mathematical operators to it JavaScript converts this for us.
An even quicker way to get the timestamp is to use the Unary Plus:
+new Date();
The unary plus operator precedes its operand and evaluates to its operand but attempts to converts it into a number, if it isn't already. Although unary negation (-) also can convert non-numbers, unary plus is the fastest and preferred way of converting something into a number, because it does not perform any other operations on the number.
– MDN's Unary Plus documentation.
The same applies with strings "1" * 1 equals 1 because JavaScript automatically converts "1" to a number because of the presence of the multiplication operator (*).
One of the weird things about JavaScript is that if you try and perform an operation between two variables, it will try to convert them to a type where it can perform this operation. But this can happen in different ways, so for example "2" * 2 === 4 is true, but so is "2" + 2 === "22" because it goes to the string version of the + operator first.
When you take a Date and try to do multiply or divide it, JavaScript will understand that as converting the Date to it's numerical timestamp value. Consequently the surprising outcome you see above.
However:
new Date() + 1
Results in "Tue Jul 07 2015 15:20:17 GMT+0100 (GMT Daylight Time)1" because the date can also be treated as a string.
This is one of the many quirks of JavaScript that will be helpful to understand when you run into that weird bug, but you would be well advised to avoid using in your code if you want to be able to understand it at a glance later ( spoiler warning: You do! )

(new Date('2012-12-01')).getMonth() === 10?

(new Date('2012-12-01')).getMonth() is 10 instead of 11 (getMonth is 0-indexed). I've tested on Firefox, Chrome, and Node.js. Why does this happen?
You are experiencing a timezone issue. Your JS engine interprets the string as UTC, since it was no further specified. From the specification of Date.parse (which is used by new Date):
The String may be interpreted as a local time, a UTC time, or a time in some other time zone, depending on the contents of the String. The function first attempts to parse the format of the String according to the rules called out in Date Time String Format (15.9.1.15). If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
In your timezone, the datetime is Nov 30 2012 19:00:00 GMT-0500 - in November. Use .getUTCMonth() and you would get December. However, never trust Date.parse, every browser does it differently. So if you are not in a restricted environments like Node.js, you always should parse your string (e.g. with regex) and feed it to new Date(Date.UTC(year, month, date, …)).
For Firefox's case, at least, RFC2822 states that date specifications must be separated by Folding White Space. Try (new Date('2012 12 01')).getMonth(); Usage of - as a separator does not appear to be defined.
The error is arising from prefixing the day 01 with 0. Not sure WHY this is, but if you remove the zero before the 1, it gives you the right month (11).
Also, it starts giving the wrong month at October if that means anything.
Short term fix, use 1 instead of 01.

Categories