I have an array:
dates = [
"2019-10-04T12:38:02.506204+00:00",
"2019-10-04T14:59:39.370487+00:00",
"2019-10-04T14:59:50.897597+00:00",
"2019-10-04T14:59:57.344401+00:00",
"2019-10-04T15:00:04.631711+00:00",
"2019-10-04T15:00:13.460667+00:00",
"2019-10-04T15:00:21.672496+00:00",
"2019-10-04T15:00:29.643194+00:00",
"2019-10-04T15:00:37.022307+00:00",
"2019-10-04T15:01:00.134239+00:00",
"2019-10-04T15:01:08.146224+00:00",
"2019-10-04T15:01:15.205999+00:00",
"2019-10-04T15:01:21.877861+00:00",
"2019-10-04T15:01:28.089368+00:00",
"2019-10-04T16:29:05.438103+00:00",
"2019-10-04T16:29:49.927139+00:00",
"2019-10-05T16:35:00.994699+00:00",
"2019-10-06T08:45:47.289637+00:00",
"2019-10-06T08:46:11.294362+00:00",
"2019-10-06T08:46:23.702585+00:00",
"2019-10-06T08:46:33.374726+00:00",
"2019-10-06T08:46:42.726666+00:00",
"2019-10-06T08:46:52.916887+00:00",
"2019-10-06T08:47:02.659273+00:00",
"2019-10-06T16:08:14.975139+00:00",
"2019-10-06T16:08:29.047735+00:00",
"2019-10-06T16:08:42.831487+00:00",
"2019-10-06T16:08:58.472631+00:00",
"2019-10-07T08:32:43.248706+00:00",
"2019-10-07T08:37:00.512249+00:00",
"2019-10-07T08:38:12.188744+00:00",
"2019-10-07T08:40:08.362515+00:00",
"2019-10-07T08:41:57.151322+00:00",
"2019-10-07T08:46:52.107963+00:00",
"2019-10-07T08:53:38.604697+00:00",
"2019-10-07T08:53:53.449463+00:00",
"2019-10-07T09:15:31.659223+00:00"
]
I need to make a switch with two buttons, the first will switch the dates forward, the second - back.
Dates are used from an array.
Help with JS please.
You can use the isBefore method on Moment.js objects. If you pass in the second argument as "day", then it would consider that:
"2019-10-04T12:38:02.506204+00:00" (4th of October) is before "2019-10-05T16:35:00.994699+00:00" (5th of October)
"2019-10-04T12:38:02.506204+00:00" (4th of October) is not before "2019-10-04T14:59:39.370487+00:00" (4th of October)
let dates = [
"2019-10-04T12:38:02.506204+00:00",
"2019-10-04T14:59:39.370487+00:00",
"2019-10-04T14:59:50.897597+00:00",
"2019-10-04T14:59:57.344401+00:00",
"2019-10-04T15:00:04.631711+00:00",
"2019-10-04T15:00:13.460667+00:00",
"2019-10-04T15:00:21.672496+00:00",
"2019-10-04T15:00:29.643194+00:00",
"2019-10-04T15:00:37.022307+00:00",
"2019-10-04T15:01:00.134239+00:00",
"2019-10-04T15:01:08.146224+00:00",
"2019-10-04T15:01:15.205999+00:00",
"2019-10-04T15:01:21.877861+00:00",
"2019-10-04T15:01:28.089368+00:00",
"2019-10-04T16:29:05.438103+00:00",
"2019-10-04T16:29:49.927139+00:00",
"2019-10-05T16:35:00.994699+00:00",
"2019-10-06T08:45:47.289637+00:00",
"2019-10-06T08:46:11.294362+00:00",
"2019-10-06T08:46:23.702585+00:00",
"2019-10-06T08:46:33.374726+00:00",
"2019-10-06T08:46:42.726666+00:00",
"2019-10-06T08:46:52.916887+00:00",
"2019-10-06T08:47:02.659273+00:00",
"2019-10-06T16:08:14.975139+00:00",
"2019-10-06T16:08:29.047735+00:00",
"2019-10-06T16:08:42.831487+00:00",
"2019-10-06T16:08:58.472631+00:00",
"2019-10-07T08:32:43.248706+00:00",
"2019-10-07T08:37:00.512249+00:00",
"2019-10-07T08:38:12.188744+00:00",
"2019-10-07T08:40:08.362515+00:00",
"2019-10-07T08:41:57.151322+00:00",
"2019-10-07T08:46:52.107963+00:00",
"2019-10-07T08:53:38.604697+00:00",
"2019-10-07T08:53:53.449463+00:00",
"2019-10-07T09:15:31.659223+00:00"
]
let currentDate = moment(dates[0]); //pick a spot for the "current time"
//sort the dates to ensure they are sequential
dates.sort();
let nextDate = dates.
filter(isoDate => currentDate.isBefore(isoDate, "day")) //filter anything before the current
[0]; //take the first (lowest) date
console.log(nextDate);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
Related
Assume I have two time values as startTime and endTime.
I want to find the exact mid value of this time objects.
Example: if startTime is 10:30 and endTime is 11:30, I need 11:00 as the midpoint value.
How to solve this using momentjs in JavaScript?
The middle between two dates is just half of the difference between the dates added to the smaller one.
The difference should be pretty straight forward:
Math.abs(moment(a).diff(b))
Math.abs() removes the minus from negative numbers (aka if a < b).
Calculating the middle has two steps too, first divide the difference by two, then add it to the smaller date (Math.min()):
diff/2+Math.min(moment(a).valueOf(),moment(b).valueOf())
moment(n).valueOf() turns your Date strings into comparable integers.
function middleDate(a,b) {
let diff = Math.abs(moment(a).diff(b))
let middle = diff/2+Math.min(moment(a).valueOf(),moment(b).valueOf())
return moment(middle)
}
console.log(middleDate("2022-01-27T10:30:00Z", "2022-01-27T11:30:00Z"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
If you only want the hours and minutes, you can just format the returned moment object (and use moment.utc() to remove the timezone):
function middleDate(a,b) {
let diff = Math.abs(moment(a).diff(b))
let middle = diff/2+Math.min(moment(a).valueOf(),moment(b).valueOf())
return moment(middle)
}
console.log(
moment.utc(middleDate("2022-01-27T10:30:00Z", "2022-01-27T11:30:00Z")).format("hh:mm")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
Here is how i solved it
(For my particular case that deals with time only. For more generic cases, you can use the answer above)
import moment from 'moment';
export const middleDate = (startTime, endTime) => {
let duration = moment.duration(moment(endTime, 'HH:mm:ss').diff(moment(startTime, 'HH:mm:ss'))); //get Total Interval
let midInterval = duration.asHours() / 2; // find half of the interval
return moment(moment(startTime, 'HH:mm:ss')).add(midInterval, 'hour').format('HH:mm'); //add the mid interval to the startTime we have
};
I want to divide a given date range into months. And I want to separate these months according to weeks. I want to get the date from the start date to the end of the month at 7-day intervals. For example: (Like 15/12/2020, 22/12/2020, 29/12/2020 -newMonth- 05/01/2020)
It is difficult to understand with this explanation. After all I want to create a json like this.
Starting Date: 15/11/2020 - End Date: 20/01/2021
[
{
"dates": [
15, // Starting date(15/11/2020)
22, // 1 week later
29 // 1 week later
],
"firstDay": "15/11/2020"
},
{
"dates": [
6,
13,
20,
27
],
"firstDay": "06/12/2020"
},
{
"dates": [
3,
10,
17
],
"firstDay": "03/01/2021"
}
]
There are a few tricks that you can use to create the array structure you want:
Use a while loop, which keeps a current date that is constantly incremented by 7 days, to be compared against the end date. The current date should be initialized using the start date in the beginning of the loop.
To perform the "group by month" in your desired structure, you will need to using an object (which serves as a dictionary). The object will use the month + year combination as key, so that if your date ranges span over multiple years you won't collapse months from different years into the same entry.
For each iteration of the while loop, you construct the key from the current date, and then check if the key exists in your dictionary. If not, then we can push this object into your dictionary:
{
dates: [currentDate],
firstDate: currentDate
}
If it already exists, then you simply push the date into the dates array of the sub-object.
Finally, to convert your dictionary into the array strcuture you wanted, using ES6 Object.values() will work. See proof-of-concept below:
function generateRanges(startDate, endDate) {
let current = moment(startDate, 'DD/MM/YYYY');
const end = moment(endDate, 'DD/MM/YYYY');
// A dictionary to track unique month+year combinations
const daysByMonth = {};
while (current < end) {
// Construct key based on month+year
const key = `${current.month()}${current.year()}`;
const date = current.date();
// If key already exists, then we push into the `dates` array
if (key in daysByMonth) {
daysByMonth[key].dates.push(date);
}
// Otherwise we construct a brand new sub-object
else {
daysByMonth[key] = {
dates: [date],
// Since this is the first time we encounter the key,
// We can assume this is the earliest/first date of the month
firstDate: current.format('DD/MM/YYYY')
}
}
// At the end of the while loop, increment by a week, rinse and repeat
current.add(7, 'days');
}
// Once done, we only want the values in the dictionary
// We don't need to keep the unique month+year key
return Object.values(daysByMonth);
}
const range = generateRanges('15/11/2020', '20/01/2021');
console.log(range);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
The Moment.JS has an API for adding days to a date: the add function.
In order to build your final object, you would start from your startDate, and add 7 days for each loop iteration until reaching the end date. Like this:
let currentDate = moment(startDate);
while(currentDate < endDate) { // Make sure endDate is also a moment object
// Add the current date - you will want to adapt this
dates.push(currentDate.clone()); // Since .add() will mutate the currentDate object, clone the object when adding it to the array so that it's not affected by the add()
// Prepare for the next iteration
currentDate.add({days: 7});
}
As for month changes, remember the last month for every iteration (use month) and create a new object in your final array, then add dates to object instead of the previous one.
Side note: Months are zero-indexed in Javascript, which is not the case for the month day (date)
When dealing with date or datetime is always a good practice to use milliseconds. Also it is space efficient, a long integer = 4kb instead of a Date() object which is 32bytes.
In your case it is very straightforward to convert our dates to milliseconds, find how many weeks are between as integer and then run a loop increment by this iteration.
let from = new Date('2020/11/15').getTime();
let to = new Date('2021/01/20').getTime();
let week = 604800000;
let day = 86400000;
let allWeeks = [];
let current =0;
let weeks = (to-from)/day/7
for (i=0; i<weeks; i++){
allWeeks.push(new Date(from += week).toLocaleDateString())
}
console.log(JSON.stringify(allWeeks))
//["22/11/2020","29/11/2020","06/12/2020","13/12/2020","20/12/2020","27/12/2020","03/01/2021","10/01/2021","17/01/2021","24/01/2021"]
Finally you will have a list applicable for JSON to build any logic you prefer.
I hope to pin point a differed approach to your case!
I'm trying to get the list of k closest dates from a day of the week but I don't know how. Can someone please give me a hint or suggestion? Thank you.
For example:
getDates(k, day) { .... } // k is number of dates (1,2,3 etc), day is day of the week ("monday", "tuesday" etc)
// today is 05/19/2020
getDates(1, "monday") // output: [05/25/2020]
getDates(2, "monday") // output: [05/25/2020, 06/01/2020]
I'm convinced that you have already figured out how to solve this question from all the comments. So I'll just post my answer to give my personal approach to this problem.
I'm pretty sure that it's very difficult to solve this without using:
1: day to number converter (sunday => 0, monday => 1, and so on...
This is because Date.prototype.getDay returns a number, not in the
word form)
and
2: date addition function (because there are couple of date addition
that we can't avoid (one for deciding the nearest day of the week, and another for creating new dates for the output array), it's better to just make it a function.)
My approach is (although not fully optimized):
1: First, convert the input day to integer form. This can be done in
few ways, but I simply created an array of dates, and used the
indexOf method to get the day number. But there is another neat way,
which is flexible to any region you live (application of this SOF link).
2: Then, get the nearest date from the current time with day day
(the while loop in the middle). I think it's very easy to read, but we
can use a one liner here. Which will look something like new_day.setDate(new_day.getDate() + (day_num - first_day.getDay() +
(7 * (new_day.getDay() > day_num))));. But I personally feel this is a
bit nasty, so I prefer the loop method.
3: Finally, create the array of dates. In my code, I first created the
empty array of k items (without fill(), the array will be [empty,
empty...] and will be not iterable), then map them to corresponding
dates from calculation.
There is actually a downfall to my method though. When I'm converting
the date object to the form MM/DD/YYYY, i'm using the
toLocaleDateString method. But apparently, using
toLocaleDateString() is risky (see the comment to this SOF
answer), which I'm not really sure why, but if so that would be a
problem.
Also, the output is 5/25/2020 while your expected output is
05/25/2020. It depends on your expectation, but it might be a
problem.
function addDays(date, day) {
let new_date = new Date(date.getTime());
new_date.setDate(new_date.getDate() + day);
return new_date;
}
function getDates(k, day) {
const day_num = ['sunday', 'monday', 'tuesday', 'wednesday' ,'thursday' ,'friday', 'saturday'].indexOf(day.toLowerCase()) // converting day name to number
let new_day = new Date();
while (new_day.getDay() !== day_num) {
new_day = addDays(new_day, 1)
}
return Array(k).fill().map((_, index) => addDays(new_day, index * 7).toLocaleDateString() )
}
// today is 05/19/2020
console.log(getDates(1, "monday")) // output: [05/25/2020]
console.log(getDates(2, "monday")) // output: [05/25/2020, 06/01/2020]
console.log(getDates(10, "monday"))
console.log(getDates(20, "monday")) // also for k > 10
Hope that helped, cheers! :)
As comments suggest, you can move the supplied date to the next instance of the required day, then keep adding 7 days until you get the number of dates you want.
In the following function, if start day is say Monday and the current day is also Monday, then the first date in the results array is the current day. If you want it to be the following week, change:
dateDay > idx?
to
dateDay >= idx?
I think the function should return an array of Dates, then the caller can format them as required.
/**
* Get array of count date stings from next dayName starting on or after today or d
* #param {number} count: number of dates to get (1,2,3 etc.)
* #param {string} dayName: name of day of week, min first 2 letters, any case ("Mon", "TU","we" etc)
* #param {Date} [d]: starting date
* #returns {string[]} Array of k date strings starting with next day, each +7 days from the previous date
*/
function getDates(count, dayName, d = new Date()) {
// Don't modify supplied date
d = new Date(+d);
// Get supplied day name as JS day index
let idx = ['su','mo','tu','we','th','fr','sa'].indexOf(dayName.toLowerCase().slice(0,2));
// Get index of d's day
let dayNum = d.getDay();
// Set to next instance of day, or today if current day == day
d.setDate(d.getDate() + idx - dayNum + (dayNum > idx? 7 : 0));
// Fill result array with required date strings
let result = [];
while (count-- > 0) result.push(d.toDateString()) && d.setDate(d.getDate() + 7);
return result;
}
// Examples
[[1, "Monday"],
[2, "TUES"],
[3, 'wed'],
[1, 'th'],
[3, 'fr'],
[2, 'sa'],
[1, 'su'],
[-20, 'fri'] // -ve count returns empty array
].forEach(args => console.log(getDates(...args)));
I have an array of ISO 8601 dates intervals (start and end date). how do you only select the dates before slash with javascript (start dates)?
var dates = [
2019-12-31T23:45:00.000-03:00/2020-01-01T10:30:00.000+06:00,
2020-01-01T07:15:00.000+07:00/2019-12-31T16:00:00.000-10:00
]
I would like the result to be
[2019-12-31T23:45:00.000-03:00,2020-01-01T07:15:00.000+07:00]
I tried this string-replace method,
let result = dates.replace(/\/.*/g, '');
but it replaces everything after the first bracket.
The variables dates is an array, so you should iterate on this.
var dates = [
'2019-12-31T23:45:00.000-03:00/2020-01-01T10:30:00.000+06:00',
'2020-01-01T07:15:00.000+07:00/2019-12-31T16:00:00.000-10:00'
]
// [2019-12-31T23:45:00.000-03:00,2020-01-01T07:15:00.000+07:00]
let result = dates.map(date => date.split('/')[0]);
console.log(result);
But be carefull, in your question, your dates variable is not an array of type string. Maybe it's an array of type Date...
I prefer to String.prototype.split() instead of replace. Make an iteration over the array using map(), in every cycle of the iteration split the element (start date / end date) and capture the start date as you expected.
var dates = [
'2019-12-31T23:45:00.000-03:00/2020-01-01T10:30:00.000+06:00',
'2020-01-01T07:15:00.000+07:00/2019-12-31T16:00:00.000-10:00'
]
let results = dates.map(i => i.split('/')[0])
console.log(results)
Output:
[
"2019-12-31T23:45:00.000-03:00",
"2020-01-01T07:15:00.000+07:00"
]
In node.js I need to get the date within the date range of particular gap.
Consider two dates:
startDate:2016-07-10T00:00:00.000Z,
payByDate:"13"(13th of every month);
endDate:2016-10-08T00:00:00.000Z
I need an array of dates of monthly or weekly gap between these 2 dates.
My result Should be:(Monthly gap)
[2016-07-13T00:00:00.000Z,
2016-08-13T00:00:00.000Z,
2016-09-13T00:00:00.000Z]
EDIT:
My startdate is 2016-07-10T00:00:00.000Z , so i can calculate the noOfmonths using endDate-startdate, but the array entry should be by payByDate.
I am using MOMENT.JS, but its returning only noOfMonths not the dates. Please share your ideas. Thanks in advance.
Now is it possible to calculate the calculate the Above array.Please note that "payByDate" is string.
you can use getMonth() function to get the month of your date. then keep adding one to it till you reach the end date.
var arrayDates = [];
arrayDates.push(startDate);
var tempDate = new Date(new Date(startDate).setMonth(startDate.getMonth()+1));
while(tempDate < endDate)
{
arrayDates.push(tempDate);
var tempDate = new Date(new Date(tempDate).setMonth(tempDate.getMonth()+1));
}
Abouve logic will work for monthly gap. to get array of weekly gap, use getDate() function and keep adding 7 to it till you reach the end Date
Try using the manipulate method in moment.js to add a month to the starting date until you reach the second date.
Consider this code. Assuming that the first date and the last date are already moments when they are passed into this function, you can use the diff and manipulate methods to create an array of dates by month in between the two different dates:
EDIT:
function monthsBetween(payByDate, startDate, lastDate) {
var months = [];
//finds nearest date to the start date that is on the payByDate
var currentDate = startDate.date(parseInt(payByDate));
//checks if the date is after the startDate and adds a month if it is
if(!currentDate.isSameOrAfter(startDate)){
currentDate = currentDate.add(1, 'months');
}
while (currentDate.isSameOrBefore(lastDate)) {
months.push(currentDate);
currentDate = currentDate.add(1, 'months');
}
return months;
}