Sort Data and Time String in Javascript [duplicate] - javascript

This question already has answers here:
Why does Date.parse give incorrect results?
(11 answers)
Closed 1 year ago.
I Know there are lot of answers out there to sort date and time. But in every answer, they sort only date or they sort only time. I can't find the answer which sorts both date and time string.
Here's the Code I tried:
var x = [ '29/09/2020 11:55:56', '04/08/2021 11:57:06', '30/09/2019 15:19:49', '04/08/2021 13:57:06' ]
x.sort((a, b) => new Date(b).getTime() - new Date(a).getTime()).reverse();
console.log(x)
Output I got:
["04/08/2021 13:57:06", "30/09/2019 15:19:49", "04/08/2021 11:57:06", "29/09/2020 11:55:56"]
Required Output:
["04/08/2021 13:57:06","04/08/2021 11:57:06", "29/09/2020 11:55:56", "30/09/2019 15:19:49", ]
I searched whole day. Please Help me with some solutions to fix this.

The problem is that Date uses a yyyy/mm/dd format instead of a dd/mm/yyyy format. So all your Date objects are Invalid Date.
P.S. If you sort but have to reverse the order afterwards, just reverse the subtractions.

if momentjs an option:
var x = [ '29/09/2020 11:55:56', '04/08/2021 11:57:06', '30/09/2019 15:19:49', '04/08/2021 13:57:06' ];
x.sort((a, b) => moment(b,"DD-MM-YYYY hh:mm:ss") - (moment(a,"DD-MM-YYYY hh:mm:ss")));
console.log(x)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>

I think this is a very hard task when you try to compare date and time in strings. And would take to much time when executing. Instead i would convert date and times to timestamp format then compare. To convert a date string to a timestamp format i referred to the answer on this stackoverflow question
const dates = [ '29/09/2020 11:55:56', '04/08/2021 11:57:06', '30/09/2019 15:19:49', '04/08/2021 13:57:06' ];
dates.sort((date1,date2) => {
var dateTimeParts = date1.split(' '),
timeParts = dateTimeParts[1].split(':'),
dateParts = dateTimeParts[0].split('/'),
date1;
date1 = new Date(dateParts[2], parseInt(dateParts[1], 10) - 1, dateParts[0], timeParts[0], timeParts[1], timeParts[2]);
var dateTimeParts2 = date2.split(' '),
timeParts2 = dateTimeParts2[1].split(':'),
dateParts2 = dateTimeParts2[0].split('/'),
date2;
date2 = new Date(dateParts2[2], parseInt(dateParts2[1], 10) - 1, dateParts2[0], timeParts2[0], timeParts2[1], timeParts[2]);
return date1.getTime() / 1000 - date2.getTime() / 1000
});
console.log(dates);

Date and time are not separate in the context of a javascript Date object, internally they are just stored as millisecond offsets. But, you can't rely on Date parsing strings for you. Write a custom parser for you specific date-time strings and then then simply subtract dates.
There's no need for reverse() either, simply switch the subtraction to change direction.
const x = [ '29/09/2020 11:55:56', '04/08/2021 11:57:06', '30/09/2019 15:19:49', '04/08/2021 13:57:06' ];
const parse_date_string = (str) => {
const [date, time]= str.split(' ');
const [day, month, year] = date.split('/');
const [hr, min, sec] = time.split(':');
return [year, (month - 1), day, hr, min, sec].map(Number);
}
x.sort((a, b) => new Date(...parse_date_string(b)) - new Date(...parse_date_string(a)));
console.log(x)

Related

Date parsing from different formats?

I have a string, which can come in different formats:
01.01.2020
01/01/2020
01-01-2020
or
2020-01-01
2020.01.01
2020/01/01
Now if I try doing
const date = new Date(myDateString);
I will in some cases get an error "Invalid Date".
How can I cover all scenarios and transform any scenario into a valid date?
It seems like the new Date(), only takes the format Y-m-y?, even though the other cases are also "valid" dates?
You can moment to do that. But you need to know which format is used.
https://stackoverflow.com/a/44134515/3573340
Date() constructor is accurate if it's parameters are YYYY, MM, DD. The dates of Jan 1, 2020 should go like this:
new Date(2020, 0, 1) // Month is 0 index, so MM -1
Given an array of strings .map() and split() each by .-/ delimitters, resulting in sub-arrays of 3 strings from each string:
strArr.map(str => str.split(/[-./]/))
then .flatMap() to catch any array that starts with 2 digits and .reverse() it:
.flatMap(sub => sub[0].length === 2 ? [sub.reverse()] : [sub])
Finally, .flatMap() each sub-array into the Date() constructor:
.flatMap(sub => [new Date(+sub[0], +sub[1] - 1, +sub[2])])
const jan1_2020 = [
`01.01.2020`,
`01/01/2020`,
`01-01-2020`,
`2020-01-01`,
`2020.01.01`,
`2020/01/01`
];
const formatDate = strArr => strArr.map(str => str.split(/[-./]/)).flatMap(sub => sub[0].length === 2 ? [sub.reverse()] : [sub]).flatMap(sub => [new Date(+sub[0], +sub[1] - 1, +sub[2])]);
let x = formatDate(jan1_2020);
console.log(x);
You can split the string on the known delimiters and then see whether the first part has four digits. If so, you know it's year-month-day. If not, you know it's month-day-year. You can then construct the date accordingly:
const dateStrings = [
"10.31.2020",
"10/31/2020",
"10-31-2020",
"2020-10-31",
"2020.10.31",
"2020/10/31",
]
function parseDateStr(str) {
// split on dots, dashes, and slashes
const parts = str.split(/[./-]/);
// mm-dd-yyyy
const [year, month, day] = parts[0].length > 2 ? parts : [parts[2], parts[0], parts[1]];
// alternate for dd-mm-yyyy
// const [year, month, day] = parts[0].length > 2 ? parts : parts.reverse();
// construct and return the date. (month is 0 based)
return new Date(year, month - 1, day);
}
const results = dateStrings.map(parseDateStr);
console.log(results);
JavaScript will parse the dates in following formats:
ISO Date - YYYY-MM-DD
Short Date - YYYY/MM/DD
Long Date - Jan 01 2022
Some other versions of the dates also might also work but the best practice is to limiting your formats to ISO format. For more on js date formats refer this

Comparing two dates in when one date is in a previous year

I have this function that determines if two sets of dates are overlapping. When the set of dates are in the same year, it works correctly and returns true if there's overlap. If one of the dates is from a previous year, the comparison fails to detect the overlap.
For instance:
var obj = { startDate1: "02/01/2020",
endDate1: "03/01/2020",
startDate2:"02/05/2020",
endDate2:"02/15/2020" }
Using:
if ((obj.endDate1 >= obj.startDate2 && obj.startDate1 <= obj.endDate2) ||
(obj.endDate2 >= obj.startDate1 && obj.startDate2 <= obj.endDate1)) {
}
Returns true since the dates overlap.
However, when startDate1 is in a different year (2019), like so:
var obj = { startDate1: "12/01/2019",
endDate1: "03/01/2020",
startDate2:"02/05/2020",
endDate2:"02/15/2020" }
The expression above fails to detect the overlap even though startDate1 and endDate1 do in fact overlap with startDate2 and endDate2.
I've tried using moment and regex to format the dates, but to no avail. Any ideas what I'm doing wrong?
Edit: Here's how I'm using moment:
const obj = {
startDate1: moment(new Date(value.startDate)).format("MM/DD/YYYY"),
endDate1: moment(new Date(value.endDate)).format("MM/DD/YYYY"),
startDate2: moment(new Date(startDate)).format("MM/DD/YYYY"),
endDate2: moment(new Date(endDate)).format("MM/DD/YYYY")
};
The values I'm passing in to new Date are iso date strings.
Sorry for the confusion..
Edit #2/Answer:
I just converted the dates using native JS Date.parse.
const obj = {
certStart: Date.parse(value.startDate),
certEnd: Date.parse(value.endDate),
startDate: Date.parse(startDate),
endDate: Date.parse(endDate) };
Using the Native Date function in js
var obj = {
startDate1: "02/01/2020",
endDate1: "03/01/2020",
startDate2:"02/05/2020",
endDate2:"02/15/2020"
};
// Create a clone of obj object and convert values to timestamp.
var $dates = {};
$dates.startDate1 = new Date(obj.startDate1).getTime();
$dates.startDate2 = new Date(obj.startDate2).getTime();
$dates.endDate1 = new Date(obj.endDate1).getTime();
$dates.endDate2 = new Date(obj.endDate2).getTime();
Comparing time:
if (($dates.endDate1 >= $dates.startDate2 && $dates.startDate1 <= $dates.endDate2) ||
($dates.endDate2 >= $dates.startDate1 && $dates.startDate2 <= $dates.endDate1)) {
// etc...
}
Use new Date() or moment.js to create and manipule dates.
var obj = { startDate1: moment("12/01/2019"),
endDate1: moment("03/01/2020"),
startDate2:moment("02/05/2020"),
endDate2:moment("02/15/2020")}
and moment functions to manipulate dates
Despite your tag, it doesn't seem like you are actually formatting the dates using momentjs - unless you aren't sharing some of the code. It really looks like you are making string comparisons. For date comparison in general, I suggest reading the related: Compare two dates with JavaScript
Relevant MDN on Date: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
And don't forget to reference the moment.js docs: https://momentjs.com/docs/
EDIT: As already pointed out, you are using .format(), which returns a display string https://momentjs.com/docs/#/displaying/format/ - so you're still comparing strings instead of dates.
You may want to checkout isBetween: https://momentjs.com/docs/#/query/is-between/
Parse date string to Date, then get its value as a number:
const [m, d, y] = dateStr.split('/').map(Number)
// month is 0-indexed, whereas year and day are 1-indexed
const dateVal = new Date(y, m - 1, d).valueOf()
Then, you can easily compare them with >, <, and so on.
If the date string is in YYYY/MM/DD format then the string comparison will work.
var obj = {
startDate1: "2019/12/01",
endDate1: "2020/03/01",
startDate2: "2020/02/05",
endDate2: "2020/02/15"
}
// this will work
if ((obj.endDate1 >= obj.startDate2 && obj.startDate1 <= obj.endDate2) ||
(obj.endDate2 >= obj.startDate1 && obj.startDate2 <= obj.endDate1)) {
console.log('overlap detected')
}

How can I substract hours from my date using data from a string?

I have a date in UTC in javascript, and I would like to substract some hours.
I searched online and apparently I should concat the substraction to the date like the following:
const diff = "-5"
const utcDate = "2017-02-22 17:28:13"
const date = new Date(utcDate + diff[0] + ' ' + diff[1])
//desired output: 2017-02-22 12:28:13
But I can't seem to make it work.
I do all my Date calculations with .getTime().
So an hour is 3600000 milliseconds.
const MILLISECONDS_HOUR = 3600000;
const diff = -5;
const utcDate_str = "2017-02-22 17:28:13";
const utcDate = new Date( utcDate_str );
const minus_5_hours = new Date( utcDate.getTime() + ( MILLISECONDS_HOUR * diff ));
console.log( utcDate.toJSON());
console.log( minus_5_hours.toJSON());
The big advantage is that javascript will take care of leap years, month boundaries and such.
But since you are calling your variable diff, are you trying to calculate lcoal time vs UTC time?
In that case, reread the javascript Date methods. There's a bunch of methods to handle both UTC and local time, for example: date.getUTCDate() and date.getDate().
So you might not have to calculate all of this yourself.
Also, if you format your dates according to the ISO, "2017-02-22T17:28:13.000Z", this will automatically get parsed as UTC.
someDate.setHours(someDate.getHours()+1);
From https://stackoverflow.com/a/1050782/537998

Get millis when using new Date().toLocaleTimeString() [duplicate]

This question already has answers here:
Convert a Unix timestamp to time in JavaScript
(34 answers)
Closed 4 years ago.
I have this code:
v.d = new Date().toLocaleTimeString();
it yields something like this:
20:11:40
So this is the time of day to the second, without milliseconds.
My question is: is there a built in call that can show just the time with milliseconds?
I am looking for something like this (to the millisecond):
20:11:40.124
This works, but I am looking for something more compact if possible:
const d = new Date();
v.d = d.toLocaleTimeString() + `.${d.getMilliseconds()}`;
this yields:
20:17:30.744
note that to make this work well, you need to add this part:
Formatting milliseconds to always 3 digits for d.getMilliseconds() call
There’s to ISOString(). But stepping back a level, with that exception there’s no standard for formatting dates in js. So you can use toISOString or build up your own string with individual date functions.
I did find one issue with your original solution. When I perform it it yields hh:mm:ss PM.mil. I assume you want hh:mm:ss.mil Here is that solution written as a function so you can pass the date object in and get the proper format:
const d = new Date()
const getTimeWithMilliseconds = date => {
const t = d.toLocaleTimeString();
return `${t.substring(0,8)}.${date.getMilliseconds() + t.substring(8,11)}`;
}
console.log(getTimeWithMilliseconds(d));
Or if you want it in 24 hour format:
const d = new Date()
const getTimeWithMilliseconds = date => {
return `${date.toLocaleTimeString('it-US')}.${date.getMilliseconds()}`;
}
console.log(getTimeWithMilliseconds(d));
You can't depend on toLocaleTimeString returning a specific format as it's implementation dependent. It's much more reliable to build the format yourself, e.g.:
function getFormattedTime(date) {
var d = date || new Date();
var z = n => ('0'+n).slice(-2);
var zz = n => ('00'+n).slice(-3);
return `${z(d.getHours())}:${z(d.getMinutes())}:${z(d.getSeconds())}.${zz(d.getMilliseconds())}`;
}
console.log(getFormattedTime());
console.log(getFormattedTime(new Date(2018,1,1)));
console.log(getFormattedTime(new Date(2018,4,30,23,51,12,89)));
Also see Where can I find documentation on formatting a date in JavaScript?

Sort an array of strings that are dates JavaScript

I've tried using underscorejs, min and max methods but they can't handle strings. From what i've read and learnt anyway, since I get infinite back from both.
My array looks like : dateData = ["26/06/2016", "04/06/2016", "13/05/2016", "20/07/2016"]
How can I grab the last and the first date in these?
I tried using sort also that looks like : _.chain(dateData).sort().first().value() but I get back the last item in the array rather then the last date in the array.
var dateData = ["26/06/2016", "04/06/2016", "13/05/2016", "20/07/2016"];
function dateToNum(d) {
// Convert date "26/06/2016" to 20160626
d = d.split("/"); return Number(d[2]+d[1]+d[0]);
}
dateData.sort(function(a,b){
return dateToNum(a) - dateToNum(b);
});
console.log( dateData );
To retrieve the first, last date:
var firstDate = dateData[0];
var lastDate = dateData[dateData.length -1];
Basically, if you first convert all your 26/06/2016 to a date Number like 20160626 you can .sort() those numbers instead.
so you're basically sorting:
20140626
20140604
20140513
20140720
resulting in:
[
"13/05/2016",
"04/06/2016",
"26/06/2016",
"20/07/2016"
]
If we can format the dateStrings in a particular format, then sorting them as strings also sorts them as dates e.g. YYYY-MM-DD.
You can use localeCompare to compare strings.You can use following code to sort the dates:
dateData = ["26/06/2016", "04/06/2016", "13/05/2016", "20/07/2016"]
dateData.sort(function(a, b){
var A = a.split("/");
var B = b.split("/");
var strA = [ A[2], A[1], A[0] ].join("/");
var strB = [ B[2], B[1], B[0] ].join("/");
return strA.localeCompare( strB );
});
console.log( dateData );
Once sorted, you can get the min and max dates as:
var minDate = dateData[0];
var maxDate = dateData[ dateData.length - 1 ];
The getTime() method returns the numeric value corresponding to the
time for the specified date according to universal time. Date.getTime()
dateData = ["26/06/2016", "04/06/2016", "13/05/2016", "20/07/2016"]
.map(a=>a.split('/').reverse().join('/'))
.sort((a,b)=>new Date(a).getTime() - new Date(b).getTime());
console.log(dateData);
A number people have already touched on this, but you need to convert the date strings to something that can be compared in the sort function. The one thing I haven't seen shared is how to get the first and last dates. This should do the trick:
//original date array
var dateData = ["04/06/2016", "13/05/2016", "20/07/2016","26/06/2016"];
//map through the original array and convert the dates to js date objects
var formattedDates = dateData.map(function(date){
var splitDate = date.split("/")
return new Date(splitDate[2],splitDate[1]-1,splitDate[0])
})
//sort the dates
formattedDates.sort(function(a,b){
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a) - new Date(b);
});
//Now you can get the first and last dates:
var firstDate = formattedDates[0]
var lastDate = formattedDates[formattedDates.length-1];
//log to check:
console.log('first date: ', firstDate)
console.log('last date: ', lastDate)
One way I know to do this is using the .sort() function for a string. https://msdn.microsoft.com/en-us/library/4b4fbfhk(v=vs.94).aspx
You would have to change your array into YYYY-MM-DD
then you could have the following code
var dateData = ["2016/06/26", "2016/06/04", "2016/05/13", "2016/07/20"];
dateData.sort();
var first = dateData[0];
var last = dateData[dateData.length-1];
where first is the earliest date and last is the latest date
To be easier for the subsequest operations, change the format to be like this: YYYY/MM/DD.
This way regular sorting will get you the min and max properly, and you won't need further parsing.
Helper function to sort would be like this:
for(var i=0;i<dateData.length;++i)
{
var split = dateData[i].split("/");
dateData[i] = split.reverse().join("/");
}
Roko's answer worked for me, and +1. And Jose had a similar thought to me, +1...
...There's an easier and more robust way: .valueOf()
converting date string to number:
const dateAsNumber = new Date(dateAsString).valueOf()
JS has a built in method/function for calculating the number of milliseconds that have passed since a date; that's .valueOf(), which can be called on a Date object. So, turn your date string into a Date object (with "new Date()" with the date string as the argument), and then convert to milliseconds.
After that, the normal .sort() works fine. As shown below, for your convenience:
const arrayOfDateStrings = ["5/01/2012", "10/01/2020", "10/01/2019", "11/30/2016", "10/01/2021", "02/01/2020"];
const sortedArray = arrayOfDateStrings.sort((a,b)=>new Date(a).valueOf() - new Date(b).valueOf());
console.log(sortedArray);
Or Moment.js can be used instead of the built-in Date object/functions, that works in a very similar way.

Categories