Error parsing date string with `new Date` - javascript

datefromJSON = req.body.occasion_date;
occasion_date = new Date(datefromJSON);
console.log(occasion_date);
//while running this i get log like this
//"Invalid Date"
when i get json data from req.body then save in datefromJSON var, and for storing into database i use to convert into Date object that get error

Your date string (whatever it is "31-08-2016") isn't in a format recognized by the Date constructor. So you ended up with a Date whose underlying time value is NaN, which is shown as "Invalid Date" when you ask for the string version. Gratuitous example:
console.log(new Date("foobar").toString());
The only formats the specification requires a JavaScript implementation to support are:
The one added in the ES5 specification in 2009, which was meant to be (and as of all recent specifications actually is; there was an error in ES5 and ES2015/ES2016) a subset of ISO-8601. (This is the format produced by toISOString.) spec
The ones produced by the toString and toUTCString (aka toGMTString) methods. spec
So for instance:
console.log(new Date("2016-08-31T09:25").toString());
Every JavaScript implementation I've run into also unofficially supports parsing the U.S. format with slashes, MM/dd/yyyy, (even in non-U.S. locales), but the timezone varies (most interpret it as local time, others interpret it in GMT).
So you'll need to either:
A) Parse your string (regular expressions, split, etc.) and use the form of the Date constructor that supports supplying the parts individually. Mind the gotcha that the months value starts with 0 = January (not 1 = January). There are several dozen questions with examples of that here on SO. Here's an example of parsing the common dd-MM-yyyy HH:mm:ss or dd-MM-yyyy HH:mm:ss.SSS format as a local date/time:
Regex-based:
function parseIt(str) {
var rex = /^\s*(\d{1,2})[\/-](\d{1,2})[\/-](\d{4})(?: (\d{1,2})(?::(\d{1,2})(?::(\d{1,2})(?:\.(\d{1,3}))?)?)?)?\s*$/;
var parts = rex.exec(str);
var dt = null;
if (parts) {
dt = new Date(+parts[3], // Year
+parts[2] - 1, // Month
+parts[1], // Day
+parts[4] || 0, // Hours
+parts[5] || 0, // Minutes
+parts[6] || 0, // Seconds
+parts[7] || 0 // Milliseconds
);
}
return dt;
}
function test(str) {
var dt = parseIt(str);
console.log(str, "=>");
console.log(" " + String(dt));
console.log(" (" + (dt ? dt.toISOString() : "null") + ")");
}
test("foobar");
test("31-08-2016");
test("31/08/2016");
test("31/08/2016 9");
test("31/08/2016 9:25");
test("31/08/2016 09:25");
test("31/08/2016 09:25:17");
test("31/08/2016 09:25:17.342");
The regex looks complicated, but it's really just a bunch of capture groups nested inside non-capturing, optional groups. Explanation here.
or
B) Use a library (like MomentJS) that lets you parse a string by saying what the format is.

Related

Prevent moment.js accepting an integer as a valid ISO8601 date

I have some API's for creating dashboard widgets. Those API's return basic name/value data pairs that are passed to Google Charts. Moment.js checks whether the value is an ISO8601 date, and if so passes to Google Charts as a date instance.
However, the ISO_8601 isValid check is currently returning true if the date is a simple integer, e.g. 1234:
var myInt = 1234;
if (moment(myInt, moment.ISO_8601, true).isValid()) {
console.log("Valid!");
}
I couldn't locate the necessary functionality to force a date format in the moment.js code, so this brutal hack works for now:
var myInt = 1234;
if (JSON.stringify(myInt).includes("T") && moment(myInt, moment.ISO_8601, true).isValid()) {
console.log("Valid!");
}
Is there a correct way to use moment.js to configure the isValid() check?
The date format from my API is yyyy-mm-ddThh:mm:ss (without Z on the end).
According to THIS answer, when using strict parsing (last parameter set to true) you should also specify parse format, to avoid situations like you discribed. As many users notice, specyfying string format instead of using moment.ISO_8601 works as expected.
alert(isISODateValid(123)); //false
alert(isISODateValid("2011-10-10T14:48:00")); //true
alert(isISODateValid("2011-10-10T14:48:00Z")); //true
function isISODateValid(date) {
return moment(date.toString().replaceAll("Z",""), "YYYY-MM-DDTHH:mm:ss", true).isValid();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
EDITS: Updated snippet - if date contains "Z" suffix, remove it before parsing validating source date format
Since you stated that: "The date format from my API is yyyy-mm-ddThh:mm:ss (without Z on the end)", the best way to parse it is explictly pass the format you are expecting to moment, using the right moment format tokens instead of using moment.ISO_8601.
So, in your case, simply use moment(myInt, "YYYY-MM-DDTHH:mm:ss", true), as shown in the snipppet:
function checkValid(input) {
if (moment(input, "YYYY-MM-DDTHH:mm:ss", true).isValid()) {
console.log(input + " is valid!");
}
}
checkValid(1234);
checkValid("2021-04-27T20:40:15");
checkValid("2021-04-27T20:40:15Z");
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
Please note that the Z at the end stands for the timezone offset UTC+0, if you have it moment takes it into account, while without it, moment parses the input as local time (see Local vs UTC vs Offset guide)
As a side note, moment.ISO_8601 works as you were expecting in moment versions prior to 2.25.0:
function checkValid(input) {
if (moment(input, moment.ISO_8601, true).isValid()) {
console.log(input + " is valid!");
}
}
checkValid(1234);
checkValid("2021-04-27T20:40:15");
checkValid("2021-04-27T20:40:15Z");
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
You could testify the sting before you pass it to the moment.? I have taken an example from this post
/**
* RegExp to test a string for a full ISO 8601 Date
* Does not do any sort of date validation, only checks if the string is according to the ISO 8601 spec.
* YYYY-MM-DDThh:mm:ss
* YYYY-MM-DDThh:mm:ssTZD
* YYYY-MM-DDThh:mm:ss.sTZD
* #see: https://www.w3.org/TR/NOTE-datetime
* #type {RegExp}
*/
var ISO_8601_FULL = /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?(([+-]\d\d:\d\d)|Z)?$/i
// Usage:
ISO_8601_FULL.test( "2016-05-24T15:54:14.876Z" ) // true
ISO_8601_FULL.test( "2002-12-31T23:00:00+01:00" ) // true
ISO_8601_FULL.test( "2016-02-01" ) // false
ISO_8601_FULL.test( "2016" ) // false
if (ISO_8601_FULL.test(myDate) && moment(myDate, moment.ISO_8601, true).isValid()) {
console.log("Valid!");
}
I suppose the date should not be an integer.
Interesting is the difference between string and number. If it's a number, it is interpreted as the number of milliseconds since epoc, which is quite helpful in computer languages, but obviously not always what was requested and also not obvious to every developer. This can easily avoided with a type check (typeof input != 'string').
The other variant is more confusing: "12345" is not valid. good. But "1234" is interpreted as a year, and at the same time "34" seems to be interpreted as a time offset in minutes (Sun Jan 01 1234 00:00:00 GMT+0034). To me, this clearly looks like a bug in the library, since it's quite useless to parse the same digits multiple times for different purposes. But also after this is fixed, "1234" stays a valid date (year only) as defined in the standard ISO 8601
https://en.wikipedia.org/wiki/ISO_8601
For reduced precision,[17] any number of values may be dropped from any of the date and time representations, but in the order from the least to the most significant. For example, "2004-05" is a valid ISO 8601 date, which indicates May (the fifth month) 2004. This format will never represent the 5th day of an unspecified month in 2004, nor will it represent a time-span extending from 2004 into 2005.
Btw: "543210" is also valid and means "5432-10", or October of the year 5432
function checkValid(input) {
m = moment(input, true);
console.log(input + " type: " + typeof input + " valid: " + m.isValid() + " result:" + m.toString());
}
checkValid(1234);
checkValid("1234");
checkValid(12345);
checkValid("12345");
checkValid("2021-04-27T20:40:15");
checkValid("2021-04-27T20:40:15Z");
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>

JavaScript getting date string as 11pm on the day before

I have the following input field:
In my web app I have -
string date - 06/05/2018
And this JS code:
var d = "06/05/2018".split("/");
var date = new Date(d[2] + "-" + d[1] + "/" + d[0]).getTime();
console.log(date)
This returns 1525561200000 which if I put that into epoch converter gives me...
Saturday, May 5, 2018 11:00:00 PM
This then screws up with my filtering system - date ranges because if I select the minimum date to be 06/05/2018 with the input field:
var d = $('#min').val()
var date = new Date(d).getTime();
console.log(date)
It is returning 1525564800000 which comes to Sunday, May 6, 2018 12:00:00 AM
How do I get around this?
Thanks
I could write an entire thesis on how problematic and difficult it is to work with dates in Javascript and how to avoid pitfalls and weird bugs, but in the end your specific problem comes down to a simple typo.
The string you're parsing manually and passing to the Date constructor looks like this:
2018-05/06
You've mistakenly used a / instead of a - as the second delimiter when concatenating the string. For some reason, the browser then creates the date object as midnight 2018-05-06 local time. When passing in the string in the standard format (which is what happens when taking it from the date input), i.e. 2018-05-06, the date object gets created as midnight 2018-05-06 UTC time.
So, in short, your problem can be solved by replacing the "/" with "-" in your string concatenation and the two dates should be the same.
However, I should point out that passing a string to the Date constructor is unreliable since the result is not standardized and may differ between browsers (which is also why it behaves so unpredictable and seemingly illogical in this case). It's a better idea to pass numbers instead since the specification dictates the result of that. You're already halfway there since you've split the date string into its components. Try this:
var date = new Date(
Number(d[2]),
Number(d[1]) - 1, // Subtracting 1 from month since it's base 0
Number(d[0])
).getTime();
(Technically, we don't even need to explicitly convert to Number since the Date constructor expects all arguments to be numbers when there's more than one argument and will convert whatever it gets into numbers internally)

Parse date from Apple receipt validation server with JavaScript

I have a datetime from Apple's receipt validation server. I need to compare it to today's date. I'm trying to use moment.js but I am fine with native Date as well.
The date is formatted like this, in what Apple's docs claim, incorrectly, is RFC 3339 format:
2017-11-28 23:37:52 Etc/GMT
Since this isn't a valid RFC 3339 or ISO 8601 datetime, it is failing to parse using moment.js or Date.parse(). How can I parse it?
Note that I don't want to simply strip the Etc/ out because its important for determining the correct timezone according to https://opensource.apple.com/source/system_cmds/system_cmds-230/zic.tproj/datfiles/etcetera:
We use POSIX-style signs in the Zone names and the output abbreviations, even though this is the opposite of what many people expect. POSIX has positive signs west of Greenwich, but many people expect positive signs east of Greenwich. For example, TZ='Etc/GMT+4' uses the abbreviation "GMT+4" and corresponds to 4 hours behind UTC (i.e. west of Greenwich) even though many people would expect it to mean 4 hours ahead of UTC (i.e. east of Greenwich).
You can adopt one of two strategies:
Manually parse the string and use the parts for the Date constructor, then adjust for the timezone.
Transform the timestamp into a string that should be supported by most browsers in use (i.e. ISO 8601 extended format per ECMA-262).
The first way is more robust.
The reference doesn't say how timezones like +0530 are represented in the Etc/ format, so the following doesn't deal with them:
'Etc/GMT' becomes '+00:00'
'Etc/GMT+4' becomes '-04:00'
'Etc/GMT-10' becomes '+10:00'
Also, the following functions don't validate the format, they assume the supplied string is suitable.
// Parse strings in format 2017-11-28 23:37:52 Etc/GMT
function parsePOZIX(s) {
var b = s.split(' ');
// These are the date and time parts
var c = b[0].split(/\D/).concat(b[1].split(/\D/));
// Create a new Date using UTC
var d = new Date(Date.UTC(c[0],c[1]-1,c[2],c[3],c[4],c[5]));
// Now adjust for the timezone
var tz = b[2].split('/')[1];
var sign = /^\+/.test(tz) ? 1 : -1;
var hr = tz.replace(/\D/g, '') || '0';
d.setUTCHours(d.getUTCHours() + sign*hr);
return d;
}
['2017-11-28 23:37:52 Etc/GMT',
'2017-11-28 23:37:52 Etc/+8',
'2017-11-28 23:37:52 Etc/-10'].forEach(function(s){
console.log(s + '\n' + parsePOZIX(s).toISOString());
});
And the second (less reliable way) which parses and reformats the string, then uses the built-in parser:
// Reformat strings like 2017-11-28 23:37:52 Etc/GMT
// to ISO 8601 compliance, then use built-in parser
function parsePOSIX2(s) {
var b = s.split(' ');
var tz = b[2].split('/')[1];
var sign = /^\+/.test(tz)? '-' : '+';
var hr = tz.replace(/\D/g,'') || '0';
hr = ('0' + hr).slice(-2) + ':00'; // Must include colon for Safari
return new Date(b[0] + 'T' + b[1] + sign + hr);
}
['2017-11-28 23:37:52 Etc/GMT',
'2017-11-28 23:37:52 Etc/-4'].forEach(function(s) {
console.log(s + '\n' + parsePOSIX2(s).toISOString());
});
Note that Safari will not parse a timezone like "+0000" (resulting in an invalid date), it must include a colon: "+00:00".
The easiest way to parse an an arbitrary format is to use some regex to split it to pieces, then arrange it in a format you can use.
const date = '2017-11-28 23:37:52 Etc/GMT';
const [,year,month,day,hour,minute,second,timezone] = date.match(/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):(\d{2})\sEtc\/([A-Z]{3})/);
console.log(year, month, day, hour, minute, second, timezone);
const parsedDate = new Date(`${year}-${month}-${day} ${hour}:${minute}:${second} ${timezone}`);
console.log(parsedDate);
The nice thing about this approach is it works for any random format you may come across (you just need a different regex). With ES6 destructing, it can be quite compact.
Note: I'm not sure if that Etc bit is constant or has special meaning, so you should check out the spec and make sure you don't need to do anything with it.
The format Apple is returning consists of an ISO 8601 datetime followed by an IANA timezone. That's an eccentric format I've never seen elsewhere, and it seems to be a mistake on Apple's part given that their docs claim it's meant to be an RFC 3339 datetime string.
JavaScript has no built-in support for working with IANA timezones. For that matter, nor does the core Moment.js library. However, Moment Timezone does. If you include both the moment and moment-timezone libraries, then you can parse your date like this:
/**
* Parse a datetime string returned from the Apple Receipt Validation Server, in
* a format like:
* 2017-11-28 23:37:52 Etc/GMT
* and return its value as a Moment datetime object.
*/
function parseAppleDatetime(appleDatetimeStr) {
const [dateStr, timeStr, timezoneStr] = appleDatetimeStr.split(' '),
datetimeStr = dateStr + ' ' + timeStr;
return moment.tz(datetimeStr, timezoneStr);
}
Now if you do, for instance:
parseAppleDatetime('2017-11-28 23:37:52 Etc/GMT').format()
... you'll get:
"2017-11-28T23:37:52Z"

How to do a date format in Javascript?

I need to understand how to do date formattting in javascript.
i have date as,
var date="12/02/1994";// dd/mm/yyy
var date1=new Date(date);
date1.getDate();// this gives me Month which is 02
date1.getMonth();// this gives me date which is 12.
How do i get the exact date i have in var date in get date and getmonth function? Please help
The answer is pretty simple: JavaScript uses mm/dd/yyyy data format.
It doesn't support dd/mm/yyyy format, so, if you need to parse this format, then you will have to do this manually like this:
function parseDdmmyyyy(str)
{
var spl = str.split('/');
return new Date(spl[2], spl[1] - 1, spl[0]);
}
or you will have to use external libraries like Moment.js.
Javascript date() expects date in mm/dd/yy and not in dd/mm/yy. And months start from 0 and not 1.
var from = "12/02/1994".split("/");
var date1 = new Date(from[2], from[1] - 1, from[0]);
date1.getDate();
date1.getMonth();
Use new Date('02/12/1994'), new Date('1994-02-12') or new Date(1994, 02-1, 12), because in js months start from 0 and american date format is used where month goes first
you can use the simple JS file DateFormat.js which has some very good example through the URL mattkruse (Date Funtion)
from this JS file you can validate the incoming date is a true format even you can add format date within a several ways.
Presumably you want to know how to format strings so they are consistently parsed by browsers. The short answer, is there is no guarantee that any particular string will be correctly parsed by all browsers in use (or perhaps even most).
So the bottom line is: don't parse strings with the Date constructor, ever. It's largely implementation dependent and even the one format specified in ES5 and ECMAScript 2015 is poorly and inconsistently supported.
How browsers treat a string like "12/02/1994" is entirely implementation dependent, however most will treat it as the peculiar US month/day/year format, i.e. 2 December and getMonth will return 11, since months are zero indexed.
So you should always manually parse strings (a library can help, but a simple parsing function is only 2 lines, 3 if validation is required), e.g.
// Parse a date string as d/m/y
// If s is not a valid date, return a Date object with its
// time value set to NaN.
function parseDMY(s) {
var b = s.split(/\D/);
var d = new Date(b[2], --b[1], b[0]);
return d && b[1] == d.getMonth()? d : new Date(NaN);
}
document.write(parseDMY('12/02/1994'));

Using momentjs to convert date to epoch then back to date

I'm trying to convert a date string to epoch, then epoch back to the date string to verify that I'm providing the correct date string.
var epoch = moment("10/15/2014 9:00").unix(); // do I need to do .local()?
var momentDate = moment(epoch); // I've also tried moment.utc(epoch)
var momentDateStr = momentDate.calendar();
alert("Values are: epoch = " + epoch + ", momentDateStr = " + momentDateStr);
Renders
Values are: epoch = 1413378000, momentDateStr = 01/17/1970
Note: I'm using the following version of the moment js script, //cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.3/moment-with-locales.js
There are a few things wrong here:
First, terminology. "Epoch" refers to the starting point of something. The "Unix Epoch" is Midnight, January 1st 1970 UTC. You can't convert an arbitrary "date string to epoch". You probably meant "Unix Time", which is often erroneously called "Epoch Time".
.unix() returns Unix Time in whole seconds, but the default moment constructor accepts a timestamp in milliseconds. You should instead use .valueOf() to return milliseconds. Note that calling .unix()*1000 would also work, but it would result in a loss of precision.
You're parsing a string without providing a format specifier. That isn't a good idea, as values like 1/2/2014 could be interpreted as either February 1st or as January 2nd, depending on the locale of where the code is running. (This is also why you get the deprecation warning in the console.) Instead, provide a format string that matches the expected input, such as:
moment("10/15/2014 9:00", "M/D/YYYY H:mm")
.calendar() has a very specific use. If you are near to the date, it will return a value like "Today 9:00 AM". If that's not what you expected, you should use the .format() function instead. Again, you may want to pass a format specifier.
To answer your questions in comments, No - you don't need to call .local() or .utc().
Putting it all together:
var ts = moment("10/15/2014 9:00", "M/D/YYYY H:mm").valueOf();
var m = moment(ts);
var s = m.format("M/D/YYYY H:mm");
alert("Values are: ts = " + ts + ", s = " + s);
On my machine, in the US Pacific time zone, it results in:
Values are: ts = 1413388800000, s = 10/15/2014 9:00
Since the input value is interpreted in terms of local time, you will get a different value for ts if you are in a different time zone.
Also note that if you really do want to work with whole seconds (possibly losing precision), moment has methods for that as well. You would use .unix() to return the timestamp in whole seconds, and moment.unix(ts) to parse it back to a moment.
var ts = moment("10/15/2014 9:00", "M/D/YYYY H:mm").unix();
var m = moment.unix(ts);
http://momentjs.com/docs/#/displaying/unix-timestamp/
You get the number of unix seconds, not milliseconds!
You you need to multiply it with 1000 or using valueOf() and don't forget to use a formatter, since you are using a non ISO 8601 format. And if you forget to pass the formatter, the date will be parsed in the UTC timezone or as an invalid date.
moment("10/15/2014 9:00", "MM/DD/YYYY HH:mm").valueOf()

Categories