I have created a simplified version of the project I am working on. I want the divs in the past above the current date (in red), and the divs in the future below. I have tried using order in CSS but that didn't work.
<div class="timeline">
<div>04-10-2022</div>
<div>05-02-2022</div>
<div>06-12-2022</div>
<div>07-26-2022</div>
<div>01-15-2023</div>
<div>02-01-2023</div>
</div>
const timeline = document.querySelector('.timeline')
const timeline_items = document.querySelectorAll('.timeline > div')
const today = new Date()
const s = new Intl.DateTimeFormat("en-uk", {
dateStyle: "short"
})
timeline_items.forEach(dateitem => {
const dateConvert = new Date(dateitem.textContent);
const newDate = s.format(dateConvert);
dateitem.innerHTML = newDate;
})
const date_container = document.createElement("div");
date_container.style.background = "red";
const DateText = document.createTextNode(s.format(today));
date_container.appendChild(DateText);
timeline.appendChild(date_container)
Given a container (.timeline) you may:
collect all the dates available there;
add a new element, if not present yet, containing the date of today (formatted as
MM-DD-YYYY) and setting its class as today;
sort all those dates in cronological order ascending from past to future;
unset the html content of the container and append to it the dates according to their new order;
The date of today will be styled according to the css rule selecting the .today elements.
main();
function main(){
const timeline = document.querySelector('.timeline');
const todayFormatted = getTodayFormatted();
const today = addTodayToList(timeline, todayFormatted);
const dates = [...timeline.children];
sortDates(dates);
refreshList(timeline, dates);
}
//returns today as MM-DD-YYYY
function getTodayFormatted(){
const today = new Date();
const dd = today.getDate().toString().padStart(2, '0');
const mm = (today.getMonth() + 1).toString().padStart(2, '0');
const yyyy = today.getFullYear();
return `${mm}-${dd}-${yyyy}`;
}
//adds today date to target if not present yet,
//and adds also the 'today' class (and returns the element)
function addTodayToList(target, today){
//searches if the today date was present already in the list
let dates = target.querySelectorAll(':scope > div');
//isn't safe against duplicates existing in the html list
let todayDate = [...dates].filter( date => date.textContent == today)?.[0];
//if not, creates a new date and append to list
if(!todayDate){
todayDate = document.createElement('div');
todayDate.textContent = today;
target.append(todayDate);
}
//sets anyway its class 'today'
todayDate.classList.add('today');
return todayDate;
}
//sorts the elements in dates according to their date content
//(based on the fact the dates rearranged as YYYY-MM-DD follows alph. order)
function sortDates(dates){
dates.sort((a, b) => {
const [aMonth, aDayOfMonth, aYear] = a.textContent.split("-");
const [bMonth, bDayOfMonth, bYear] = b.textContent.split("-");
const aRearranged = `${aYear}-${aMonth}-${aDayOfMonth}`;
const bRearranged = `${bYear}-${bMonth}-${bDayOfMonth}`;
return aRearranged.localeCompare(bRearranged);
});
}
//empties the target element and appends to it the passed dates
function refreshList(target, dates){
target.innerHTML = '';
target.append(...dates);
}
.today{
background: red;
}
<div class="timeline">
<div>04-10-2022</div>
<div>05-02-2022</div>
<div>06-12-2022</div>
<div>07-26-2022</div>
<div>01-15-2023</div>
<div>02-01-2023</div>
<div>06-11-2023</div>
<div>03-08-2023</div>
</div>
Related
This question already has answers here:
How to add days to Date?
(56 answers)
Closed 6 months ago.
I'm building a weekly picker. For the most part it works as intended, I actually get the right week when I click on it, but the selected days within the calendar portion of the picker are displaying a day behind. For example, in the screenshot below, the week picked is 2020-06-08 to 2020-06-14 yyyy-mm-dd format by the way. But as you can see in the picker, it displays the week shifted - 1
I'll post the relevant code but hard as I tried, I couldn't make a codesandbox for you. Way too many dependencies
Here is the convertDate function for the formatting. This is not related to the problem but in case you're wondering what was in it from the main function
const convertDate = (date) => {
let dt = new Date(date);
var yyyy = dt.getFullYear();
var mm = dt.getMonth() + 1; // getMonth() is zero-based
var dd = dt.getDate();
return `${yyyy}-${(mm > 9 ? '' : '0') + mm}-${(dd > 9 ? '' : '0') + dd}`;
};
And this the actual function that renders the days in the picker
EDIT (with help from Sergei
const handleClick = (e) => {
setDate(new Date(date.setDate(e.target.id)));
// added this to calculate the offset to my local timezone
let localDate = new Date(date.setDate(e.target.id));
var localoffset = new Date().getTimezoneOffset();
var timeZoneFromDB = -4.00;
var tzDifference = timeZoneFromDB * localoffset + localDate.getTimezoneOffset();
var offsetTime = new Date(localDate.getTime() + tzDifference * localoffset * 1000);
const firstDay = new Date(
offsetTime.setDate(offsetTime.getDate() - offsetTime.getDay() + 1)
);
const lastDay = new Date(
offsetTime.setDate(offsetTime.getDate() - offsetTime.getDay() + 7)
);
setWeek({ firstDay, lastDay });
const value = e.target.value;
setState({
...state,
[e.target.name]: value,
startdate: convertDate(firstDay),
enddate: convertDate(lastDay)
})
};
const renderDays = () => {
let month = date.getMonth() + 1;
let ar = [];
for (let i = 1; i <= days[month]; i++) {
let currentDate = new Date(date).setDate(i);
let cName = "single-number ";
if (
new Date(state.startdate).getTime() <= new Date(currentDate).getTime() &&
new Date(currentDate).getTime() <= new Date(state.enddate).getTime()
) {
cName = cName + "selected-week";4
console.clear();
console.log(new Date().getTimezoneOffset());
console.log("start date: ", new Date(state.startdate).getTime());
console.log("converted start date: ", convertDate(new Date(state.startdate).getTime()));
console.log("current date: ", new Date(currentDate).getTime());
console.log("converted current date: ", convertDate(new Date(currentDate).getTime()));
console.log("end date: ", new Date(state.enddate).getTime());
console.log("converted end date: ", convertDate(new Date(state.enddate).getTime()));
}
ar.push(
<div key={v4()} id={i} className={cName} onClick={handleClick}>
{i}
</div>
);
}
const displayDate = new Date(date).setDate(1);
let dayInTheWeek = new Date(displayDate).getDay();
let empty = [];
for (let i = 1; i < dayInTheWeek; i++) {
empty.push(<div key={v4()} id={i} className="single-number empty"></div>);
}
return [...empty, ...ar];
};
Here's a pic of what I am console.logging so you can see that I am capturing the intended dates (ignore the undefined). This is the state being console logged in the useEffect() But also check out the individual logs from within the renderDays(). They're a day behind
I just need the days in the picker to + 1 in the view Thanks in advanced you for your help
With the help of Sergei and Heretic in the comments I was able to get it to work by adding the following helper function outside of the main view object
function addDays(date, days) {
var result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
Then I updated my render function to conditionally check this way
let currentTime = new Date(currentDate).getTime()
let startdayinview = new Date(state.startdate).getTime()
let enddayinview = new Date(state.enddate).getTime()
if (
addDays(startdayinview, -1) <= addDays(currentTime, -2) &&
addDays(currentTime, 0) <= addDays(enddayinview, 1)
) {
cName = cName + "selected-week";
}
As Heretic mentioned, the Date object is not ideal, but we gotta work with what we're given!
How can I get all weekDays(a particular weekday concerned) between a start Date and an end date inclusively. The function would take startDate, endDate (both as moment objects), dayNum(as integer)(0 - Sun, 1 - Mon ... 6 - Sat) and return a list of moment objects of such days.
I have written a function but it is not working.
const getDates = (startDate, endDate, dayNum) => {
let realStart = moment(startDate);
let end = moment(endDate);
let start = realStart.clone();
start.add(-15, "days");
let result = [];
var current = start.clone();
current.day(dayNum + 1);
while (current.isSameOrBefore(end)) {
if (current.isSameOrAfter(realStart)) {
let temp = current.clone();
result.push(temp);
current.day(8 + dayNum);
} else {
current.day(8 + dayNum);
}
}
return result;
};
rzr_f's answer always increments one day at a time, which is inefficient when there is a large span between startDate and endDate, or when the function is called repeatedly.
Here is a more efficient version which increments by 7 days at a time, and never has to check whether the date it's adding is on the correct weekday since it always starts on the correct weekday:
const getDates = (startDate, endDate, dayNum) => {
let current = moment(startDate)
const end = moment(endDate)
const results = []
// If current is on the wrong weekday, move it forward to the first matching weekday:
if (current.weekday() !== dayNum) {
current.add(dayNum >= current.weekday() ? ( dayNum - current.weekday() ) : ( 7 - ( current.weekday() - dayNum ) ), 'day')
}
while(current.isSameOrBefore(end)) {
results.push(current.clone())
current.add(7, 'day')
}
return results
}
I think something like this would work for what you're trying to do:
const getDates = (startDate, endDate, dayNum) => {
let current = moment(startDate)
const end = moment(endDate)
const results = []
while(current.isSameOrBefore(end)) {
if (current.weekday() === dayNum) results.push(current.clone())
current.add(1, 'day')
}
return results
}
I have a list of array exdate which has some date. I want to exclude those dates and suggest the next available date from today. Date should not be random.
const exdate = ["24/08/2020", "8/8/2020"] //dates needs to be excluded [DD/MM/YYYY]
The newly generated date would be "25/08/2020" which is the closest one and not in the array.
This post has a question that is generating a random date using math.random function but my scenario is different.
Iterate inside a while loop and check if exdate contains the current date. If it doesnt contain the date, add 1 day to the current date and check it again. If the current date is not present inside the exdate array, exit the while loop and print the value.
A thing you might consider: What is the expected format of your dates? You should make sure it stays consistent. e.g. dont use leading 0s in front of months. My answer should point you into the right direction.
exdate = ['24/8/2020', '8/8/2020'];
let currDate = new Date();
let dd = currDate.getDate();
let mm = currDate.getMonth() + 1;
let y = currDate.getFullYear();
let dateFormat = dd + '/' + mm + '/' + y;
while (true) {
dd = currDate.getDate();
mm = currDate.getMonth() + 1;
y = currDate.getFullYear();
dateFormat = dd + '/' + mm + '/' + y;
if (!exdate.includes(dateFormat)) break;
currDate.setDate(currDate.getDate() + 1);
}
console.log(dateFormat);
I think this code does what you are after and is quite simple:
import moment from "moment";
// Constants
const dateFormat = 'DD/MM/YYYY'
// Utils
const strToDate = (date) => moment(date, dateFormat)
const dateToStr = (date) => date.format(dateFormat)
const sortByMoment = (a, b) => b.diff(a)
const incrementDate = (date) => date.add(1, 'day')
const isToday = (date) => moment().isSame(date, 'day')
// Data
const exdate = ["17/08/2020", "24/08/2020", "8/8/2020"];
// Implementation
const sortNewestToOldest = (data) => data
.map(strToDate)
.sort(sortByMoment)
const nextAvailableDate = ([head]) => isToday(head) ? [dateToStr(incrementDate(head))] : [dateToStr(moment())]
nextAvailableDate check if todays date is in the exdate list, if yes return tomorrow, else return today. If you also had future dates in there that you need to accomodate for you could expand isToday to be isTodayOrInTheFuture. The moment functions you would need can all be found here.
so my json data looks like this:
[ {"begin": "2018-01-01", "end": "2018-01-07", "id":"a"}, {"begin":"2018-01-08", "end":"2018-01-15", "id":"b"}, {"begin":"2018-03-01", "end":"2018-03-07", "id":"y"}]
it goes like this at a gap of 7 days/ a week. Given a specific date, I want to pick up its id based on the dates to falls to. E.g. 2018-01-01 to 2018-01-07 would be id a etc.
But sometimes a week might not exist e.g. all February weeks are missing. So in that case, if I want id of say 2018-02-25, I should get the next closest group, which is id y.
Currently, I am getting for dates that fall in an exact group but not if they are missing or not found, as shown below. Ideally, I would love to loop the json data only once but am open to an efficient solution.
const date = new Date(); //get todays date but care about month and day only
let client_date = new Date(2018, date.getMonth() , date.getDate());
let data = parsed_data.filter(value => {
const start_date_parts = value['begin'].split('-');
const end_date_parts = value['end'].split('-');
const start_date = new Date(start_date_parts[0], start_date_parts[1]-1, start_date_parts[2]);
const end_date = new Date(end_date_parts[0], end_date_parts[1]-1, end_date_parts[2]);
// compare now client_date
if (client_date >= start_date && client_date <= end_date){
return value;
}
});
it works as expected for dates that have a group representation in the json data but not for others. Any thoughts?
You should calculate the difference between the passed datetimes adding .getTime() to get a timestamp value.
let parsed_data = [
{"begin": "2018-01-01", "end": "2018-01-07", "id":"a"},
{"begin":"2018-01-08", "end":"2018-01-15", "id":"b"},
{"begin":"2018-03-01", "end":"2018-03-07", "id":"y"}
];
const date = new Date(); //get todays date but care about month and day only
let client_date = new Date(2018, date.getMonth() , date.getDate()).getTime();
// Set default
let i = parsed_data[0];
// Set diff to timediff in 10 years
let diff = 60*60*24*365*10000;
// Loop the data
parsed_data.forEach(function(value, index) {
let start_date_parts = value['begin'].split('-');
let end_date_parts = value['end'].split('-');
let start_date = new Date(start_date_parts[0], start_date_parts[1]-1, start_date_parts[2]).getTime();
let end_date = new Date(end_date_parts[0], end_date_parts[1]-1, end_date_parts[2]).getTime();
if (Math.abs(start_date - client_date) < diff) {
diff = Math.abs(start_date - client_date); // set new diff value
i = parsed_data[index];
}
if (Math.abs(end_date - client_date) < diff) {
diff = Math.abs(start_date - client_date); // set new diff value
i = parsed_data[index];
}
});
console.log(i); // the closest datetime
console.log('The id: ' + i['id']) // the id you need
As you scan the array, remember which group is the closest to the target date of all seen so far. That way, if you exhaust the search and don't find a match, you'll know which was the closest.
const parsed_data = [ {"begin": "2018-01-01", "end": "2018-01-07", "id":"a"}, {"begin":"2018-01-08", "end":"2018-01-15", "id":"b"}, {"begin":"2018-03-01", "end":"2018-03-07", "id":"y"}]
let client_date = new Date(2018, 1, 2);
let data = parsed_data.filter(value => {
const end_date_parts = value['end'].split('-');
const end_date = new Date(end_date_parts[0], end_date_parts[1]-1, end_date_parts[2]);
// compare now client_date
if (client_date <= end_date){
return value;
}
});
console.log(data);
For my class assignment, I need to return an array of dates in between two selected dates on a calendar (arrival & departure).
I was given two sets of code that I can use, however I can't figure out how to link them together.
var arrival = document.getElementById('arrivalDate');
console.log(arrival.value);
var checkout = document.getElementById('departureDate');
console.log(checkout.value);
// Figure out the number of days they are check in for.
var days = checkout.value.split('-')[2] - arrival.value.split('-')[2];
console.log(days);
function dateRange(arrival, days) {
range = [];
// Starting At
return range;
}
// Returns an array of dates between the two dates
var getDates = function(startDate, endDate) {
var dates = [],
currentDate = startDate,
addDays = function(days) {
var date = new Date(this.valueOf());
date.setDate(date.getDate() + days);
return date;
};
while (currentDate <= endDate) {
dates.push(currentDate);
currentDate = addDays.call(currentDate, 1);
}
return dates;
};
// Usage
var dates = getDates(new Date(2013,10,22), new Date(2013,11,25));
dates.forEach(function(date) {
console.log(date);
});
Seems pretty simple when you've already been given the answer!
var arrivalDate = new Date(document.getElementById('arrivalDate').value);
var departureDate = new Date(document.getElementById('departureDate').value);
var dateRange = getDates(arrivalDate, departureDate);