Fill in empty blocks for each calendar month JS - javascript

I am currently able to fetch the given days of the current month as well as previous and future months using JS. What I would like to achieve, is if say December starts on a Tuesday I would like to pass in empty objects for Sunday and Monday. December also ends on a Thursday, so I would like to pass in empty objects for Friday and Saturday.
The code I am currently using to fetch each calendar month and display them is as follows:
import React, { useEffect, useState, useCallback } from "react";
import "./styles.css";
export default function App() {
const [visibleMonth, setVisibleMonth] = useState(new Date().getMonth());
const [calData, setCalData] = useState(null);
const [dates, setDates] = useState(null);
const getDaysArray = async (s, e) => {
let a = [];
for (let d = new Date(s); d <= e; d.setDate(d.getDate() + 1)) {
a.push(new Date(d).toString());
}
return a;
};
const currentMonth = useCallback(async () => {
let d = new Date();
d.setMonth(visibleMonth);
let firstDay = new Date(d.getFullYear(), d.getMonth(), 1);
let lastDay = new Date(firstDay.getFullYear(), firstDay.getMonth() + 1, 0);
let calendarMonth = d.toLocaleString("en-us", {
month: "long",
year: "numeric"
});
setCalData(calendarMonth);
const dates = await getDaysArray(firstDay, lastDay);
setDates(dates);
}, [visibleMonth]);
useEffect(() => {
currentMonth();
}, [currentMonth]);
const prevMonth = async () => {
let d = new Date();
d.setMonth(visibleMonth - 1);
setVisibleMonth((state) => visibleMonth - 1);
let firstDay = new Date(d.getFullYear(), d.getMonth(), 1);
let lastDay = new Date(firstDay.getFullYear(), firstDay.getMonth() + 1, 0);
let calendarMonth = d.toLocaleString("en-us", {
month: "long",
year: "numeric"
});
setCalData(calendarMonth);
const dates = await getDaysArray(firstDay, lastDay);
setDates(dates);
};
const nextMonth = async () => {
let d = new Date();
d.setMonth(visibleMonth + 1);
setVisibleMonth((state) => visibleMonth + 1);
let firstDay = new Date(d.getFullYear(), d.getMonth(), 1);
let lastDay = new Date(firstDay.getFullYear(), firstDay.getMonth() + 1, 0);
let calendarMonth = d.toLocaleString("en-us", {
month: "long",
year: "numeric"
});
setCalData(calendarMonth);
const dates = await getDaysArray(firstDay, lastDay);
setDates(dates);
};
return (
<div className="App">
<h1>{calData}</h1>
<button onClick={prevMonth}>Prev Month</button>
<button onClick={nextMonth}>Next Month</button>
{dates &&
dates.map((item, i) => {
return <div key={i}>{item}</div>;
})}
</div>
);
}
the reason I am attempting to do so is due to the grid I have created. As it stands now, every month starts at sunday and I am not successfully pairing the correct days of the month with the days on the calendar. For example december starts on a tuesday, yet my styling shows it starts on a sunday any help would be greatly appreciated. the console logs currently show the days each month should start and end on.
Attached is a code pen for debugging! https://codesandbox.io/s/heuristic-visvesvaraya-r9lcw?file=/src/App.js

Based upon your most recent comments and updates, I recommend the following changes to the getDaysArray() method:
Create the d date outside the loop in order to use getDay() (day of week #)
Fill the a[] with empty strings with the number of day of week # in a for loop
Finally, populate the date strings into the remainder of the a[] array.
That should do it:
const getDaysArray = async (s, e) => {
let a = [];
let d = new Date(s);
let emptyCount = d.getDay();
for(let i = 0; i < emptyCount; i++) {
a.push('');
}
for (d; d <= e; d.setDate(d.getDate() + 1)) {
a.push(new Date(d).toString());
}
return a;
};

Related

Getting date range based on date and hour

I am trying to get individual dates ("2022-10-10") and hours ("2022-10-10T09") between an interval in UTC. I could get the individual dates by the following -
function getDatesInRange(startDate, endDate) {
const date = new Date(startDate.getTime());
const dates = [];
while (date <= endDate) {
const day = new Date(date).toISOString().split(':')[0].split('T')[0];
dates.push(day);
date.setDate(date.getDate() + 1);
}
return dates;
}
console.log(getDatesInRange(new Date('2022-10-10T20:50:59.938Z'), new Date('2022-10-15T23:50:59.938Z')));
Hence, the above returns - ["2022-10-10", "2022-10-11", "2022-10-12", "2022-10-13", "2022-10-14", "2022-10-15"]
I also want to return the hours of the start and end date and the rest should be dates. So i want to get in return - ["2022-10-10T20", "2022-10-10T21", "2022-10-10T22", "2022-10-10T23" "2022-10-11", "2022-10-12", "2022-10-13", "2022-10-14", "2022-10-15T00", "2022-10-15T01"]
Here is what i have as of now -
function getHoursInRange(startDate, endDate) {
let startDatePlusOne = new Date(startDate);
startDatePlusOne.setDate(startDatePlusOne.getDate() + 1);
let endDateMinusOne = new Date(endDate);
endDateMinusOne.setDate(endDateMinusOne.getDate() - 1);
const date = new Date(startDate.getTime());
console.log("Start date :", date);
let dates = getDatesInRange(startDatePlusOne, endDateMinusOne);
console.log("Only days : ", dates);
startDatePlusOne.setHours(0);
while (date < startDatePlusOne) {
const day = new Date(date).toISOString().split(':')[0];
dates.push(day);
date.setHours(date.getHours() + 1);
}
endDateMinusOne.setHours(23);
const edate = endDateMinusOne.getTime();
while (edate < endDate) {
const day = new Date(edate).toISOString().split(':')[0];
dates.push(day);
date.setHours(date.getHours() + 1);
}
return dates
}
For this use case, i am getting the days back excluding the start and end dates. But for getting each hour of start and end date it gets stuck somehow. Somehow i feel there is a better way to do this. Any ideas ?
You can do it a simpler way by incrementing the timestamp by 30 minutes at a time, and keeping a note of all non-duplicate hour strings and date strings:
function getDatesInRange(startDate, endDate) {
let h = new Set(), d = new Set(), t = [];
for(let i=startDate.getTime(); i<endDate.getTime(); i+=1000*1800) t.push(i);
[...t, endDate.getTime()].forEach(i=>{
let s = new Date(i).toISOString();
[[s.split(':')[0], h], [s.split('T')[0], d]].forEach(([s,r])=>r.add(s));
});
let firstDate = [...d.values()][0], lastDate = [...d.values()].pop();
return d.size===1 ? [...h.values()] : [
...[...h.values()].filter(v=>v.startsWith(firstDate)),
...[...d.values()].filter(v=>v!==firstDate && v!==lastDate),
...[...h.values()].filter(v=>v.startsWith(lastDate))];
}
console.log(getDatesInRange(
new Date('2022-10-10T20:50:59.938Z'), new Date('2022-10-15T23:50:59.938Z')));
dateRange constructs an array of Date objects corresponding to the supplied range, inclusive.
dayToString takes a date and creates an array of strings, one for each hour of the day between the specified UTC hour range, inclusive.
dateRangeToStrings accepts an array of dates and constructs an array of strings according to the rules laid-out in the question.
const twoDigit = (n) => String(n).padStart(2, '0')
const toISODateString = (date) => `${date.getUTCFullYear()}-${twoDigit(date.getUTCMonth() + 1)}-${twoDigit(date.getUTCDate())}`
const dateRange = (start, end, curr = new Date(start)) => {
const dates = []
while (curr <= end) {
dates.push(new Date(Date.UTC(curr.getUTCFullYear(), curr.getUTCMonth(), curr.getUTCDate())))
curr.setUTCDate(curr.getUTCDate() + 1)
}
return dates
}
const dayToString = (date, startUTCHour = 0, endUTCHour = 23) =>
Object.keys([...Array(24)])
.slice(startUTCHour, endUTCHour + 1)
.map((h)=>`${toISODateString(date)}T${twoDigit(h)}`)
const dateRangeToStrings = (arr, startUTCHour, endUTCHour) => {
const beginning = dayToString(arr[0], startUTCHour)
const middle = arr.slice(1, -1).map(toISODateString)
const end = dayToString(arr[arr.length - 1], 0, endUTCHour)
return beginning.concat(middle, end)
}
const getDatesInRange = (start, end) =>
dateRangeToStrings(dateRange(start, end),
start.getUTCHours(),
end.getUTCHours())
console.log(getDatesInRange(new Date('2022-10-10T20:50:59.938Z'),
new Date('2022-10-15T23:50:59.938Z')))

moving forward and backward between each month

I currently am working on an application where I am attempting to create a calendar using javascript. Based on the current date, I can select a back button and go back as many months as I would like and fetch the days within each month. I can also select a forward button and go forward as many months as I would like. The issue I am running into is that if I go back to say October 2020, and hit the forward button, the calendar will start on January 2021 because I am cycling months based on the current date rather than the dates I am currently on. What I would like to achieve is if I am in October 2020 and hit the forward button, I like it to go to November 2020. Any and all suggestions are appreciated.
my code is as follows:
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [count, setCount] = useState(1);
const [countTwo, setCountTwo] = useState(1);
const [dates, setDates]=useState(null)
const getDaysArray = async (s, e) => {
let a = [];
for (let d = new Date(s); d <= e; d.setDate(d.getDate() + 1)) {
a.push(new Date(d).toString());
}
return a;
};
const prevMonth = async () => {
setCount((state) => state + 1);
let d = new Date();
d.setMonth(d.getMonth() - count);
let firstDay = new Date(d.getFullYear(), d.getMonth(), 1);
let lastDay = new Date(firstDay.getFullYear(), firstDay.getMonth() + 1, 0);
const dates = await getDaysArray(firstDay, lastDay);
setDates(dates);
};
const nextMonth = async () => {
setCountTwo((state) => state + 1);
let d = new Date();
d.setMonth(d.getMonth() + countTwo);
let firstDay = new Date(d.getFullYear(), d.getMonth(), 1);
let lastDay = new Date(firstDay.getFullYear(), firstDay.getMonth() + 1, 0);
const dates = await getDaysArray(firstDay, lastDay);
setDates(dates);
};
return (
<div className="App">
<button onClick={prevMonth}>Prev Month</button>
<button onClick={nextMonth}>Next Month</button>
{dates &&
dates.map((item, i) => {
return <div key={i}>{item}</div>;
})}
</div>
);
}
here is a code pen for debugging! https://codesandbox.io/s/sweet-brook-y2n7p?file=/src/App.js:0-1413
Rather than tracking the current date, use a counter to keep track of the current month number. Then subtract or add one that that number for previous or next. And se the month using that number.
Here is a fork of your project with this solution:
https://codesandbox.io/s/clever-rgb-mryst
And the code:
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [visibleMonth, setVisibleMonth] = useState(new Date().getMonth());
// const [countTwo, setCountTwo] = useState(1);
const [dates, setDates] = useState(null);
const getDaysArray = async (s, e) => {
let a = [];
for (let d = new Date(s); d <= e; d.setDate(d.getDate() + 1)) {
a.push(new Date(d).toString());
}
return a;
};
const prevMonth = async () => {
let d = new Date();
d.setMonth(visibleMonth - 1);
setVisibleMonth((state) => visibleMonth - 1);
let firstDay = new Date(d.getFullYear(), d.getMonth(), 1);
let lastDay = new Date(firstDay.getFullYear(), firstDay.getMonth() + 1, 0);
const dates = await getDaysArray(firstDay, lastDay);
setDates(dates);
};
const nextMonth = async () => {
let d = new Date();
d.setMonth(visibleMonth + 1);
setVisibleMonth((state) => visibleMonth + 1);
let firstDay = new Date(d.getFullYear(), d.getMonth(), 1);
let lastDay = new Date(firstDay.getFullYear(), firstDay.getMonth() + 1, 0);
const dates = await getDaysArray(firstDay, lastDay);
setDates(dates);
};
return (
<div className="App">
<button onClick={prevMonth}>Prev Month</button>
<button onClick={nextMonth}>Next Month</button>
{dates &&
dates.map((item, i) => {
return <div key={i}>{item}</div>;
})}
</div>
);
}

How to get the full dates between two dates using javascript / react?

My requirement is to get the number of days between two dates.
For example, the start date is 02/20/2020 to 01/03/2020 I would like to display the results like
Feb 20 Thursday, Feb 21 Friday,....01 Mar Monday.
I went through this scenario in StackOverflow but I didn't get the expected solution for the same.
Could anyone please guide in achieving this scenario using javascript or react?
You may calculate the difference between dates, than make up desired array of dates casted to date string of necessary format:
const d1 = new Date('02/20/2020'),
d2 = new Date('03/01/2020'),
diff = (d2-d1)/864e5,
dateFormat = {weekday:'long',month:'short',day:'numeric'},
dates = Array.from(
{length: diff+1},
(_,i) => {
const date = new Date()
date.setDate(d1.getDate()+i)
const [weekdayStr, dateStr] = date.toLocaleDateString('en-US',dateFormat).split(', ')
return `${dateStr} ${weekdayStr}`
}
)
console.log(dates)
.as-console-wrapper {min-height:100%;}
Or, as long as we're having fun here :) following is React implementation:
const { render } = ReactDOM,
{ useState } = React
const DatePicker = ({min,max,onPick,role}) => (
<input
type="date"
onChange={onPick}
{...{min,max}}
/>
)
const ListOfDates = ({startDate,endDate}) => {
const d1 = new Date(startDate),
d2 = new Date(endDate),
diff = (d2-d1)/864e5,
dateFormat = {weekday:'long',month:'short',day:'numeric'},
dates = Array.from(
{length: diff+1},
(_,i) => {
const date = new Date()
date.setDate(d1.getDate()+i)
const [weekdayStr, dateStr] = date.toLocaleDateString('en-US',dateFormat).split(', ')
return `${dateStr} ${weekdayStr}`
}
)
return (
<ul>
{dates.map((date,key) => <li {...{key}}>{date}</li>)}
</ul>
)
}
const App = () => {
const [start, setStart] = useState(''),
[end, setEnd] = useState(''),
onPickStart = ({target:{value}}) => setStart(value),
onPickEnd = ({target:{value}}) => setEnd(value)
return (
<div>
<DatePicker max={end} onPick={onPickStart} />
<DatePicker min={start} onPick={onPickEnd} />
<ListOfDates startDate={start} endDate={end} />
</div>
)
}
render (
<App />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
...and jQuery one:
$(document).ready(() => {
$('.datepick').on('change', function(){
$(this).attr('id') == 'startDate' ?
$('#endDate').attr('min', $(this).val()) :
$('#startDate').attr('max', $(this).val())
if($('#startDate').length && $('#endDate').length) {
const d1 = new Date($('#startDate').val()),
d2 = new Date($('#endDate').val()),
diff = (d2-d1)/864e5,
dateFormat = {weekday:'long',month:'short',day:'numeric'},
dates = Array.from(
{length: diff+1},
(_,i) => {
const date = new Date()
date.setDate(d1.getDate()+i)
const [weekdayStr, dateStr] = date.toLocaleDateString('en-US',dateFormat).split(', ')
return `${dateStr} ${weekdayStr}`
}
),
dateListItems = dates.map(d => `<li>${d}</li>`)
$('#dateList').html(dateListItems)
}
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<label>Start Date: <input id="startDate" type="date" class="datepick"></input></label>
<label>End Date: <input id="endDate" type="date" class="datepick"></input></label>
<ul id="dateList"></ul>
you can use momentjs to get the result:
//let moment = require("moment");
let date = [];
let startDate = "02/20/2020";
let endDate = "01/03/2020";
while ( moment(startDate, "MM/DD/YYYY").valueOf() <= moment(endDate, "DD/MM/YYYY").valueOf()) {
date.push(moment(startDate, "MM/DD/YYYY").format("MMM DD dddd"));
startDate = moment(startDate, "MM/DD/YYYY").add(1, "days").format("MM/DD/YYYY");
}
console.log(date);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
You can start by first creating two date objects, one for the start date and another for the end date. Then, find out how many days are in between these dates. Finally, you can loop through this number and get the current date plus the current index in the loop and print that.
As a React component:
const App = () => {
const [dates, setDates] = React.useState([]);
React.useEffect(() => {
const start = new Date('02/20/2020');
const end = new Date('03/01/2020');
const daysBetween = (end.getTime() - start.getTime()) / (1000 * 3600 * 24);
const arr = [];
for (let i = 0; i <= daysBetween; i++) {
const temp = new Date();
temp.setDate(start.getDate() + i)
arr.push(temp);
}
setDates(arr);
}, []);
return (
<ul>
{dates.map(date => (
<li key={date}>
{date.toLocaleDateString(
"en-US",
{month: "short", day: "2-digit", weekday: "long"}
)}
</li>
))}
</ul>
);
}
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
From the date given, find the difference in days then based on the no of days, make a loop and log each increment day in between using toLocaleString()..
const startDate = "02/20/2020";
const endDate = "03/01/2020";
const diffTime = Math.abs(new Date(endDate) - new Date(startDate));
const diffDays = 0|diffTime/864e5;
for(let i = 0; i <= diffDays; i++){
const newdate = new Date(new Date(startDate).getTime()+(i*864e5));
console.log(newdate.toLocaleString('en-us', { day:'2-digit', month: 'short', weekday:'long'}))
}
Another method to get the difference between two dates in JavaScript:
const d1 = new Date("06/30/2019");
const d2 = new Date("07/30/2019");
// To calculate the time difference of two dates
const timeDiff = d2.getTime() - d1.getTime();
// To calculate the no. of days between two dates
const days = timeDiff / (1000 * 3600 * 24);
//to list the days
while (days !== 0) {
let date = new Date(d2)
date.setDate(date.getDate() - days)
console.log(date)
days--
}

How to i represent date array list between two date in proper way?

I have date array from between two date. But i can not represent it in pretty way of array list.
stackblitz link
I want the array list as like:
[
{date: "2019-12-5", day: 'Thursday'}, {date: "2019-12-6", day: 'Friday'}
]
Using toLocaleDateString() and weekday option
let startDate = new Date("2019-12-05");
let endDate = new Date("2019-12-15");
function getDateArray(start, end) {
const arr = [];
const dt = new Date(start);
while (dt <= end) {
arr.push(new Date(dt));
dt.setDate(dt.getDate() + 1);
}
const dateDayArray = [];
for (const val of arr) {
const year = val.getFullYear();
const month = val.getMonth() + 1;
const date = val.getDate();
const fullDate = year + '-' + month + '-' + date;
let obj = {
date: fullDate,
day: val.toLocaleDateString('en-US', {weekday: 'long'})
};
dateDayArray.push(obj);
}
console.log(dateDayArray);
}
getDateArray(startDate, endDate)
Updated your stackblitz using days array. It is faster than any computations.
const days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
getDateArray(start, end) {
const arr = [];
const dt = new Date(start);
while (dt <= end) {
arr.push(new Date(dt));
dt.setDate(dt.getDate() + 1);
}
// console.log(arr);
const dateDayArray = [];
for (const val of arr) {
const year = val.getFullYear();
const month = val.getMonth()+1;
const date = val.getDate();
const fullDate = year + '-' + month + '-' + date;
let obj: any = {
date: fullDate, day: days[val.getDay()]
};
dateDayArray.push(obj);
}
console.log(dateDayArray);
}
The best way to use momentjs. Using momentjs, you can convert the date into any format.
const dt = moment(new Date()).format('YYYY-MM-dd');
Try like this:
var days = [ "Sunday","Monday","Tuesday", "Wednesday", "Thursday","Friday","Saturday"];
this.output = this.input.map(item => ({
date: item.date,
day: days[item.day],
}));
Working Demo

get month range using new Date()

function firstDayOfMonth(given_month) {
var d = new Date();
d.setMonth(given_month, 1);
return d.toISOString();
}
function lastDayOfMonth(given_month) {
var d = new Date();
d.setMonth(given_month + 1, 0);
return d.toISOString();
}
var temp = {
firstDayOfMonth: firstDayOfMonth(given_month),
lastDayOfMonth: lastDayOfMonth(given_month)
}
console.log(JSON.stringify(temp))
Trying to get first day of the month and last day of the month to develop a monthly report. I got {"firstDayOfMonth":"2016-11-01T09:45:30.998Z","lastDayOfMonth":"2016-11-30T09:45:30.998Z"}
for temp, is that normal? why it's 9:45 something?
You are doing the right thing. You just need to set hour for start and end day:
firstDayOfMonth.setHours(0,0,0,0);
lastDayOfMonth.setHours(23,59,59,999);
Just normalize date object with H/M/S set to 12:00:00, like this:
const givenMonth = 6;
const normalizeTime = d => {
d.setHours(12);
d.setMinutes(0);
d.setSeconds(0);
return d;
};
const getISODate = (month, day) => {
const d = new Date();
d.setMonth(month, day);
return normalizeTime(d).toISOString();
};
const temp = {
firstDayOfMonth: getISODate(givenMonth, 1),
lastDayOfMonth: getISODate(givenMonth + 1, 0)
};
temp;

Categories