I am trying to write a function that gets an array of dates (potentially unsorted, date_array) as input and determines the number of consecutive days that are in the array from a certain date (start_date) backwards, not including the start date.
Example:
start_date = 23.01.2023
date_array = [
01.01.2023,
02.01.2023,
20.01.2023, <- day 3 of the streak
21.01.2023, <- day 2 of the streak
22.01.2023, <- day 1 of the streak
22.01.2023, <- day 1 of the streak
23.01.2023,
24.01.2023]
Result:
streak_lenght: 3 days
The function I implemented is supposed to create a date range from the start date backwards and count, how many dates in the array are within the range. Every loop, the end date of the range goes one day further into the past. The loop continues as long as the number of dates within the range grow.
start date ... start date - 1 day
start date ... start date - days
However, for some reason the start date gets overwritten by the end date before the while loop starts...
I would greatly appreciate any help - or suggestions for better solutions to the problem. Thanks in advance!
const dateArray = [
new Date("2022-12-31"),
new Date("2023-01-02"), // day 3 of streak
new Date("2023-01-03"), // day 2 of streak
new Date("2023-01-03"), // day 2 of streak
new Date("2023-01-04"), // day 1 of streak
new Date("2023-01-05")];
const d_start = new Date("2023-01-05");
function currentStreak(dateArray, d_start) {
// dateArray: Array of dates on which event was executed
// d_start: start date of the streak, not included
// Create a range d_start ... d_end, d_end starts one day before d_start
let d_end = d_start
d_end.setDate(d_start.getDate() - 1)
let countPrev = -1
let count = 0
let streakCount = 0
// Count how many array elements are in the range and continue loop while this number increases
while (count > countPrev) {
countPrev = count
// count number of array elements between start and end date
count = dateArray.reduce((accumulator, currentValue) => {
if((d_start > currentValue) && (currentValue > d_end))
{
accumulator += 1
}
return accumulator
}, 0)
// set new end date for next iteration
d_end = d_end.setDate(d_end.getDate() - 1)
streakCount = streakCount+1
}
return count;
}
currentStreak(dateArray, d_start)
So here we say a day gap < 26Hrs.
Push all runs to the accumulator and then pop the result.
const dateArray = [
new Date("2022-12-31"),
new Date("2023-01-02"), // day 3 of streak
new Date("2023-01-03"), // day 2 of streak
new Date("2023-01-03"), // day 2 of streak
new Date("2023-01-04"), // day 1 of streak
new Date("2023-01-05")]
// 26Hrs is 9.36e+7;
const streak = dateArray.reduce((p,day,i,arr) => {
if ( arr[i+1]?.valueOf() == day.valueOf() ) return p;
if ( arr[i+1]?.valueOf() - day.valueOf() < 9.36e+7 ) {
p[p.length - 1].push(arr[i+1]);
} else {
p.push([])
}
return p;
},[]).sort((a,b) => a.length - b.length).pop()
console.log({ streak })
However, for some reason the start date gets overwritten by the end
date before the while loop starts...
That's what happens when you use setDate: it mutates the date.
Wouldn't a reducer be sufficient?
[edit based on comment] I have a small library to calculate the difference between 2 dates available. In the stackoverflow snippet this can not be loaded via the javascript panel, so here's a snippet using a script (type module) in the html panel, making use of that library.
<script type="module">
const diffCalc = (await
import("//kooiinc.github.io/DateDifferenceCalculator/index.js")).default();
const dateArray = [
new Date("2022-12-31"),
new Date("2023-01-02"), // day 3 of streak
new Date("2023-01-03"), // day 2 of streak
new Date("2023-01-03"), // day 2 of streak
new Date("2023-01-04"), // day 1 of streak
new Date("2023-01-05") ];
const streak = dateArray.reverse()
.reduce( (acc, d, i, self) => {
const diff = i && diffCalc(d, self[i-1]);
return diff && `${diff}`.startsWith(`1 day`)
? [...acc, d] : acc;
}, [] );
console.log(`Streak length: ${
streak.length}\nStreak values: ${
JSON.stringify(streak, null, 2)}`);
</script>
Related
I need to find number of days if i enter startDate and endDate.from the start_date to end_date i only want to retrieve weekdays i.e monday to friday along with the offcial_leave variable
for_example:
let numberOfdays;
let startDate = '2022-04-04'; //yy-mm-dd format
let endDate = '2022-04-08';
// Below variable come from db and vary according the start and endate
// eg:- 2022-12-25 will be holiday if we select start and end in december
let holidays = ['2022-04-05', '2022-04-07' ]
numberOfdays => 3
// I want to return number of days to 3
How can i achieve this in in JavaScript
thanks
First convert the startDate and endDate to javascript Date. Then, declare a variable i to store while looping through the date. Also, declare holidayIndex which stores the current index at which holiday date needs to be checked with the current date.
Inside the loop, convert the date to YYYY-MM-DD format (original format) to check if the current date (isoDate) lies between a holiday, i.e., it is not a holiday date. If the holidayIndex is at last index of array, then just check if the current date (isoDate) is not in the holidays array. If not found, then increment numberOfDays variable.
Otherwise, a holiday date is found, hence no need to increment numberOfDays. Just increment holidayIndex to be ready to match the upcoming dates for next holiday date.
Here is the solution:
let numberOfdays = 0;
const startDate = '2022-04-04'; //yy-mm-dd format
const endDate = '2022-04-08';
// Below variable come from db and vary according the start and endate
// eg:- 2022-12-25 will be holiday if we select start and end in december
const holidays = ['2022-04-05', '2022-04-07'];
let holidayIndex = 0;
const start = new Date(startDate);
const end = new Date(endDate);
let i = start;
while (i <= end) {
const isoDate = i.toISOString().split('T')[0];
if (
(holidayIndex < holidays.length - 1 && isoDate < holidays[holidayIndex] && isoDate > holidays[holidayIndex + 1]) ||
formattedDate !== holidays[holidayIndex]
) {
numberOfdays += 1;
} else {
holidayIndex += 1;
}
i.setDate(i.getDate() + 1);
}
I'm trying to create a method to calculate the total days off.
I have an array that contains the working days:
"workingDays": [1,2,3,4,5],
So I have two dates; a startDate and an endDate, and I should count in this range how many days aren't working days (i.e. total days off).
For example, I have a range from 03/15 (today) to 03/21, a total of 7 days.
Today (03/15) is day 2, and it is a working day so I don't have to increment a counter for days off, while for example 03/19 is not a working day (it is number 6) so I have to increment the day off variable.
I have tried to implement the code, but it doesn't work correctly:
const checkDate = (start, end) => {
const workingDays = [1,2,3,4,5]
let dayOff = 0
var currentDate = new Date(start)
while (currentDate <= end) {
workingDays.map((day) => {
console.log('current ', currentDate.getDay(), ' day ', day)
if (currentDate.getDay() !== day) {
dayOff += 1
}
currentDate = currentDate.addDays(1)
})
}
console.log('dayOff ', dayOff)
return dayOff
}
it prints me:
LOG current 2 day 1
LOG current 3 day 2
LOG current 4 day 3
LOG current 5 day 4
LOG current 6 day 5
LOG current 0 day 1
LOG current 1 day 2
LOG current 2 day 3
LOG current 3 day 4
LOG current 4 day 5
LOG dayOff 10
but it is wrong because the result should be 2.
How can I do? Thank you
EDIT.
The function that I use to add a day:
Date.prototype.addDays = function (days) {
var date = new Date(this.valueOf())
date.setDate(date.getDate() + days)
return date
}
currentDate = currentDate.addDays(1)
.addDays is not a correct function of Javascript. It's used in C#.
Below is the right code which will return correct answer:
const workingDays = [1,2,3,4,5]
const endDate = new Date(startDate)
let dayOff = []
var currentDate = new Date(endDate)
while (currentDate <= endDate) {
console.log('currentDate', currentDate)
let index = workingDays.indexOf(currentDate.getDay());
console.log('dayOff - index', index)
if(index == -1)
{
console.log('dayOffDate - addded', currentDate)
dayOff.push(currentDate)
}
currentDate = new Date(currentDate.setDate(currentDate.getDate() + 1))
}
The following small function will calculate the total days off between two (2) dates, given the first date and the end date.
It is simple and assumes the end date is the same or higher than the start date (does not check for reversing the dates).
function getTotalOffDays(start,end) {
const workingDays = [1,2,3,4,5];
let daysOff = 0, // days off counter
date = new Date(start), // starting date
endDate = new Date(end); // end date
while (date <= endDate) {
if (!workingDays.includes(date.getUTCDay())) daysOff += 1; // increment days off
date = new Date(date.setUTCDate(date.getUTCDate() + 1)); // go the next day
}
return daysOff;
}
console.log("Total days off: " + getTotalOffDays("2022-03-15", "2022-03-21"));
I need to display the current week of the month in the following format in react-native:
(Week 2: 05.10 - 11.10) (example of week 2 of current month)
What would be some suggestions as how to achieve this? I know that there are packages such as momentjs to build this but would like some examples of how to achieve this
any help is appreciated!
You can adapt the code below. I say "adapt" because you haven't specified when your week starts (Sunday or Monday?) or how you want to count which week within the month it is (i.e. is week #1 the first full week? The code below assumes so).
Anyway, by clicking the "Run Code Snippet" button, you'll see what it does, including some intermediate steps, which are there to illustrate where the values are coming from, and therefore what you might want to "adapt" for your needs.
//get the first day of week and last day of week, borrowed from https://stackoverflow.com/a/64529257/1024832 above
const getWeek = (date = new Date()) => {
const dayIndex = date.getDay();
const diffToLastMonday = (dayIndex !== 0) ? dayIndex - 1 : 6;
const dateOfMonday = new Date(date.setDate(date.getDate() - diffToLastMonday));
const dateOfSunday = new Date(date.setDate(dateOfMonday.getDate() + 6));
return [dateOfMonday, dateOfSunday];
}
//get week number w/in the month, adapted from https://stackoverflow.com/a/57120367/1024832
const getWeekNumber = () => {
let todaysDate = moment(moment.now());
let endOfLastMonth = moment(todaysDate).startOf('month').subtract(1, 'week');
let weekOfMonth = todaysDate.diff(endOfLastMonth, 'weeks');
return weekOfMonth;
}
//capture/log some steps along the way
const [Monday, Sunday] = getWeek();
console.log("First/Last of week as Date Objects: ", Monday, Sunday);
let Monday_formatted = moment(Monday).format("DD.MM");
let Sunday_formatted = moment(Sunday).format("DD.MM");
console.log(Monday_formatted, "-", Sunday_formatted);
console.log("Week #:", getWeekNumber());
//set the DIV content
document.getElementById("datehere").innerText = `(Week ${getWeekNumber()}): ${Monday_formatted} - ${Sunday_formatted}`;
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<div id="datehere"></div>
Here's an answer as a function which returns the current week's Monday and Sunday in an array:
getWeek = (date = new Date()) => {
const dayIndex = date.getDay();
const diffToLastMonday = (dayIndex !== 0) ? dayIndex - 1 : 6;
const dateOfMonday = new Date(date.setDate(date.getDate() - diffToLastMonday));
const dateOfSunday = new Date(date.setDate(dateOfMonday.getDate() + 6));
return [dateOfMonday, dateOfSunday];
}
const [Monday, Sunday] = getWeek();
console.log(Monday, Sunday);
The response is two valid date objects. You can also pass a date object for the function to get Monday and Sunday of that date's week (e.g. getWeek(new Date(0));
But when you want to parse those dates, you should gain better knowledge of Date Object.
Im building a mini calendar that just displays the current month, I have figured out how to map out the calendar, here is the code:
Code:
var month = moment(),
index = 0,
maxDay = month.daysInMonth(),
start = month.startOf("month"),
offset = (start.isoWeekday() - 1 + 7) % 7; // start from monday
var week = []; // holds the weeks
var days = []; // holds the days
do {
var dayIndex = index - offset;
if(dayIndex >= 0 && dayIndex < maxDay){
days.push({
number: dayIndex + 1,
isPast: null, // stuck here boolean
isToday: null // stuck here boolean
})
}
if(index % 7 === 6){
week.push(days);
console.log(week);
days = [];
if (dayIndex + 1 >= maxDay) {
break;
}
}
index += 1;
} while(true);
This works fine, the only issue Im having is to figure out if the day is today or its in the past?
the code is here also: https://jsfiddle.net/chghb3Lq/3/
Moment has isBefore, isAfter and isSame functions to compare moments and as the docs says:
If you want to limit the granularity to a unit other than milliseconds, pass the units as the second parameter.
There are a couple of things in your code that you can achieve in a simple way using momentjs instead of reimplementing by yourself:
To loop from the first day of the month until the last day you can use:
startOf('month') and endOf('month') as limit of the loop
add(1, 'day') to increment loop index
isBefore as loop condition
Use date() to get date of the month (1-31)
Use day() to get day of the week (0 => Sunday, ... 6 => Saturday); or weekday() to get day of the week locale aware.
Using these suggestions your code could be like the following:
var day = moment().startOf('month');
var endOfMonth = moment().endOf('month');
var week = [];
var month = [];
while( day.isBefore(endOfMonth) ){
week.push({
number: day.date(),
isPast: moment().isAfter(day, 'day'),
isToday: moment().isSame(day, 'day')
});
if( day.day() === 0 ){
month.push(week);
week = [];
}
day.add(1, 'day');
}
console.log(month);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
Use moment methods like isSame() , isBefore(), isSameOrBefore() etc.
They each allow setting comparison units like year month week day hour minute second
See Query Section of moment docs
I want to create date ranges from statics dates of array.
Here is an example of what I want:
var collDates = [
"2017/01/01",
"2017/01/02",
"2017/01/03",
"2017/01/04",
"2017/01/08",
"2017/01/09"
];
This should be transformed to:
[
{ start_date: "2017/01/01", end_date: "2017/01/04" },
{ start_date: "2017/01/08", end_date: "2017/01/09" }
];
I have resolved the issue and here is an example code.
var staticDates = ["2017/01/01", "2017/01/02", "2017/01/03", "2017/01/04", "2017/01/08", "2017/01/09", "2017/01/10", "2017/01/11", "2017/01/12", "2017/01/13", "2017/01/14", "2017/01/15", "2017/01/16", "2017/01/17", "2017/01/18", "2017/01/19", "2017/01/20", "2017/01/21", "2017/01/22", "2017/01/23", "2017/01/24", "2017/01/25", "2017/01/26", "2017/01/27", "2017/01/28", "2017/01/29"];
var coll_dateIntervals = [];
var arr_temp = [];
var i = 1;
$.each(staticDates, function(index, moment_date){
//Day difference in # of days
var diff = Math.abs(moment(staticDates[index]).diff(staticDates[index + 1], "days"));
arr_temp.push(moment_date);
//Check the date difference in days.
if(diff <= 1 && diff !== undefined){
//If the difference is 1, than add the date to the temporary array
arr_temp.push(moment_date);
//If it's more than 1 day, or the last object
} else if (diff > 1 || diff === undefined){
//Store the interval in an object
console.log(arr_temp[arr_temp.length - 1]);
var obj_dateInterval = { start_date: moment(arr_temp[0], "YYYY/MM/DD").format('YYYY-MM-DD'), end_date: moment(arr_temp[arr_temp.length - 1], "YYYY/MM/DD").format('YYYY-MM-DD')};
coll_dateIntervals.push(obj_dateInterval);
//Empty the array to start the new loop
arr_temp = [];
}
});
console.log(coll_dateIntervals);
Without a library, you just need a couple of simple functions for parsing and formatting. Doesn't do anything with invalid dates, but not difficult to add if required.
Step through the dates to create ranges. Begin with a start date, calculate the next day as current date plus one. When get a date that isn't the next day, end the range and start a new one. Also end when run out of dates.
For single dates, creates a single date range.
/* Parse date in format y/m/d
** #param {string} s - string to parse
** #returns {Date}
*/
function parseYMD(s) {
var b = s.split(/\D/);
return new Date(b[0], b[1] - 1, b[2])
}
/* Collate array of date strings in y/m/d format to ranges
** #param {Array} dateArray - array of dates to parse in y/m/d format
** #returns {Array} ranges in format {start_date: yyyy/mm/dd, end_date: yyyy/mm/dd}
*/
function datesToRanges(dateArray) {
return dateArray.reduce(function(acc, currentDate, index) {
var d = parseYMD(currentDate);
// Start of a processing, initialise accumulator
if (!acc) {
acc = {range: {start_date: currentDate}, ranges: []};
// If not continuing range, end current range, store and start new
} else if (acc.previousDate.setDate(acc.previousDate.getDate() + 1) != +d) {
acc.range.end_date = acc.previousString;
acc.ranges.push(acc.range);
acc.range = {start_date: currentDate};
}
// Keep current values for next iteration
acc.previousDate = d;
acc.previousString = currentDate;
// If at end of dates, end current range
if (index == dateArray.length - 1) {
acc.range.end_date = currentDate;
acc.ranges.push(acc.range);
}
return acc;
}, null).ranges;
}
// Tests
var collDates = [
"2017/01/01", // Start 4 day range
"2017/01/02",
"2017/01/03",
"2017/01/04", // End 4 day range
"2017/01/06", // Zero day range
"2017/01/08", // Start one day range
"2017/01/09", // End one day range
"2017/01/15", // Zero day range
];
console.log(datesToRanges(collDates));
PS
Can't see what this has to do with jQuery. There's no moment.js tag so I haven't used it. ;-)