Format/parse different Date formats correctly - javascript

I receive Date in 3 formats from different APIs
UTC format: 2014-01-01T00:00:00.000Z (String)
GMTformat: Thu, 29 Nov 2018 17:30:56 GMT (String)
unixTimeStamp: 1558606726 (number)
Also the UTC format sometimes might not have Z in the end so the normal parsing will give a time difference.
function formatDate(dateString) {
var dateTime, utcFormatRegex, zeroHourOffsetRegex;
// Some APIs return a Date in standard ISO UTC format may not have Z at the end
utcFormatRegex = /^\d{4}-\d{2}-\d{2}T.*$/;
zeroHourOffsetRegex = /^.*Z$/;
if (utcFormatRegex.test(dateString) && !zeroHourOffsetRegex.test(dateString)) {
dateString+='Z';
}
dateTime = new Date(dateString);
}
Given that there are parsing functions for all of the different formats, i need a function that determines which parsing function we should be using based on a regex and parse it accordingly. If regex is not the ideal solution then how can i approach this?
What I'm getting at is there should probably be a more robust solution than 'if there isn't a Z then add one' to get it to parse through the single date time parser. What if we get another date time format that doesn't play nicely with a Z on the end? We'll be making multiple changes at that point in time.

Using a regular expression is OK, but you need to test strictly for the formats you're expecting. If you get something you don't expect, throw an error. It's one of the failings of current built–in parsers is that there's no way to specify strict parsing, e.g. where a format is supplied and the parser throws an error if the input string doesn't match.
There are libraries that can help, a search will reveal quite a few.
But if you only have to support the 3 formats in the OP, something like the following may suit:
/* Return a Date where the input may be:
** string: ISO 8601 timestamp that should be treated as UTC
** whether it has a trailing Z or not
** string: Timestamp in the format (using moment.js tokens):
** ddd, DD MMM YYYY HH:mm:ss GMT
** nunber: UNIX time value, seconds since 1970-01-01 UTC
*/
function toDate(value) {
// Parse the string & fail early if it fails
let d = new Date(value);
// Throw error if couldn't parse value
if (isNaN(d.getTime())) {
throw 'Invalid timestamp: ' + value;
}
// Otherwise, do the work
let days = 'Sun Mon Tue Wed Thu Fri Sat'.split(' ');
let months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');
// Test for time value first as that's the easiest
if (typeof value == 'number' && !isNaN(value)) {
return new Date(value * 1000);
// Test for ISO 8601 next
} else if (/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ?$/.test(value)) {
return new Date(/Z$/.test(value)? value : value + 'Z');
// Test for random format
} else if (/^[a-z]{3}, \d?\d [a-z]{3} \d{4} \d\d:\d\d:\d\d GMT$/i.test(value)) {
let b = value.split(/ |:/);
if (days.includes(b[0].substr(0,3)) && months.includes(b[2])) {
let x = new Date(Date.UTC(
b[3], // year
months.indexOf(b[2]), // month, zero indexed
b[1], // day
b[4], b[5], b[6] // hh:mm:ss
));
// Check value was a valid date, only need to check some parts
if (x.getUTCFullYear() == b[3] &&
x.getUTCDate() == b[1] &&
x.getUTCHours() == b[4] &&
x.getUTCSeconds() == b[6]) {
return x;
} else {
throw 'Invalid timestamp: ' + value;
}
}
// Throw error as must be unknown format
} else {
throw 'Unknown format: ' + value;
}
}
// Minimal testing
var isoString0 = '2014-01-01T00:00:00.000Z',
isoString1 = '2014-01-01T00:00:00.000', // no Z, parse as UTC anyway
randomString = 'Thu, 29 Nov 2018 17:30:56 GMT',
unixTimeValue = 1558606726, // Assume seconds
invalidDate0 = '2018-02-29T00:00:00.000Z', // no 29 Feb in 2018, fail built-in parse
invalidDate1 = 'Thu, 29 Feb 2018 17:30:56 GMT', // no 29 Feb in 2018, fail manual parse
invalidFormat = '6/6/2019'; // Unknown format
[isoString0, isoString1, randomString, unixTimeValue, invalidDate0,
invalidDate1, invalidFormat].forEach(s => {
var result;
try {
result = toDate(s);
console.log(s + ' =>\n' + result.toISOString());
} catch (e) {
console.log(e);
}
});

Related

how to convert date to 20160422060933.0Z format?

In angular I have to save data to database in this time format 20160422060933.0Z ?
Someone told me that this is Microsoft time format. I don't know how to convert date to this format, anyone encountered this before?
2016 is a year, 04 is a month, and 22 is a date but i don't know what 060933.0Z is. We use Dreamfactory API and SQL Server
Later edit: based on another answer, actually this seems to be a standard format colloquially called a "LDAP date". See Converting a ldap date for some details on the format (and how to parse it in Java). It can for sure be easily parsed with any typical JS date library or even without any library.
Let's break it down into pieces.
2016 = full year
04 = month, padded to 2 digits
22 = day of month, likely also padded to 2 digits
06 = hour of day, padded to 2 digits, likely on a 24h scale
09 = minute of the hour, padded to 2 digits
33 = second of the minute, likely padded to 2 digits
. = literal
0 = probably "second fraction"
Z = offset from UTC. Z meaning UTC.
Parsing it
You have several options to parse it:
If you assume you're going to always get an UTC datetime from the backend, you can naively parse it in JavaScript just by extracting the relevant substrings.
const input = '20160422060933.0Z';
new Date(Date.UTC(
input.substr(0, 4), // year
input.substr(4, 2) - 1, // month is 0-indexed
input.substr(6, 2), // day
input.substr(8, 2), // hour
input.substr(10, 2), // minute
input.substr(12, 2), // second
("0." + input.split(/[.Z]/gi)[1]) * 1000 // ms
));
// Fri Apr 22 2016 09:09:33 GMT+0300 (Eastern European Summer Time)
You can be a little creative and actually manipulate the string into an ISO format. Then you can just use the native Date.parse function, which supports parsing ISO strings (other formats are browser-dependent). The advantage is that it'll support dates that are not UTC as well.
new Date(Date.parse(
input.substr(0, 4) + "-" + // year, followed by minus
input.substr(4, 2) + "-" + // month, followed by minus
input.substr(6, 2) + "T" + // day, followed by minus
input.substr(8, 2) + ":" + // hour, followed by color
input.substr(10, 2) + ":" + // minute, followed by color
input.substr(12, 2) + // second
input.substr(14) // the rest of the string, which would include the fraction and offset.
))
// Fri Apr 22 2016 09:09:33 GMT+0300 (Eastern European Summer Time)
Use a library like luxon, momentjs, etc. This you might already have a JS library in your project. You'd need to build a date format pattern to parse this format into a native Date object or some other library-specific object. For example, with momentjs you'd do:
moment("20160422060933.0Z", "YYYYMMDDHHmmss.SZ")
// Fri Apr 22 2016 09:09:33 GMT+0300 (Eastern European Summer Time)
Formatting into it
This side of the operation is even simpler.
Without any date library, you just need to get rid of the "-", ":" and "T" separators from the ISO format. So you can just do the following:
new Date().toISOString().replace(/[:T-]/g, "")
// '20230209175305.421Z'
If you want to use a date library, then you just do the reverse, format operation using the same pattern as for parsing. Eg. in momentjs:
moment(new Date()).utc().format("YYYYMMDDHHmmss.S[Z]")
// "20230209175222.5Z"
(note that I needed to place the "Z" in brackets due to https://github.com/moment/moment-timezone/issues/213).
Just a side note to the other answer here:
You can use ldap2date npm package for parsing, should be not that "heavy" as moment.
Code:
import ldap2date from "ldap2date";
// or import { parse, toGeneralizedTime } from "ldap2date";
const dateString = "20160422060933.0Z";
const date = ldap2date.parse(dateString);
console.log(date.toUTCString());
// Fri, 22 Apr 2016 06:09:33 GMT
const str = ldap2date.toGeneralizedTime(date);
console.log(str);
// 20160422060933Z (note: no period.)
console.log(str.replace("Z", ".0Z"));
// 20160422060933.0Z
function getLdapString(date) {
return ldap2date.toGeneralizedTime(date);
}
const d = new Date();
console.log(getLdapString(d), d.toISOString());
// 20230209181603.965Z 2023-02-09T18:16:03.965Z
And some monkey-patching to match "format":
function getLdapString(date) {
return date.getMilliseconds() !== 0
? ldap2date.toGeneralizedTime(date)
: ldap2date.toGeneralizedTime(date).replace("Z", ".0Z");
}
const d = new Date();
d.setMilliseconds(15);
const d1 = new Date();
d1.setMilliseconds(0);
console.log("Date with milliseconds: ", d.toUTCString(), getLdapString(d));
console.log("Date without milliseconds: ", d1.toUTCString(), getLdapString(d1));
// Date with milliseconds: Thu, 09 Feb 2023 18:22:27 GMT 20230209182227.15Z
// Date without milliseconds: Thu, 09 Feb 2023 18:22:27 GMT 20230209182227.0Z
Or to ignore milliseconds part completelly
function getLdapString(date) {
const copy = new Date(date);
copy.setMilliseconds(0);
return ldap2date.toGeneralizedTime(copy).replace("Z", ".0Z");
}
console.log("Date with milliseconds: ", d.toUTCString(), getLdapString(d));
console.log("Date without milliseconds: ", d1.toUTCString(), getLdapString(d1));
// Date with milliseconds: Thu, 09 Feb 2023 18:29:50 GMT 20230209182950.0Z
// Date without milliseconds: Thu, 09 Feb 2023 18:29:50 GMT 20230209182950.0Z

Check if a date string is in ISO and UTC format

I have a string with this format 2018-02-26T23:10:00.780Z I would like to check if it's in ISO8601 and UTC format.
let date= '2011-10-05T14:48:00.000Z';
const error;
var dateParsed= Date.parse(date);
if(dateParsed.toISOString()==dateParsed && dateParsed.toUTCString()==dateParsed) {
return date;
}
else {
throw new BadRequestException('Validation failed');
}
The problems here are:
I don't catch to error message
Date.parse() change the format of string date to 1317826080000 so to could not compare it to ISO or UTC format.
I would avoid using libraries like moment.js
Try this - you need to actually create a date object rather than parsing the string
NOTE: This will test the string AS YOU POSTED IT.
YYYY-MM-DDTHH:MN:SS.MSSZ
It will fail on valid ISO8601 dates like
Date: 2018-10-18
Combined date and time in UTC: 2018-10-18T08:04:30+00:00 (without the Z and TZ in 00:00)
2018-10-18T08:04:30Z
20181018T080430Z
Week: 2018-W42
Date with week number: 2018-W42-4
Date without year: --10-18 (last in ISO8601:2000, in use by RFC 6350[2])
Ordinal date: 2018-291
It will no longer accept INVALID date strings
function isIsoDate(str) {
if (!/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(str)) return false;
const d = new Date(str);
return d instanceof Date && !isNaN(d) && d.toISOString()===str; // valid date
}
console.log(isIsoDate('2011-10-05T14:48:00.000Z'))
console.log(isIsoDate('2018-11-10T11:22:33+00:00'));
console.log(isIsoDate('2011-10-05T14:99:00.000Z')); // invalid time part
let date= '2011-10-05T14:48:00.000Z';
var dateParsed= new Date(Date.parse(date));
//dateParsed
//output: Wed Oct 05 2011 19:48:00 GMT+0500 (Pakistan Standard Time)
if(dateParsed.toISOString()==date) {
//Date is in ISO
}else if(dateParsed.toUTCString()==date){
//DATE os om UTC Format
}
I think what you want is:
let date= '2011-10-05T14:48:00.000Z';
const dateParsed = new Date(Date.parse(date))
if(dateParsed.toUTCString() === new Date(d).toUTCString()){
return date;
} else {
throw new BadRequestException('Validation failed');
}
I hope that is clear!

How to compare two dates (MonthName YearNumber) in javascript

This functionality is working fine in Chrome... But not IE or FF.
I am trying to validate two fields that take the value of MonthName YearNumber (see screenshot).
I am using Date.parse() to get miliseconds, then compare if Start Date <= End Date.
function IsStartEndDtError(StartDt, EndDt) {
//convert dates to miliseconds to compare if one is greater than other
var StartDtMili = Date.parse(StartDt);
var EndDtMili = Date.parse(EndDt);
if (StartDtMili <= EndDtMili) {
return false;
}
else {
return true;
}
}
What appears in Firebug:
Since the format your date is in isn't universally supported you can try a library like Date.js:
Date.parse("November 2012")
// returns: Thu Nov 01 2012 00:00:00 GMT+0000 (GMT)
If you don't want another library you can manually replace the month names with numbers and create a new date string.
Ecmascript does not seem to support full month names, if you look at "Section 15.9.1.15 Date Time String Format" in the spec.
In Firefox:
new Date("November 2012")
// Invalid Date
new Date("2012-11")
// Thu Nov 01 2012 00:00:00 GMT+0000 (GMT)
The second date format should be standardized across browsers, the first isn't.
11 1999, November 1999 are not parsable formats. You either need to use a date library that is more flexible with its input formats, or process your input and identify the parts in it:
function IsStartEndDtError(StartDt, EndDt) {
var months = {
January: 0,
February: 1,
...
};
//convert dates to miliseconds to compare if one is greater than other
var StartDtMili = (new Date(StartDt.split(" ")[1], month[StartDt.split(" ")[0]])).getTime();
var EndDtMili = (new Date(EndDt.split(" ")[1], month[EndDt.split(" ")[0]])).getTime();
if (StartDtMili <= EndDtMili) {
return false;
}
else {
return true;
}
}

Test if a string has timezone in javascript

I receive a date time as a string from the server that might look like this:
07/08/2012 13:17:32
This is a UTC time.
Or it might have timezone in format:
07/08/2012 13:17:32 UTC+01:00
I need a general way to parse this into a Date object for display. If I do a var d = new Date(str) then the first example it assumes is a local time.
Edit:
It might not always be 'UTC' in the string, I think it could be GMT, or Z, or any other timezone signifier.
Any ideas?
If your timezone is always in format UTC+nn, and strings with explicit UTC TZ are parsed correctly, as I assume from your question, then simple
if (date_string.search(/a-z/i) == -1) {
date_string += 'UTC+00:00'
}
will do.
As a quick and dirty solution, it looks like the timezone is a final "part" of the format separated by whitespace. So you could count the number of "parts" in the input string and add a default timezone if none is found. For example:
function parseDateDefaultUTC(str) {
var parts = str.split(/\s+/);
return new Date((parts.length===3) ? str : str + ' UTC');
}
var d;
d = parseDateDefaultUTC("07/08/2012 13:17:32");
d; // => Sun Jul 08 2012 07:17:32 GMT-0600 (MDT)
d = parseDateDefaultUTC("07/08/2012 13:17:32 UTC+01:00");
d; // => Sun Jul 08 2012 06:17:32 GMT-0600 (MDT)

Regarding JavaScript new Date() and Date.parse()

var exampleDate='23-12-2010 23:12:00';
I want to convert above string into a date and have tried a couple things:
var date = new Date(exampleDate); //returns invalid Date
var date1 = Date.parse(exampleDate); //returns NAN
This code is running fine in IE and Opera, but date is returning me an invalid Date and date1 is returning NAN in Firefox. What should I do?
The string in your example is not in any of the standard formats recognized by browsers. The ECMAScript specification requires browsers to be able to parse only one standard format:
The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ
This format includes date-only forms:
YYYY
YYYY-MM
YYYY-MM-DD
It also includes time-only forms with an optional time zone offset appended:
THH:mm
THH:mm:ss
THH:mm:ss.sss
Also included are “date-times” which may be any combination of the above.
If the String does not conform to that format the function may fall back to any
implementation-specific heuristics or implementation-specific date formats. Unrecognizable Strings or dates
containing illegal element values in the format String shall cause Date.parse to return NaN.
So in your example, using 2010-12-23T23:12:00 is the only string guaranteed to work. In practice, most browsers also allow dates of the format DD Month YYYY or Month DD, YYYY, so strings like 23 Dec 2010 and Dec 23, 2010 could also work.
Above format is only supported in IE and Chrome.
so try with another formats. following are some formats and there supporting browsers.
<script type="text/javascript">
//var dateString = "03/20/2008"; // mm/dd/yyyy [IE, FF]
var dateString = "2008/03/20"; // yyyy/mm/dd [IE, FF]
// var dateString = "03-20-2008"; // mm-dd-yyyy [IE, Chrome]
// var dateString = "March 20, 2008"; // mmmm dd, yyyy [IE, FF]
// var dateString = "Mar 20, 2008"; // mmm dd, yyyy [IE, FF]
// Initalize the Date object by passing the date string variable
var myDate = new Date(dateString);
alert(myDate);
</script>
You could parse it manually with a regular expression then call the date constructor with the date elements, as such:
var parseDate = function(s) {
var re = /^(\d\d)-(\d\d)-(\d{4}) (\d\d):(\d\d):(\d\d)$/;
var m = re.exec(s);
return m ? new Date(m[3], m[2]-1, m[1], m[4], m[5], m[6]) : null;
};
var dateStr = '23-12-2010 23:12:00';
parseDate(dateStr).toString(); //=> Thu Dec 23 2010 23:12:00 GMT-0800
JavaScript should support conversion at least from the following dateStrings:
* yyyy/MM/dd
* MM/dd/yyyy
* MMMM dd, yyyy
* MMM dd, yyyy
Try with:
var exampleDate='12/23/2010 23:12:00';
var date = new Date(exampleDate);
Use datejs and this code:
var exampleDate='23-12-2010 23:12:00';
var myDate = Date.parseExact(exampleDate, 'dd-MM-yyyy hh:mm:ss');
myDate should be a correctly constructed Date object.
Just use in this format:
var exampleDate='2010-12-23 23:12:00';
#casablanca has a good answer but it's been 10+ years and this still has a lot of weight in Google so I thought I'd update with a new answer.
TL;DR
// Use an ISO or Unix time string to generate `Month DD, YYYY`
const newDate = new Date('23-12-2010')
const simpleDate = `${newDate.toLocaleString('en-us', { month: 'long' } )} ${newDate.getDate()}, ${newDate.getFullYear()}`
// yields: December, 23 2010 (if you want date suffix, read until the end)
Background: Dates come in a lot of formats, but you're mostly going to receive:
An ISO 8601 format date (YYYY-MM-DDTHH:mm:ss.sssZ) where Z is a UTC timezone offset. You might also get a subset of this (ie, YYYY-MM-DD)
Unix timestamp format date (1539734400), where the number is literally the total amount of milliseconds since the beginning of Unix time, Jan 1st 1970.
Basics: JS has a built-in Date prototype that accepts ISO 8601 and derivatives (of just time or just date). You can instantiate with new Date and return a date object OR you can use the Date.parse() method to return a Unix timestamp.
const dateObj = new Date('23-12-2010:23:12:00') // returns date object
const dateDateOnly = new Date('23-12-2010') // returns date object
const dateTimeOnly = new Date('23:12:00') // returns date object
const dateString = Date.parse('23-12-2010:23:12:00') // returns Unix timestamp string
You can also break the date into 7 parameters: the year, the month (starting from 0), the day, the hour, the minutes, seconds and milliseconds with the time zone offset - NOTE, I've used the multi-params approach only once in my career. Since I'm in Texas I get, UTC-5 (Central Time) when I run the following:
const dateByParam = new Date(2021, 2, 26, 13, 50, 13, 30) // Fri Mar 26 2021 13:50:13 GMT-0500 (Central Daylight Time)
New-ish Stuff toLocaleString: Typically, the return from the Date object is still pretty dense like our last example (Fri Mar 26 2021 13:50:13 GMT-0500 (Central Daylight Time) so additional methods have been added to help developers.
Typically with a date, I want something like March 21st, 2021 - the day and year have been easy to get for a long time:
// Assuming myDate is a JS Date object...
myDate.getDate() // date on the calendar, ie 22
myDate.getDay() // day of the week, where 0 means Sunday, 1 means monday, etc
myDate.getFullYear() // 4 digit year, ie, 2021
But I've always had to build a function to turn getDay into January, February, March, not anymore. toLocaleString() gives you some new superpowers. You can pass it two params, a string for region (ie, en-us) and an object with what you want back (ie, { month: 'long' }). This helps internationalize the response, if need be.
// Again, assuming myDate is a JS Date object...
myDate.toLocaleString('en-us', { month: 'long' } ) // March
Date Suffix I've still seen no built-in way to get the suffix for a date, like th, st, so I built this utility function that uses the modulus % operator to check the divisor of each day number and apply the right suffix (aimed at an American audience but might be the same elsewhere?).
/**
* setDateSuffix()
*
* Desc: Takes two digit date, adds 'st', 'nd', 'rd', etc
*
* #param { integer } num - a number date
*/
export const setDateSuffix = (num) => {
const j = num % 10,
k = num % 100
if (j === 1 && k !== 11) {
return num + "st";
}
if (j === 2 && k !== 12) {
return num + "nd";
}
if (j === 3 && k !== 13) {
return num + "rd";
}
return num + "th";
}
Altogether now.. Long winded way of getting here, but if I am given an ISO or Unix date and I want Month DDth, YYYY, this is what I run:
// setDateSuffix IS NOT PART OF BUILT-IN JS!
const newDate = new Date('23-12-2010')
const simpleDate = `${newDate.toLocaleString('en-us', { month: 'long' } )} ${setDateSuffix(newDate.getDate())}, ${newDate.getFullYear()}`
// yields: December 23rd, 2010
Note - all of this will likely change, hopefully for the better, when temporal becomes a reality in JS: https://github.com/tc39/proposal-temporal. Look forward to somebody's 2030 update of this post!

Categories

Resources