Change React State based on a specific timezone - reactjs

Let's imagine that a local bakery has a website that shows its working hours along with a tag "Open" or "Closed". This tag needs to change based on the hours of operation.
The solution that I thought of was to have two states:
for status of working hours (open/closed)
for current time
For the second one I used a solution that I found for online clock:
const [date, setDate] = useState(new Date());
useEffect(() => {
let timer = setInterval(() => setDate(newDate()), 1000);
return function cleanup() {
clearInterval(timer);
};
});
And for changing open/closed state I thought of using this handler:
const handleStatus = () => {
let openHour = new Date();
let closeHour = new Date();
openHour.setHours(9, 0, 0);
closeHour.setHours(18, 30, 0);
if (date > openHour) {
//CLOSED
} else {
//OPEN
}
};
The problem with this approach is that it only works for the state that I am located at and not the other timezone. The goal is to keep the state change for only a specific timezone, so the user that is located in NYC at 9pm will see that the bakery is still open since it is 6pm in LA and it closes only at 6:30pm. How do I go around that? If there is any other approach please let me know!

As of now, JavaScript standard library does not have a concept of ZonedDateTime. Until it becomes available, you can calculate the timezone offset manually and operate on the Date objects in UTC.
// Calculate timezone offset of America/Los_Angeles
utcDate = new Date();
utcDate.setHours(0, 0, 0, 0);
strDateLA = utcDate.toLocaleString("en-US", {
timeZone: "America/Los_Angeles",
});
offset = (new Date(strDateLA) - utcDate) / (1000 * 60 * 60);
console.log(offset);
Now that you have the timezone offset, you can convert the opening and closing hours into UTC date-time.
openHourUTC = new Date();
openHourUTC.setHours(9, 0, 0, 0);
// Opening hour in UTC
openHourUTC = openHourUTC - offset;
closeHourUTC = new Date();
closeHourUTC.setHours(18, 30, 0, 0);
// Closing hour in UTC
closeHourUTC = closeHourUTC - offset;
Note: If the timezone offset is negative e.g. -8, the above code snippet calculates openHourUTC = openHourUTC - (-8) => openHourUTC = openHourUTC + 8.
Now, you can do:
if (date >= openHourUTC && date < closeHourUTC) {
// OPEN
} else {
// CLOSED
}

Maybe you should try to use the browser's timezone instead of the local device's timezone. You can get the browser's timezone with the Intl.DateTimeFormat().resolvedOptions().timeZone method.
You can use this method to update the working hours status based on the browser's timezone:
const handleStatus = () => {
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
// Set the open and close hours based on the timezone
let openHour, closeHour;
if (timezone === 'America/Los_Angeles') {
openHour = new Date();
openHour.setHours(9, 0, 0);
closeHour = new Date();
closeHour.setHours(18, 30, 0);
} else {
// Set the open and close hours for other timezones here
}
if (date > openHour && date < closeHour) {
// The bakery is open
} else {
// The bakery is closed
}
};
This way, the working hours status will always be based on the browser's timezone, regardless of the local device's timezone.
Alternatively, you could use a server-side solution to get the correct time for the browser's timezone. This would involve making a server-side API call to get the current time in the browser's timezone, and then using that time to update the working hours status. This approach would be more accurate, as it takes into account daylight saving time and other factors that can affect the local device's time. However, it would require setting up a server and making API calls, which may not be necessary if you only need to display the working hours status on a static website.
I hope this helps you some ways.

Related

Discord.js Time command

Const currentDate = new Date();
client.on('message', msg => {
if(msg.author.bot) return;
if(msg.content === `${prefix}time`) {
msg.channel.send(currentDate.toLocaleString());
}
})
This does provide the right time for MY timezone but is there anyway to make the bot send a time stamp, different regions see a different time?
Well, you can use Discord's <t:unix-timestamp:t> feature. It'll vary depending on the timezone of the user seeing the message. You can get the Unix timestamp with Date.now() (const unixTimestamp = Date.now()) but that's the timestamp in milliseconds so you will need to divide it by 1000 for seconds.
You also want to use Math.floor since dividing milliseconds will give a decimal value and Discord's timestamp thing won't work.
const unixTimestamp = Math.floor(Date.now() / 1000)
Here's the command using your command handler:
client.on('message', (msg) => {
if (msg.author.bot) return;
if (msg.content === `${prefix}time`) {
const unixTime = Math.floor(Date.now() / 1000);
// you can remove the :t in the end so it's a full date
// :t makes it only the time
msg.channel.send(`The current time is <t:${unixTime}:t>`);
}
});
Also, I recommend using messageCreate and not message as it's deprecated.

Is it possible to call a function after a specifc time?

For example is it possible to call a function after 2 weeks?
I thought about react and a library like dayjs and some code like this:
const date = Number(dayjs(new Date()).format("DD"))
if (date === date + 14) {
//function
}
I testet it with seconds and realised that i am dumb because everytime i refresh the page it will restart the "timer". Do you have any ideas if it is possible?
As suggested in the comments, you could make a cookie/session storage item containing the date at which you want this to take place. Here is an example using session storage, though a cookie is more preferable:
const nowInTwoWeeks = new Date(Date.now()) // Today's date
nowInTwoWeeks.setDate(nowInTwoWeeks.getDate() + 14) // Add 14 days
sessionStorage.setItem('twoWeeksMark', nowInTwoWeeks)
const now = new Date(Date.now()) // Today's date to use for comparison
if (now >= sessionStorage.getItem('twoWeeksMark')) {
// Do the work
}

Implementing a 15 minute timeout in React issues

I'm implementing a Save feature on a form I'm creating in React. When a User clicks save on a form, I want them to type the username/password. The next time that same user tries to save, if it's been under 15 minutes, I want them to not have to sign the save username/password. If it's over 15, I want them to authenticate again.
I stumbled across this code on a different stackoverflow question about timeouts.
calculateHours(date1 , expireTime){
const dateOne = date1;
const dateTwo = new Date();
const dateOneObj = new Date(dateOne);
const dateTwoObj = new Date(dateTwo);
const hours = Math.abs(dateTwoObj - dateOneObj)/ 36e5;
const minutes = hours/ 36e5;
return expireTime > hours ? true : false
}
Code for defining the 15 minute timeout currently.
if(this.calculateHours(localStore.time , 15)){
this.wrappedClickHandler()
This is for a 15 minute timeout, but the code is really sloppy and ugly and I was wondering if anyone has a better way of implementing this?
Unless you use a library to handle time, it is not so easy to get better code: You can do some better naming, but the operation is similar:
const isExpired = (startDate, minutes) => {
startDate = startDate instanceof Date ? startDate : new Date(startDate)
const diff = new Date() - new Date(startDate)
const minutesDiff = Math.floor((diff/1000)/60);
return minutesDiff >= minutes
}
You can use setTimeout. The function will execute after a given time
setTimeout(() => {
...your necessary code for requiring user to enter username & password...
}, 15 * 60 * 1000) // since time is to be written in milliseconds
When dealing with time, I always use epochMilli.new Date().getTime() then plus 15 minutes is (15*60*1000) milliseconds to be epochExpiresAt kept somewhere(redux?) And then you use the isExpired Function to check every time user click save.
const isExpired = (epochExpiresAt) => epochExpiresAt > new Date().getTime()

React function generating available time slots doesn't generate the correct time slots

Hi I want to make a function that generates available time slots. It should generate the time slots while keeping in mind that the time slot can't overlap with an already made appointment.Before the time slots are generated a user can specify which kind of appointment to schedule. Each appointment sort has a duration. So it should also check if the time slot added with the duration doesn't overlap.
I'm struggling to make this all working so far I get time slots but it seems to only checks the start of an already made appointment. I'm kind of running in circles here and would love for some advice or part solutions that I can implement to make my idea work
const GenerateAvailableTimeSlots = (start, serviceObject, allAppointments) => {
const moment = extendMoment(Moment);
var x = {
nextSlot: 15,
appointmentsOfThatDay: [],
startTime: '8:00',
endTime: '20:00'
};
// function to filter only the appointment that occur on specified day --> ( start )
let filterAppointments = (allAppointments, start) => {
let results = [];
let filterAppoinments = allAppointments.filter(appoinment => appoinment.date === start.format('MMMM Do YYYY'));
filterAppoinments.map(appoinment => results.push([appoinment.start.format('HH:mm'), appoinment.end.format('HH:mm')]))
console.log("results", results);
return results;
};
x.appointmentsOfThatDay = filterAppointments(allAppointments, start)
console.log("appointmentsOfThatDay", x.appointmentsOfThatDay)
var slotTime = moment(x.startTime, "HH:mm");
var endTime = moment(x.endTime, "HH:mm");
// function to check time slot overlaps with already made appointments
function OverlapsScheduledAppointment(slotTime, appointments) {
//added duration to timeslot so I could check if a suggested timeslot + the duration also doesn't overlap with already made appointment
var slotTimeWithDuration = slotTime.clone().add(serviceObject.hours, 'hours').add(serviceObject.minutes, 'minutes');
// I don't know where I also could check for slotTimeWithDuration overlap
return appointments.some((br) => {
console.log(slotTime >= moment(br[0], "HH:mm") && slotTime < moment(br[1], "HH:mm"));
return (slotTime >= moment(br[0], "HH:mm") && slotTime < moment(br[1], "HH:mm"));
});
}
let times = [];
while (slotTime < endTime) {
if (!OverlapsScheduledAppointment(slotTime, x.appointmentsOfThatDay)) {
times.push(slotTime.format("HH:mm"));
}
slotTime = slotTime.add(x.nextSlot, 'minutes');
}
return times;
};
I've found the answer to my question.
I was going in the right direction with the above code but in order for generating available time slots that keep in mind the duration of the service you want to schedule and the appointment that are already scheduled.
I had to change this line of code:
// this line just pushes the filtered appointment for a specific day
filterAppoinments.map(appoinment => results.push([appoinment.start.format('HH:mm'), appoinment.end.format('HH:mm')]))
To this
// this line filters the appointment for a specific day and also adds the duration of a service to the start time of an already scheduled appointment. This way when I check if a generated time slot for a service will overlap with an already scheduled appointment it filters out the ones that will overlap
filterAppoinments.map(appoinment => results.push([appoinment.start.clone().subtract(serviceObject.hours, 'hours').subtract(serviceObject.minutes, 'minutes').format('HH:mm'), appoinment.end.format('HH:mm')]))

How to make a command time counter

My question is: how can I make a command clock (when you execute !count and after 4 minutes you type !time and it says 4 minutes!) in discord.js
const Discord = require('discord.js');
exports.run = (client, message) => {
var af = 0;
a = setInterval(function(){
console.log("Hi");
af = af+1;
if(af == 25){
clearInterval(a);
}
console.log(af);
}, 60000);
};
exports.help = {
name: 'time',
description: 'time?',
usage: 'time'
};
I would do it like this: when you execute !count you save the server time, when you execute !time you send back the difference between those two dates.
Pseudo-code:
var date;
if (command == 'count') {
date = new Date();
message.reply("Done");
}
if (command == 'time') {
let result = require('pretty-ms')(date ? (new Date() - date) : 0);
message.reply(result);
}
I'm using the pretty-ms npm package to format milliseconds: docs & live demo.
When someone calls !count, store the current Date somewhere. new Date() - date will give you the difference between the current time and the time you stored, in milliseconds.
Please note that if the commands are in different files, as it seems by the code you posted, you'll need to store the date in a location accessible to both files: one of the solutions is to store the date as a global variable.
// by '...' I mean that you can put it wherever you want in the global object
global['...'].date = new Date():
new Date() - global['...'].date
Edit: Date class explanation
When you create a new Date, it saves the time at the moment you create it. It's like saying "!count was executed at 04:20". When you want to check how much time has passed, you need to calculate the first date minus the current date: "!count was executed at 04:20. Now it's 05:40, so the difference is 05:40 - 04:20 = 01:20: it's 1 hour and 20 minutes since you first executed !count". That translates to new Date() - past_date = time_passed.
Since dates are stored in milliseconds that difference is in milliseconds: if you want to make it more readable, you can format it by using a function as the 'pretty-ms' package, or similar ones.
The key concept is:
When !count is called, you save a new Date() to lock that point in time
When !time is called, you get the difference by doing new Date() - past_date

Resources