React Calendar value getting stored with one day less in database - javascript

import React, { useState, useEffect } from "react";
import style from "./step1.module.css";
import { Calendar } from "react-date-range";
function Step2({ onNextClick, step }) {
const [date1, setDate1] = useState({ dateFilled: new Date().toJSON().split("T")[0] });
const [date2, setDate2] = useState({ dateOfDeath: new Date().toJSON().split("T")[0] });
//const [date1, setDate1] = useState({ dateFilled: new Date()});
//const [date2, setDate2] = useState({ dateOfDeath: new Date()});
const year = new Date(date1?.dateFilled + "T00:00:00").getFullYear();
const month = new Date(date1?.dateFilled + "T00:00:00").getMonth();
const day = new Date(date1?.dateFilled + "T00:00:00").getDate();
const filledDate = new Date(year, month, day, 0, 0, 0, 0);
//filledDate.setHours(0,0,0,0);
const year1 = new Date(date2?.dateOfDeath + "T00:00:00").getFullYear();
const month1 = new Date(date2?.dateOfDeath + "T00:00:00").getMonth();
const day1 = new Date(date2?.dateOfDeath + "T00:00:00").getDate();
const deathDate = new Date(year1, month1, day1, 0, 0, 0, 0);
//deathDate.setHours(0,0,0,0);
const values = { filledDate, deathDate };
const onChange1 = (date) => {
//const options = { timeZone : 'America/Los_Angeles'};
setDate1({ dateFilled: JSON.stringify(date).slice(1, 11) });
const dateA = { dateFilled: JSON.stringify(date).slice(1, 11) };
const year = new Date(dateA?.dateFilled + "T00:00:00").getFullYear();
const month = new Date(dateA?.dateFilled + "T00:00:00").getMonth();
const day = new Date(dateA?.dateFilled + "T00:00:00").getDate();
const newFilledDate = new Date(year, month, day, 0, 0, 0, 0) ;
//newFilledDate.setHours(0,0,0,0);
onNextClick({
filledDate: newFilledDate || new Date(),
newDeathDate: deathDate || new Date(),
});
};
const onChangeDate = (date) => {
console.log(date.toISOString());
setDate1({ dateFilled: date.toISOString() });
const newFilledDate = date.toISOString();
onNextClick({
filledDate: newFilledDate || new Date(),
newDeathDate: deathDate || new Date(),
});
};
const onChange2 = (date) => {
setDate2({ dateOfDeath: JSON.stringify(date).slice(1, 11) });
const dateB = { dateOfDeath: JSON.stringify(date).slice(1, 11) };
const year = new Date(dateB?.dateOfDeath + "T00:00:00").getFullYear();
const month = new Date(dateB?.dateOfDeath + "T00:00:00").getMonth();
const day = new Date(dateB?.dateOfDeath + "T00:00:00").getDate();
const newDeathDate = new Date(year, month, day, 0, 0, 0, 0);
//newDeathDate.setHours(0,0,0,0);
onNextClick({
filledDate: filledDate,
newDeathDate: newDeathDate,
});
};
return (
<div className={style.step2Main}>
<div className={style.calendarsCont}>
<div className={style.calenderLeft}>
<h4 className={style.dateHeading}>
<p> Date Filed</p>
{`${year}-${month + 1}-${day}`}
</h4>
<Calendar date={filledDate} dateDisplayFormat='yyyy-MM-dd' onChange={onChange1} />
</div>
<div className={style.calenderRight}>
<h4 className={style.dateHeading}>
<p> Date of Death</p>
{`${year1}-${month1 + 1}-${day1}`}
</h4>
<Calendar date={deathDate} dateDisplayFormat='yyyy-MM-dd' onChange={onChange2} />
</div>
</div>
</div>
);
}
export default Step2;
The above code allows the User to select two dates. It works fine when the User enters from the US timezone. However, if the user is entering data from other timezones like say India \ Philippines it stores data for one day less. If I say select 4th July, it saves 3rd July in the database.
In the first line of the Step2 function, the new Date() returns the selected date with India/Philippines timezone, and even if I take away the time zone it still saves the date with one day less value. If I pass the ISO string instead of the date value, the Date Range picker gives Invalid Interval value error. Looks like it is expecting the date only.
Is there any way to pass the selected date as it is? Users can enter data from any timezone and they will be selecting dates from the previous year/months mostly and it should be entered into the database as it is.
I tried converting it to JSON or adding "T00:00:00" at the end but the date is still getting stored incorrectly. I am not sure why the react-date-range picker was selected as control instead of React's date picker.

Related

Trying to check if the 2 dates are in the same week

I am using date-fns to check if the 2 dates are on the same week or not.
following the documentation If I do :
const isSameWk = isSameWeek(
new Date("2023-02-05"),
new Date("2023-02-06"),
{ weekStartsOn: 0, locale: "en-GB" }
);
If I do the above snippet it will say true which is correct but it throws the error that I need to use parseISO since the new beta v2
so using parseISO
this way
const isSameWk = isSameWeek(
parseISO(new Date("2023-02-05")),
parseISO(new Date("2016-02-06")),
{ weekStartsOn: 0, locale: "en-GB" }
);
or
const isSameWk = isSameWeek(
parseISO("2023-02-05"),
parseISO(("2016-02-06")),
{ weekStartsOn: 0, locale: "en-GB" }
);
would not throw the error but console logging just this parseISO("2023-02-05") gives me the correct but not in my locale and logging parseISO(new Date("2023-02-05")) would give invalid date
Stuck on this for a long time can't figure out where am I wrong at.
Here is are native JavaScript Date functions, no need for an external library:
const getWeekNum = (date) => {
const janFirst = new Date(date.getFullYear(), 0, 1);
// Source: https://stackoverflow.com/a/27125580/3307678
return Math.ceil((((date.getTime() - janFirst.getTime()) / 86400000) + janFirst.getDay() + 1) / 7);
}
const isSameWeek = (dateA, dateB) => {
return getWeekNum(dateA) === getWeekNum(dateB);
}
const date1Str = '2023-02-05';
const date2Str = '2023-02-06';
const date3Str = '2023-02-12';
const dateSuffix = 'T00:00:00.000Z'; // or 'T00:00:00.000' for browserlocal time
const date1 = new Date(date1Str + dateSuffix);
const date2 = new Date(date2Str + dateSuffix);
const date3 = new Date(date3Str + dateSuffix);
console.log({
'date1': date1,
'date2': date2,
'date3': date3,
'getWeekNum(date1)': getWeekNum(date1),
'getWeekNum(date2)': getWeekNum(date2),
'getWeekNum(date3)': getWeekNum(date3),
'isSameWeek(date1, date2)': isSameWeek(date1, date2),
'isSameWeek(date1, date3)': isSameWeek(date1, date3),
});
Output:
{
"date1": "2023-02-05T00:00:00.000Z",
"date2": "2023-02-06T00:00:00.000Z",
"date3": "2023-02-12T00:00:00.000Z",
"getWeekNum(date1)": 6,
"getWeekNum(date2)": 6,
"getWeekNum(date3)": 7,
"isSameWeek(date1, date2)": true,
"isSameWeek(date1, date3)": false
}
Notes:
always provide a proper ISO 8601 format format for the new Date() constructor
use format YYY-MM-DDTHH:mm:ss.sssZ for UTC date
use format YYY-MM-DDTHH:mm:ss.sss for local browser date
If you have a date string variable of format YYY-MM-DD you need to append THH:mm:ss.sssZ or THH:mm:ss.sss to get the full ISO 8601 format

Same date format between MongoDB and JS but they are not equal?

I'm trying to do an action when the date today (via Node.js) and the date value (via MongoDB) is the same. However, I'm not receiving any output from my for loop indicating that there's no match.
Here's how I get the date today (date output is 2023-01-19T00:00:00.000Z):
const d = new Date(new Date().toLocaleString("en-US", { timeZone: "Asia/Hong_Kong" }));
const day = d.getDate();
const month = d.getMonth() + 1;
const year = "2023"; //this is a dummy variable since year is required for Date().
const date = new Date(year + "-" + month + "-" + day);
console.log(date);
Here's the users document from MongoDB:
name: "Angelo"
birthday: 2023-01-11T00:00:00.000+00:00 //Date data type
name: "Josh"
birthday: 2023-01-19T00:00:00.000+00:00 //Date data type
Here's the for loop that should trigger success when there's a match, but there's no output. This is my problem.
let users = [];
db.collection("users").find({})
.forEach((user) => { users.push(user) })
.then(() => {
for (i = 0; i < users.length; i++) {
if(users[i].birthday == date) {
console.log("Success"); //no output on console
}
}
})
Checking users[i].birthday in console shows:
2023-01-19T00:00:00.000Z (Shouldn't this be a match of today's date?)
2023-01-11T00:00:00.000Z
MongoDB Date objects store values in UTC. In your example you create a date object in the local timezone, so your dates will not match.
You can create a UTC zero date using the Date.UTC() static method, where zero date means a date with zero for the hours, minutes, and seconds. Here is an example that simulates your users array from MongoDB, assuming that the birthdays are all UTC zero dates. You can't compare the date objects directly, because two date objects that have the same date have different object addresses. Therefore you need to compare the value of the date objects (.valueOf()), or a string representation (.toISOString()).
function getUtcZeroDate(str) {
if(str) {
return new Date(str + 'T00:00:00.000Z');
} else {
let date = new Date();
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
}
}
const users = [
{name: 'Angelo', birthday: getUtcZeroDate('2023-01-11') },
{name: 'Josh', birthday: getUtcZeroDate() }, // today
]
const todayUtcZeroDate = getUtcZeroDate();
console.log("Today's UTC zero date:", todayUtcZeroDate.toISOString());
users.forEach(doc => {
const match = (todayUtcZeroDate.valueOf() == doc.birthday.valueOf()) ? 'match' : 'no match';
console.log(doc.name + ', born ' + doc.birthday.toISOString(), '=>', match);
});
Output at the time of writing:
Today's UTC zero date: 2023-01-18T00:00:00.000Z
Angelo, born 2023-01-11T00:00:00.000Z => no match
Josh, born 2023-01-18T00:00:00.000Z => match
A simple way to compare dates is to use setHours(0,0,0) for both birthday date and date like below:
const d = new Date(new Date().toLocaleString("en-US", { timeZone: "Asia/Hong_Kong" }));
const day = d.getDate();
const month = d.getMonth() + 1;
const year = "2023"; //this is a dummy variable since year is required for Date().
let date = new Date(year + "-" + month + "-" + day);
date=date.setHours(0,0,0)
for fetching date and comparing from db:
let users = [];
db.collection("users").find({})
.forEach((user) => { users.push(user) })
.then(() => {
for (i = 0; i < users.length; i++) {
users[i].birthday=new Date(users[i].birthday).setHours(0,0,0)
if(users[i].birthday == date) {
console.log("Success"); // output on console
}
}
})
Try it you will get output.
I suggest considering using moment.js for this.
You'll be also need moment-timezone.js add-on to use timezones.
I recommend moment.js because with it, the code becomes more readable.
moment.tz.setDefault("Asia/Hong_Kong");
const users = [{
name: "Angelo",
birthday: new Date('2023-01-11T00:00:00.000+00:00'),
},
{
name: "Josh",
birthday: new Date('2023-01-19T00:00:00.000+00:00'),
},
];
const today = moment();
for (i = 0; i < users.length; i++) {
const user = users[i];
if ( today.isSame(user.birthday, 'date')) {
console.log("Success", users[i]);
}
}
<script src="https://momentjs.com/downloads/moment.min.js"></script>
<script src="https://momentjs.com/downloads/moment-timezone-with-data.min.js"></script>
This is really just a date formatting issue.
If you want to get the date in a particular location, you can use the Intl.DateTimeFormat constructor but get the parts as separate values rather than a timestamp that then must be parsed by the built–in parser. That's a flawed methodology because the built–in parser isn't required to correctly parse such input (same with toLocaleString).
So to get the current date in a particular location and then built a timestamp that is 0hrs UTC:
// Return the date at loc for the provided date as
// a Date set to year, month, day as UTC values.
function toUTCDateFromLoc(loc, date = new Date()) {
let f = new Intl.DateTimeFormat('en', {
year: 'numeric',
month: 'numeric',
day: 'numeric',
timeZone: loc
});
let {year, month, day} = f.formatToParts(date)
.reduce((acc, part) => {
acc[part.type] = part.value;
return acc;
}, Object.create(null));
return new Date(Date.UTC(year, month - 1, day));
}
// Kirimati and Midway should always be at least 1 day different
['Pacific/Kiritimati', // +14
'Asia/Hong_Kong', // +8
'America/Dominica', // -4
'Pacific/Midway', // -11
].forEach(loc => console.log(
`${loc.padEnd(18, ' ')}: ${toUTCDateFromLoc(loc).toISOString()}`
));

Get the date from day number of week Javascript

I have a model in my database that contains an array called "AvailableDays" [0...6]. 0 = Sunday & 6 = Saturday. I am looking to convert this day number of the week to the date of day in the current week.
For example, this is the logic broken down
Retrieve the list of available days (const availableDays = [0,2,4,6])
Get the current DATE (const today = new Date('2021-08-20');)
Covert day numbers to dates (output =['15-08-2021', '17-08-2021', '19-08-2021', '21-08-2021'])
What you can do is get the day-of-the-week from the given Date instance and work out the offset from your available day.
Then subtract that offset in days from the given date to produce your result.
const transformDate = (date, day) => {
const offset = date.getDay() - day
const d = new Date(date)
d.setDate(d.getDate() - offset)
return d
}
const availableDays = [0,2,4,6]
const today = new Date("2021-08-20")
console.log(availableDays.map(day => transformDate(today, day)))
Was able to solve this myself. I am now able to wrap this into a availableDates.map() and return an array of dates using the below logic.
var availableDay = 0
var d = new Date(),
day = d.getDay(), // 0 ... 6
calcAvailableDay = day-availableDay,
diff = d.getDate() - calcAvailableDay,
output = new Date(d.setDate(diff));
console.log(output)
You can generate all the days in weeks and then get the dates using availableDays.
const getWeekDays = (current) => {
current.setDate((current.getDate() - current.getDay() - 1));
return Array.from({ length: 7 }, (_, i) => {
current.setDate(current.getDate() + 1)
return new Date(current).toLocaleDateString('en-CA');
});
},
today = new Date('2021-08-20'),
weekDays = getWeekDays(today),
availableDays = [0, 2, 4, 6],
availableDates = availableDays.map(day => weekDays[day]);
console.log(availableDates);
JavaScript getDay method returns the day of the week for the specified date according to local time, where 0 represents Sunday.
So what you have to do is connect this index with your availableDays values.
Logic
Get current date, month, year and the index of todays date.
Loop through the availableDays array, and create new dates with the difference between the current day calculated with getDay value and the day value specified in your array.
Make use of some logic to reperesent those date object in specified format. I took support from this post to format your date string.
const availableDays = [0,2,4,6];
const today = new Date();
const currentDay = today.getDay();
const currentDate = today.getDate();
const currentMonth = today.getMonth();
const currentYear = today.getFullYear();
formatDateToString = (date) => String(date.getDate()).padStart(2, '0') + '-' + String(date.getMonth() + 1).padStart(2, '0') + '-' + date.getFullYear();
const output = availableDays.map((day) => formatDateToString(new Date(currentYear, currentMonth, currentDate - (currentDay - day))));
console.log(output);

Fill in empty blocks for each calendar month JS

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;
};

Set last day of month on YYYYMM date format - Javascript

I've a variable that has value of date in YYYYMM format. For example:
var givenDate = "201704"
How can I find out the last day of the given month and append to it. For example,
//last day of 2017 04 (April) is 30th so append value to givenDate + lastDate;
//that will be 20170430
var newFullGivenDate = "20170430";
const date = "201704";
const year = parseInt(date.substring(0, 4));
const month= parseInt(date.substring(4, 6));
const lastDay = (new Date(year, month, 0)).getUTCDate();
const newFullGivenDate = date + lastDay;
console.log(newFullGivenDate);
var givenDate = "201704";
var month = givenDate.substring(4, givenDate.length); // retrieves 04
var year = givenDate.substring(0, 4); // retrieves 2017
var d = new Date(year, month, 0);
alert(d.getDate());
Reference: MDN
To achieve expected result, use below option
last day of month - new Date(year,month ,0)
var givenDate = "201704";
var currDate = new Date(givenDate.substr(0,3),givenDate.substr(4) ,0)
var newFullGivenDate = givenDate + currDate.getDate();
console.log(newFullGivenDate)
Codepen URL for reference - https://codepen.io/nagasai/pen/OmgZMW
I would break it down into two functions:
// Get last day from year and month
let lastDayOf = (year, month) => (new Date(year, month, 0)).getDate();
// Add last day to string only if input is correct
let addLastDay = (input) => {
// In case you pass number (201705) instead of string ("201705")
if (Number.isInteger(input)) input = input.toString();
// Check if input is in correct format - 6 digit string
if (typeof input !== "string" || !input.match(/^\d{6}$/)) {
return input; // You can implement desired behavour here. I just return what came
}
const year = input.substr(0, 4);
const month = input.substr(4, 2);
return input + lastDayOf(year, month);
}
// Tests
console.assert(addLastDay("201704"), "20170430");
console.assert(addLastDay("201702"), "20170228");
console.assert(addLastDay("201202"), "20120229");
console.assert(addLastDay(201705), "20170531");
console.assert(addLastDay(20170), 20170); // Wrong input
// Interactive example
document.getElementsByTagName('button')[0].addEventListener('click', () => {
let input = document.getElementsByTagName('input')[0];
input.value = addLastDay(input.value);
});
<input type="text" value="201704"><button>Calculate</button>
If you are using moment js you can yry this:
var date = moment(newFullGivenDate ).format('YYYYMMDD');
date = date.add(-1 * parseInt(date.format('DD')), 'days').add(1, 'months');

Categories