I'm using moment.parse String+Format method
when I call moment("=min(C2:C4)", 'DD/MM/YYYY') it evaluates to valid date
Sun Apr 02 2017 00:00:00 GMT+0300 (FLE Daylight Time)
I can't understand how moment parses "=min(C2:C4)" to valid date. Just wonder, some one can explain.
From Moment.js docs:
Moment's parser is very forgiving, and this can lead to undesired/unexpected behavior.
Short answer: because of your expected loose format, moment parses the 2 as the day and the 4 as the month.
Long answer:
Visualizing this might be easier if you run your command in a console and view the results. I'd recommend taking a look there first, and I'll explain the important parts below.
Date string vs. format
You've specified your string as "=min(C2:C4)" and your format as DD/MM/YYYY, so moment is looking for a date in that general format: a day followed by a month followed by a year.
moment._pf.unusedTokens[]
This is an array and has one value, 'YYYY'; the parser did not find a year, but it did find a day and a month... hmmm.
moment._pf.unusedInput[]
This tells us more - an array with three values:
1. '=min(C'
2. ':C'
3. ')'
Looks like moment found the 2 and the 4 to match day and month. You'll find these values in the moment._pf.parsedDateParts array.
So, that should explain why the date is April 2, 2017. The month of 3 (this is the finalized month, not the actual value parsed. Months are zero-indexed, so it's back to "4") and the day of 2. No year was given, so moment assumes it's the current year. No time was given, so it assumes the start of the day.
If you don't want this behavior you should pass a 3rd / last param as true (strict matching). Check http://momentjs.com/docs/#/parsing/string-format/ for more details.
Related
I have the following date:
25-JAN-18 01.31.02 AM +00:00
and am trying to convert into a Postgres compatible timestamp with time zone format.
I am trying the following code to convert into the compatible format:
document.getElementById("parsedDate3").innerHTML = moment("25-JAN-18 01.31.02.923526 AM +00:00 ", "d-MMM-YY hh.mm.ss A Z");
But I am getting the output as
Mon Jan 01 2018 02:31:02 GMT+0100
Can anyone please help me with this.
You are using lower-case d which is for the day of the week, 0 (Sunday) through 6 (Saturday).
Use upper-case D instead, which is for the day of the month.
Note that Moment's formatting tokens are slightly different than in other libraries and languages, so check the chart in the docs carefully.
Also:
In your code you have 6 extra digits following the seconds. If you need to account for those, use SSSSSS. Otherwise they are ignored.
You shouldn't assign a Moment object directly to an HTML element's innerHTML property. Instead, call the .format() function on the Moment object first to generate a string. You can optionally pass an argument to this function, to control the output format.
You can simply do it in PostgreSQL:
SELECT to_timestamp('25-JAN-18 01.31.02.923526 AM +00:00', 'DD-MON-YY HH.MI.SS.US AM TZH:TZM');
to_timestamp
-------------------------------
2018-01-25 02:31:02.923526+01
(1 row)
This will work for PostgreSQL v11 or better.
Earlier versions of to_timestamp cannot parse the time zone information yet.
If you need it to work on 9.6, and you know that the time zone is always going to be +00:00, you could simple omit the TZH:TZM in the format string.
I have the following data structure. The first column is intervals. The first row of the interval datum is a unix time and the subsequent data are intervals (i.e. 300*1, 300*2, ect). The other column is the data values. Here is the head of the data:
a1521207300,555.45
1,554.53
2,554.07
3,553.9
4,552.67
And here I went about converting the unix time to a Date object. The a here is ornamental, so I slice() at 1 like so:
var rawTime = data[0].interval;
var timeValue = Math.round(rawTime.slice(1));
console.log(timeValue)
console.log(new Date(timeValue))
I also tried using parseInt() instead of round(). The console shows that this unix time is equivalent to: Jan 18 1970 which I had quite the guffaw at. Then I got to thinking, maybe I did something wrong. It's supposed to be a very recent date -- March 16th 2018. This is strange because my understanding is that javascript can be passed a unix date directly as per this answer.
I also checked the unix time at a conversion site: www.onlineconversion.com/unix_time.htm
Which confirmed that it's indeed a March 16th 2018 timestamp.
Question: Why is this unix date for my March 2018 data being treated like a 1970's date? Maybe the a is actually doing something after all... Anyway, what is the correct way to handle this time stamp? It's only 10 numerical digits, it does not seem to be a precision problem. Date can handle unix times up to 13 digits I believe.
As per the documentation, when you invoke new Date(value) with an integer value, it is used as the number of milliseconds since January 1, 1970. To get the date you want, the value 1521207300 appears to be number of seconds instead of milliseconds. That is, you missed a factor of 1000. new Date(1521207300000) gives Fri Mar 16 2018.
When I take away new from new Date it seems to be ok. Not sure why though.
The documentation mentions the different behavior:
Note: JavaScript Date objects can only be instantiated by calling JavaScript Date as a constructor: calling it as a regular function (i.e. without the new operator) will return a string rather than a Date object; unlike other JavaScript object types, JavaScript Date objects have no literal syntax.
It seems when called as a function Date(value), it treats the value as the number of seconds, instead of milliseconds. I didn't dig deep enough to confirm this, because it doesn't matter: the documentation says to not use it this way (and since it gives a string instead of a date object, it's not very useful anyway).
From the docs for Moment.js, it says moment().startOf('isoWeek') sets to the first day of the week according to ISO 8601.
From the wikipedia page for ISO 8601, it says the first week is (among other equivalent definitions): the first week with the majority (four or more) of its days in the starting year.
From the docs for MySQL, it says WEEK(date, 3) gives a week (1-53) with Monday as the first day of the first week with 4 or more days this year.
From what I can tell, these are all equivalent definitions of a week, yet they don't mesh.
My MySQL database (v5.6.17) gives me 201616 for SELECT YEARWEEK('2016-4-20',3)
Using Moment.js (v2.10.3) in the browser gives me Mon Apr 11 2016 for moment().year(2016).week(16).startOf("isoweek")
Those aren't the same week, and I can't figure out why.
When you call .week() you are setting the "locale" week (I have no idea how moment.js works that out), but likely the first day will be Sunday. When you then call .startOf('isoweek') you are then getting the ISO start of that week, which is the previous Monday.
If you want to use ISO, you have to use ISO methods, so:
moment().year(2016).isoWeek(16).startOf('isoweek');
creates a moment object for 2016-04-18T00:00:00
Or use a string:
moment('2016-W16') // 2016-04-18T00:00:00
which defaults to ISO week numbering and start of week.
It seems to me that Date.parse assumes all months have 31 days. Including months with 30 days and including February(which should only ever have 28/29 days).
I checked this question 31 days in February in Date object
But the answer there suggested there was nothing wrong with Date in his issue..Somebody said something to the questioner about zero indexing and he pretty much said "oh ok", and determined that it was his mistake and not a mistake of Date.
Another question Date is considering 31 days of month the guy was doing some subtraction was a number of lines of code and he seemed to not put the error down to Date in the end.
But this example that I have seems to be a bit different and more clear cut. It involves Date.parse and can be demonstrated with one/two lines of code.
Date.parse is aware that there are not 32 days in a month, that's good
Date.parse("2000-01-32");
NaN
But In February it thinks there can be 30 or 31 days
Date.parse("2013-02-30");
1362182400000
Date.parse("2013-02-31");
1362268800000
In fact it looks like it thinks all months have 31 days. That is really strange for a method that is meant to parse a date.
And there's no issue of zero indexing here. As Date.parse("...") doesn't use zero indexing (And even if it did, it wouldn't cause it tot make the error of thinking there are 31 days in February - that is more than one off!
Date.parse("01-00-2000");
NaN
Date.parse("00-01-2000");
NaN
According to the specification for Date.parse() (emphasis mine):
The function first attempts to parse the format of the String according to the rules called out in Date Time String Format. […] Unrecognisable Strings or dates containing illegal element values in the format String shall cause Date.parse to return NaN.
And according to the specification for Date Time String Format (emphasis mine):
ECMAScript defines a string interchange format for date-times based upon a simplification of the ISO 8601 Extended Format. The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ
Where the fields are as follows: […] DDis the day of the month from 01 to 31.
Therefore, any date with a day of month greater than 31 is illegal and Date.parse() returns NaN.
Please notice that the standard defines a date format, not a date: the static method isn't required to make additional verifications, and everything else is implementation-specific. For instance, Date.parse('2013-02-30') and Date.parse('2013-04-31') both return NaN on Firefox.
The implementation differs between browsers. IE, Edge and Chrome will parse strings that doesn't represent actual dates, but Firefox will return NaN for those strings. The safe thing to do is to consider the result from Date.parse as undefined for date strings where the day falls outside the range of the month.
Browsers that allow parsing of non-existent dates will return a different date. Parsing "2015-04-31" will return the date 2015-05-01. This is the same behaviour as when using new Date(2015, 3, 31), where numbers out of range is allowed and will wrap around into a different month or year. That means that the result is still usable, if you don't mind that some invalid dates turn into other dates in some browsers.
The standard isn't very clear about what values are valid:
Illegal values (out-of-bounds as well as syntax errors) in a format
string means that the format string is not a valid instance of this
format.
The day component is defined as having a range from 1 to 31:
DD is the day of the month from 01 to 31.
However, the format is based on ISO 8601, and that is not a format for parsing strings into dates, that is a format for representing dates as string. Clearly you can't represent a date that doesn't even exist as a string.
Right, so how to check if a date string has a valid value?
with moment is very easy:
export function dateStringIsValid(aDateString){
return (moment(aDateString, "DD/MM/YYYY", true).isValid())
}
How to format ISO dates BC with Moment.js?
moment("-700-01-01").year(); // 700 (WRONG)
moment("-0700-01-01").year(); // 700 (WRONG)
moment("-000700-01-01").year(); // -700 (RIGHT)
For some reason a year notation with 6 digits works. Is that the "right" way? Why doesn't notation like "-700-01-01" work?
This isn't a Moment.js-specific problem; the same happens if you attempt to initialise a Date() object with the string you're using as well. If you create it as a Date() object first and manually assign the year using setYear() it does accept a date of -700:
var date = new Date();
date.setYear(-700);
moment(date).year();
> -700
However as Niels Keurentjes has pointed out, date calculations this far back get quite complicated and may not be at all reliable.
If you want "-700-01-01" you can configure the year, month and day separately:
date.setYear(-700);
date.setMonth(0);
date.setDate(1);
console.log(date);
> Fri Jan 01 -700 11:53:57 GMT+0000 (GMT Standard Time)
As to whether the 1st day of the 1st month in 700BC was actually a Friday... you'll have to look that one up yourself.
You can also
moment('0000-01-01', 'YYYY-MM-DD').set('y', -700)
in your example the minus sign is also used as separator between years, month and days. As you point out in the comment of the answer of James, using coma as separator helps to distinguish.
moment can display expanded years using the YYYYYY notation. This feature is recorded in the moment.js display documentation and elaborated upon in the ECMAscript documentation.