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

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

Related

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()}`
));

convert string to time javascript in order to have ability to sort it

I need to convert string of format "14.12.22 15:17" to date in order to sort it by seconds. thank you in advance
const toTimestamp = (strDate) => {
const parsedDate = strDate.split(".");
const b = parsedDate[2].split(" ");
const string = `20${b[0]}-${parsedDate[0]}-${parsedDate[1]}T${b[1]}:00`;
const date = new Date(string).getTime();
console.log(string);
return date;
};
toTimestamp('4.12.22 15:17');
You want to use Date.parse() after converting the string to ISO 8601 format:
const datestrings = ["14.12.22 15:17", "14.12.22 15:16", "12.12.22 22:22"]
const toTimestamp = (strDate) => {
const parts = strDate.split(" ")
const d = parts[0].split(".");
const t = parts[1]
const string = `20${d[2]}-${d[1]}-${d[0]}T${t}`;
const date = Date.parse(string);
console.log(string, date);
return date;
}
const dateTimes = []
datestrings.forEach(d => dateTimes.push(toTimestamp(d)))
dateTimes.sort()
console.log(dateTimes)
var t = "14.12.22 15:17"
var ts = t.split(" ");
var tss = ts[0].split(".")
var tsss = []
tsss.push(tss[1] + ".")
tsss.push(tss[0] + ".")
tsss.push(tss[2] + " ")
tsss.push(ts[1])
tsss.join();
var d = new Date(tsss);
console.log(d.getTime()/1000);
You can use third-party libraries for parsing the date, e.g., moment.js is one of the famous ones.
Your code can simplified as
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<script>
const momentDate = moment('14.12.22 15:17', 'YY.MM.DD HH:mm');
console.log(momentDate.toDate());
</script>
</body>
</html>
Once you have the object you can easily sort on the date object.
Using dayjs:
const dayjs = require('dayjs')
const customParseFormat = require('dayjs/plugin/customParseFormat')
dayjs.extend(customParseFormat)
const toTimestamp = (strDate) => {
return dayjs(strDate, 'DD.MM.YY HH:mm').unix()
}
I'm not sure why you are doing all the parsing and re-assembling into an ISO format manually? Without pulling in any libraries like suggested by #Manish Rawat you could do the parsing with a regex, might make it a bit more readable:
// as pointed out in the comments, this doesn't work as expected
// it will interpret the '4' as the month...
//console.log((new Date('4.12.22 15:17')).toISOString())
const toDate = function(str) {
const rex = /(?<day>\d+).(?<month>\d+).(?<year>\d+)\s+(?<hours>\d+):(?<minutes>\d+):?(?<seconds>\d+)?/;
const { day, month, year, hours, minutes, seconds } = (str.match(rex))?.groups || {
day: 0,
month: 0,
year: 0,
hours: 0,
minutes: 0,
seconds: 0
};
// console.log(str.match(rex)?.groups, year, month, day, hours, minutes, seconds);
return new Date(`20${year}-${month}-${day} ${hours}:${minutes}${seconds ? ':' + seconds : ''}`);
};
console.log(toDate('4.12.22 15:17'), toDate('4.12.22 15:17').getTime());
also, from there, what kind of sort are you planning to do? string based i assume? why not convert those dates to actual unix timestamps, e.g. number of seconds and compare those, so you won't have to deal with "weird" javascript string sorting things that might not yield the sort you are expecting?

React Calendar value getting stored with one day less in database

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.

Change timezone to another timezone

I have a form in which the user selects a date, time and a timezone.
Example:
let date = '02.09.2020';
let time = '16.00';
let timezone = '-07.00';
I want to convert this date with timezone : '+02.00';
I have a hidden text input in which i pass the actualized date format i want.
I don't want to use momment.js for such a small thing.
You could construct a UTC date with an offset of the given timezone -07.00. Then you can add the desired offset of +02.00 to that.
Update: I am going to assume the following, based on other user's input.
Local date: 2020-02-09 16.00 -07:00
UTC date: 2020-02-09 23:00 (subtract the local timezone offset)
UTC +2: 2020-02-10 01:00 (add the desired offset)
const offsetDate = (dateStr, timeStr, srcOffset, destOffset) => {
const tokenize = str => str.split('.').map(v => parseInt(v, 10));
const [ month, date, year ] = tokenize(dateStr);
const [ hour, minute ] = tokenize(timeStr);
const [ srcOffHours, srcOffMins ] = tokenize(srcOffset);
const [ destOffHours, destOffMins ] = tokenize(destOffset);
const utcDate = new Date(Date.UTC(year, month - 1, date, hour, minute));
const srcOff = srcOffHours * 36e5 + srcOffMins * 6e4;
const destOff = destOffHours * 36e5 + destOffMins * 6e4;
utcDate.setTime(utcDate.getTime() - srcOff + destOff);
console.log(`Time: ${utcDate.toISOString()}`);
return [
utcDate.getUTCDate(),
utcDate.getUTCMonth() + 1,
utcDate.getUTCFullYear().toString().substr(2)
].map(v => (v + '').padStart(2, '0')).join(':');
};
const date = '02.09.2020';
const time = '16.00';
const timezone = '-07.00';
console.log(offsetDate(date, time, timezone, '+02.00')); // 10:02:20
You can do something like this
let date = new Date();
let event = date.toLocaleString('ko-KR', { timeZone: 'UTC' })) // if the timezone was Korea

Checking if a string matches dates?

If we have a text search field that the user types 11/25/2014 and we have an array of dates:
var dates = [ new Date(2014, 11, 25), new Date(2014, 11, 24) ];
How should we go about checking whether the string entered matches any of the dates?
I assume would first see whether it's actually a valid Date, as the user could have entered foo and new Date('foo') is an invalid date.
After that is the some of of includes like we do with strings for the Javascript Date API?
You can format the dates using date functions and construct the desired format and compare it with the input date, look for the index of the formatted date inside the array using findIndex :
const input = "11/25/2014";
var dates = [new Date(2014, 11, 25), new Date(2014, 11, 24)];
const exists = str => dates.findIndex(date => {
const day = date.getDate();
const monthIndex = date.getMonth();
const year = date.getFullYear();
return `${monthIndex}/${day}/${year}` === str;
}) > -1 ? true : false;
console.log( exists(input) )
Yeah you need to validate that the input date is actually a valid date, after that you could check if timestamp for input date matches with some of the dates into array, this could could help you
const isValidDate = (inputDate, dates) => {
const now = new Date(inputDate);
if (isNaN(now)) {
throw new Error(`${inputDate} is not a valid date`);
// or return false instead
}
// this will return the first date that matches with the input date
return dates.some(d => d.getTime() === now.getTime());
}
Please remember that you actually need to validate the format for the input date, so if the date must keep dd/mm/YYYY you must to validate that the input date keeps that format.
It really depends on exactly how consistent you expect their input to be. If you're always going to be getting a date that looks like 'DD/MM/YYYY' with no variation, then you're in a good place and can do something quite easy.
const userDate = new Date(inputText);
const dates = [ new Date(2014, 11, 25), new Date(2014, 11, 24) ];
const hasMatch = dates.some(d => d.getTime() === userDate.getTime());
You need to compare the milliseconds of dates to compare. Also remember that the month is 0-based so your list is actually December.
const input = '12/25/2014';
const test = new Date(input).getTime();
return !isNaN(test) && [new Date(2014, 11, 25), new Date(2014, 11, 26)]
.map(d => d.getTime())
.includes(test)
One thing to note is depending on the input, browsers are very inconsistent in what formats parse when using new Date(str), so if you want to cover more input formats use something like momentjs
Stackblitz
const input = "11/25/2014";
let result = [];
if (!isNaN(new Date(input).getTime())) {
result = dates.filter(date=> {
const day = date.getDate();
const monthIndex = date.getMonth();
const year = date.getFullYear();
const dateString = `${monthIndex}/${day}/${year}`;
return dateString.includes(input);
});
}
console.log("The dates that match are: ", result);
console.log("Two dates match: ", result.length == 2);

Categories