How can i get the first nearest date in the past? - javascript

I have this arrays
const datesToBeChecked = ['2020-07-03','2020-07-06', '2020-07-13', '2020-07-20']
const dateToCheckFor = '2020-07-12';
I need to get the first nearest date in the past - so when the date is 2020-07-12 i need to get
2020-07-06 from datesToBeChecked.
WHAT I TRIED
I tried this code
datesToBeChecked.forEach(date => {
let diff = moment(dateToCheckFor).diff(moment(date), 'days');
console.log(diff);
if (diff > 0) {
if (nearestDate) {
if (moment(date).diff(moment(nearestDate), 'days') < 0) {
nearestDate = date;
}
} else {
nearestDate = date;
}
}
});
but that gives me the earliest date in the array - 2020-07-03. But i need the first BEFORE THE DATE I CHECK FOR

Actually your logic is almost there. You don't need the nested if conditional. In essense, what you want is to cache the difference, and compare the current difference with the cached difference. If the cached difference is smaller, then we know the current date is the nearest one. Otherwise, we continue for the search.
This solution will work even if the dates are not sorted in chronological order (oldest to newest):
const datesToBeChecked = ['2020-07-03', '2020-07-06', '2020-07-13', '2020-07-20']
const dateToCheckFor = '2020-07-12';
let nearestDate;
let cachedDiff = Infinity;
datesToBeChecked.forEach(date => {
let diff = moment(dateToCheckFor).diff(moment(date), 'days');
if (diff > 0 && diff < cachedDiff) {
cachedDiff = diff;
nearestDate = date;
}
});
console.log(nearestDate);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>

The first element is the closest date in the past (at least yesterday, today results won't show up).
If empty then dateToCheckFor elements does not contain any date in past.
const mappedDates = dateToCheckFor
.map(d => moment(dateToCheckFor).diff(moment(d), 'days'))
.filter(diff => diff <= -1)
.sort()
.reverse();

You could try sorting all of the dates in a hash table first. So you would have a hash table:
{
date1: difference,
date2: difference,
...
}
then you would sort the objects values, such as here: Sorting object property by values
then take the first date from that, which is before the dateToCheckFor
I hope this is not too inefficient for you.

Here's one way with good ole vanilla js. Moment.js weighs 230+kb
const datesToBeChecked = ['2020-07-03', '2020-07-06', '2020-07-13', '2020-07-20']
const dateToCheckFor = '2020-07-12';
dateToCheckFor_d = new Date(dateToCheckFor).getTime();
let possibilities = datesToBeChecked.filter(dt => {
return (new Date(dt).getTime() < dateToCheckFor_d)
})
possibilities.sort().reverse()
console.log(possibilities[0])

In your code just update the below line
if (moment(date).diff(moment(nearestDate), 'days') < 0
to
if (moment(date).diff(moment(nearestDate), 'days') > 0

Related

JavaScript: sort an array of timestamps with day–month–year format

I have an array of timestamps or strings that are of this format - day–month–year.
e.g
['03-11-2018', '04-12-2018', '10-01-2017', '10-12-2017']
And I want to sort them chronologically so the above array would become [ '10-01-2017', '10-12-2017', '03-11-2018', '04-12-2018' ]
I am aware that you can use moment.js to do this but I want to write this by hand without using any 3rd lib.
Here is my attempt:
function sortTimestamp(array) {
array.sort((time1, time2) => {
const [day1, month1, year1] = time1.split('-')
const [day2, month2, year2] = time2.split('-')
return year2 > year1
? -1
: year2 < year1
? 1
: month2 > month1
? -1
: month2 > month1
? 1
: day2 > day1
? -1
: 1
})
}
It works ok but it is not really readable and it is a very imperative approach. I wonder if there is a better way to do it and also ideally it can be extended to support that other date formats e.g. month–day–year.
An alternative is to specify with extra arguments what the format is (regex) and how it should be reordered to ISO-style YYYYMMDD format. Then the function can perform that replacement and do a lexical sort:
function sortTime(array, regex, iso) {
let fmt = time => time.replace(regex, iso);
return array.sort((time1, time2) =>
fmt(time1).localeCompare(fmt(time2)));
}
// demo
let dates = ['03-11-2018', '04-12-2018', '10-01-2017', '10-12-2017'];
let result = sortTime(dates, /(..)-(..)-(....)/, "$3$2$1");
console.log(result);
So if the input should be interpreted as mm-dd-yyyy instead of dd-mm-yyyy, then make the third argument "$3$1$2" instead of "$3$2$1".
It's a little simpler using subtraction rather than nested ternaries.
function sortTimestamp(array) {
array.sort((time1, time2) => {
const [day1, month1, year1] = time1.split('-')
const [day2, month2, year2] = time2.split('-')
return year1 - year2 || month1 - month2 || day1 - day2;
})
}
You can first convert them to Date objects then use the getTime functionality to compare for sorting. A similar question was answered here
const dates = ['03-11-2018', '04-12-2018', '10-01-2017', '10-21-2017']
sortedDates = dates.sort((a,b)=>{
const dateA = new Date(a)
const dateB = new Date(b)
//change the comparison sign for required orde
return dateA.getTime() < dateB.getTime()
? 1
: -1
})
console.log(sortedDates)
Similar to Craques answer, but added both ASC, DESC sorting using the getTime method
ASC:
dates.sort((a,b) => new Date(a).getTime() - new Date(b).getTime())
DESC:
dates.sort((a,b) => new Date(b).getTime() - new Date(a).getTime())
It turns out that the new Date() constructor is fairly robust when it comes to interprete date strings. The formats used in the following snippet seem to work reliably:
const a=["2018-09-05","09-21-2018","12/24/2018","19 Sep 2018",'03-11-2018', '04-12-2018', '10-01-2017', '10-21-2017'];
console.log(
a.map((s,i)=>[new Date(s),i])
.sort(([a],[b])=>a-b)
.map(([_,i])=>i+': '+a[i])
)
You can leave out the index (i+': '+) in the output of course to make it look pretty again.

Javascript functional programming and dynamic values in an Array

I am trying to write a function that grabs the first 14 days (starting today) with momentJS. My function currently looks like
let dateArr = Array(14).fill(moment())
.map((date) => {
return date.add(1, 'days')
});
I understand that fill is for static values and not dynamic ones, so how would I go about fixing this up so that I have an array that has, ['11/12', '11/13', '11/14', etc...]
I think i need some sort of recursion so that it adds 1 day from the last iteratee, or else i think it'll just keep adding 1 day from today for each iteration
Array(14).fill(moment())
.map((date, i) => date.add(1, 'days').format('MM/DD'));
OUTPUT:
(14) ["01/13", "01/14", "01/15", "01/16", "01/17", "01/18", "01/19", "01/20", "01/21", "01/22", "01/23", "01/24", "01/25", "01/26"]
UPDATE:
Start from today^
Array(14).fill(moment())
.map((date, i) => {
if(i === 0) {
return date.format('MM/DD')
}
return date.add(1, 'days').format('MM/DD')
});
(14) ["01/12", "01/13", "01/14", "01/15", "01/16", "01/17", "01/18", "01/19", "01/20", "01/21", "01/22", "01/23", "01/24", "01/25"]
What you are doing is filling an array with a SINGLE date object, like doing this:
let date = moment();
let dateArr = Array(14).fill(date)
.map((date, index) => {
return date.add(index, 'days')
});
moment.add() will not return a new date object, but modify the current date object. What you need is to retrieve a new date object on each map (instead of returning the same date object):
let dateArr = Array(14).fill(moment())
.map((date, index) => {
return date.clone().add(index, 'days'); // Note the `clone()` so a new object is created.
});
And if you want to just retrieve a string, just add a format:
let dateArr = Array(14).fill(moment())
.map((date, index) => {
return date.clone().add(index, 'days'.format('MM/DD'); // Note the `clone()` so a new object is created.
});
Also note how a index is used to add days dinamically depending on the array position, hence first position will be today (adding 0 days).
I hope I understand you right. You will get all days from maybe today +14 days in the future. But I dont understand why you will use fill() methode?
Then that will work for you:
var getDaysArray = function(year, month, day) {
var date = new Date(year, month, day);
var result = [];
var i;
for (i=0;i<14;i++) {
result.push(date.getMonth() + '/' + date.getDate());
date.setDate(date.getDate() + 1);
}
return result;
}
console.log(getDaysArray(2021,1,12) )

Check If date is over a 2 weeks old

I’m trying to create a if statement that checks a date against the current date and throw a error if its less that 2 weeks old.
My current code
const moment = require('moment')
const today = moment().format()
const createdDate = '2020-07-30'
if (createdDate <= today ) {
console.log('Good')
} else {
console.log('Bad')
};
console.log(today)
Would appreciate any suggestions.
You can use moments subtract feature along with isSameOrAfter.
There is another option where you add two weeks to the createdDate and compare that today as well. Whichever makes more sense for you.
const today = moment();
const createdDate = moment("2020-08-30")
if (today.subtract(2, 'weeks').isSameOrAfter(createdDate)) {
console.log('good')
} else {
console.log('bad')
}
You can do this two ways and you can choose what suit you better. Both solution below will perfectly for your scenario.
You can simply use moment diff function by getting the difference of days in numbers and if the difference is more then or equal to 14 days then its good else it will be bad.
Using diff function
Live Demo:
var today = moment().startOf('day')
var createdDate = moment("2020-07-30", "YYYY-MM-DD").startOf('day')
var diff = today.diff(createdDate, 'days')
if (diff >= 14) {
console.log('Good')
} else {
console.log('Bad')
};
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.js"></script>
Using add function and clone function get the results
You can use add function along with clone and using startOf day function to make sure we always get the date from day start not when when performed the operation to check for comparison.
Live Demo:
let today = moment().startOf('day').format('YYYY-MM-DD') //today
let createdDate = moment('2020-07-30', 'YYYY-MM-DD').clone().add(14, 'days').startOf('day').format('YYYY-MM-DD') //created date minus two weeks
if (createdDate <= today) {
console.log('Good')
} else {
console.log('Bad')
};
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.js"></script>
You can use add feature of Moment as well.
const today = moment();
const createdDate = moment("2020-07-30")
if (createdDate.add(2, 'weeks').isSameOrAfter(today)) {
console.log('good to go!')
} else {
console.log('bad')
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js" integrity="sha256-ZsWP0vT+akWmvEMkNYgZrPHKU9Ke8nYBPC3dqONp1mY=" crossorigin="anonymous"></script>

MomentJS and JS Date objects not referring to the same hour

I've got a server instance (NodeJS) that receives a set of objects, and schedules them for sending push notifications to users.
Some of these objects, are periodic, and this periodicity is handled by a string like this:
90=>Mon&Tue&Thu=>16:00
Which is read as:
offset_minutes=>days_of_the_week=>initial_hour
Then, what I do is to check whether the current day matches one of the given days in the string, and then, modify the date to the given hour in the "initial_hour", and finally, substract the "offset_minutes" amount of minutes from the Date object.
Seems straightforward until now, right? Well, not that much. Let's first see the code:
const isToday = weekDays.split("&")
.map(a => {
switch (a) {
case 'Mon': return 1;
case 'Tue': return 2;
case 'Wed': return 3;
case 'Thu': return 4;
case 'Fri': return 5;
case 'Sat': return 6;
case 'Sun': return 7;
}
})
.some(v => v == currentDay);
if (isToday) {
let finalDate = moment(today)
.set("hour", Number(hour))
.set("minute", Number(mins));
if (offset) {
finalDate.subtract('minutes', Number(offset));
}
return finalDate.toDate();
Everything works well, until I do the MomentJS transformations. When I output a Date object with the ".toDate()" method, this object is always set to 2 hours before the expected time. But if I use the .toISOString() method, I get the proper time for all the occurrencies.
I guess that something is wrong with my Date objects, setting them up at a different timezone than the one I have. A couple of examples:
For the string 90=>Mon&Tue&Thu=>16:00 I get the Date object: 2019-10-14T14:00:11.852Z
For the string 30=>Mon&Tue&Wed&Thu&Fri&Sat&Sun=>18:30 I get the Date object: 2019-10-14T16:30:11.866Z
I would like to know what's the explanation for such a behavior, and if I can do something to change it so the normal Javascript Date object points to the same hour than my momentjs object, or the .toISOString() output.
Thank you!
The posted code is incomplete and doesn't demonstrate the issue described.
I've reimplemented the code without moment.js as best I can and simplified it. It seems to work fine:
function parseThing(s) {
// Parse input string
let b = s.split('=>');
let offset = +b[0];
let days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
let weekDays = b[1].split('&').map(day => days.indexOf(day));
let [hr, min] = b[2].split(':');
// Get a date for today
let date = new Date();
// If today included, return an adjusted date
if (weekDays.includes(date.getDay())) {
date.setHours(hr, min, 0, 0);
if (offset) {
date.setMinutes(date.getMinutes()+ Number(offset));
}
return date;
}
// If today isn't included, return null
return null;
}
let s0 = '90=>Mon&Tue&Thu=>16:00';
let s1 = '0=>Mon&Tue&Wed&Thu&Fri&Sat&Sun=>18:30';
console.log(parseThing(s0).toString());
console.log(parseThing(s1).toString());
Where the local day is one of those in the string (Mon, Tue, Thu) it returns a Date equivalent to a local time of 17:30, which is 90 minutes offset from 16:00, which seems to be correct.
PS I've changed Sunday to 0 as I can't see any rationale for it to be 7. Also seconds and milliseconds are zeroed too.

Trying to remove all the passed dates

I have an array with many dates, they are not in the date type but string like: "2016-08-12" for example. Then what I would like to do is to remove all dates that we already have passed. So therefor im trying to compare them to todays date and then remove it if its passed. Using typescript by the way.
my array, named datoArray, looks like this:
["2016-08-02", "2016-08-11", "2016-08-22", "2016-09-10"]
just with a lot more of the same...
then here's what I try to do:
for(var i = 0; i < this.datoArray.length; i++){
this.skoleAar = parseInt(this.datoArray[i].slice(0,4))
this.skoleMaaned = parseInt(this.datoArray[i].slice(5,8))
this.skoleDag = parseInt(this.datoArray[i].slice(8,10))
if(this.skoleAar < dagensAar){
this.datoArray.splice(i, 1);
}
if(this.skoleAar == dagensAar && this.skoleMaaned < dagensMaaned){
this.datoArray.splice(i, 1);
}
if(this.skoleAar == dagensAar && this.skoleMaaned == dagensMaaned && this.skoleDag < dagensDag){
this.datoArray.splice(i, 1);
}
}
the "dagensAar", "dagensMaaned" and "dagensDag" variables im getting from another function that works. If i "console.log" the variables it prints out int values like 2016 for the year and 8 for the month if i take from the start of the array, and for the "dagensAar", "dagensMaaned" and "dagensDag" it prints 2016 11 20, which is todays year, month and day. all is in Int type, so what im not getting here is why my "if" doesnt work? It seems like there is something wrong with the way i compare the, but i thought this was the way to compare int values?
If the dates are in ISO-8601 format then you can simply filter using Date.parse().
var dates = ["2016-08-02", "2016-08-11", "2016-08-22", "2016-09-10", "2016-12-15"];
function removePastDates(data) {
var today = new Date();
console.log('Initial state: ' + data);
var modified = dates.filter(function(dateString) {
return Date.parse(dateString) >= today;
});
console.log('Final state: ' + modified);
return modified;
}
var newDates = removePastDates(dates);
Your dates seem to be RFC compliant, meaning they can be directly fed into a new Date object. Simply compare to today and filter by that:
var today = new Date()
var futureDates = this.datoArray.filter(d => new Date(d) >= today)
(pre-ECMA6:)
var today = new Date()
var futureDates = this.datoArray.filter(function (d) {
return new Date(d) >= today;
})
I think the problem is not related to the dates.
I think the problem is that you are removing items from the array while looping the same exact array.
You should maybe try looping from the end of the array to the beginning or just save the indexes that you need to remove and later do the actual removing.
Keep in mind that when you remove an item you change the index of every item in the remaining of the array - maybe you should start removing from the greatest index so it will not confuse you.

Categories

Resources