How can I update date every Sunday? - javascript

So I'm troubleshooting with some JavaScript dates. I'm working with NodeJS Mongoose and React. I'd like to update all dates in database, but I'd like to do that every weekend and keep hours, don't change them at all.
Let say I have day like 22 January 2020, and during weekend date will update itself to 29 of January and then 5 of February. Everything in database is save like ISODate("2020-01-16T16:27:15.003Z") and I have a code to update those dates whenever I want. I'm having trouble figure out how to body of setDate() should look like to automatically change months and days while keeping the same hour everytime - so 22/01/2020 4:00 PM during weekend will change to 29/01/2020 4:00PM.
I've already tried to use momentjs to handle dates but it doesn't work with my database.
cron.schedule("* * * * * *",async function() {
const courses = await Course.find({});
courses.forEach(course => {
const newDate = () => {
let date = new Date();
return date.toISOString();
};
Course.updateMany({
"nextClasses": course.nextClasses === course.startingDate ? course.startingDate :
course.nextClasses
},{$set: {"nextClasses": newDate()}},(err) => console.log(err))
});
}
That's the code responsible for changing dates, for now it changes everything to current date every second ( on purpose, for validation purposes )

This would add 7 days to all dates every Sunday.
const addDays = (date, days) => {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
};
cron.schedule("0 0 * * 0", async function() {
const courses = await Course.find({});
courses.forEach(async (course) => {
await course.update(
{
$set: {
nextClasses: addDays(course.nextClasses, 7)
}
},
(err) => console.log(err)
);
});
});
Alternate method
cron.schedule("0 0 * * 0", async function() {
const courses = await Course.find({});
courses.forEach(async (course) => {
course.nextClasses.setDate(course.nextClasses.getDate() + 7);
course.markModified("nextClasses"); /* Mongoose does not track changes made by built-in Date methods */
await course.save((err) => console.log(err));
});
});

Related

JavaScript - converting Date to UTC and removing days from it

Trying to iterate backwards from today to August, and log out all the time stamps stored on our database between those periods.
The API only allows queries between a 24 hour window, hence my need for a for loop to call it multiple times. The query’s start and end times are in ISOstring format.
Problem is, when my loop reaches October 31st, it tries to query with startTime 23:00 endTime 00:00 and complains that this is beyond the 24 hour range limit. This makes sense, as I’m from UK and this is when the clocks go back.
Is there a way for me to set my date to UTC and continuously subtract days from it, hence ignoring timezone and any daylight saving?
Our team has consistently used date-fns, so I would love a solution using this library if a library is recommended/required.
This is what I'm currently working with:
export async function dataTrawl(): Promise<void> {
try {
for (let date = new Date(); date >= new Date('2022-10-01'); date.setDate(date.getDate() - 1)) {
const startTime = new Date(date.getTime());
startTime.setDate(date.getDate() - 1);
const response: AxiosResponse<DataExchangeResponse> = await axios.get(
'...api.../1/datapoints',
{
headers: {
accept: 'application/json',
'x-api-key': 'KEY'
},
params: {
start: startTime.toISOString(),
end: date.toISOString()
}
}
);
console.log(`dataTrawl: ${JSON.stringify(response.data)}`);
}
} catch (e) {
const error = e as AxiosError;
console.error(error);
}
}
You can work in UTC days, where (in ECMAScript) every day is exactly 8.64e7 ms long:
// Set start to end of current UTC date
let start = new Date();
start.setUTCHours(24,0,0,0);
// Loop over all previous UTC days until limit
for (let limit = new Date('2022-11-21'); start > limit; start.setTime(+start - 8.64e7)) {
let end = new Date(start - 8.64e7);
// stuff with start and end
console.log(`${start.toISOString()} -`,
`${end.toISOString()}`);
}
You can use the getUTCDate() and setUTCDate() functions to move a date backwards by one day in UTC.
This way we'll be dealing with 24 hour periods consistently with no changes due to DST rules.
export async function dataTrawl(): Promise<void> {
try {
const start = new Date();
const end = new Date('2022-10-01');
console.log(`dataTrawl: start: ${start.toISOString()} -> end: ${end.toISOString()}`);
for (let date = start; date >= end; date.setUTCDate(date.getUTCDate() - 1)) {
const startTime = new Date(date);
startTime.setDate(date.getUTCDate() - 1);
const response: AxiosResponse<DataExchangeResponse> = await axios.get(
'...api.../1/datapoints',
{
headers: {
accept: 'application/json',
'x-api-key': 'KEY'
},
params: {
start: startTime.toISOString(),
end: date.toISOString()
}
}
);
console.log(`dataTrawl: ${JSON.stringify(response.data)}`);
}
} catch (e) {
const error = e as AxiosError;
console.error(error);
}
}

Cloud functions not offsetting date for firestore query

I have a cron job that runs #12PM everyday. It's supposed to grab all the documents in a collection that were added between 6AM and 12PM on that day. I'm using a field called dateOrderAdded to run the query on. This field is in the timezone GMT -4 so when I'm running the query I have to cater for that.
"use strict";
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.sendTwelvePmOrderSummary = functions.pubsub.schedule("0 12 * * *")
.timeZone("America/Caracas")
.onRun(async context => {
//Get current date
const today = new Date();
//Set time to 6AM
const todaySixAm = new Date(today.setHours(6,0,0,0));
//Set time to 12PM
const todayTwelvePm = new Date(today.setHours(12,0,0,0));
console.log(todaySixAm.valueOf()); //browser - 1609149600000 | cloud fn - 1609135200000
console.log(todayTwelvePm.valueOf()); // browser - 1609171200000 | cloud fn - 1609156800000
//Get all orders in-between today at 6AM and today at 12PM
const orderCol = await admin.firestore().collection('orders').where('dateOrderAdded','>=',todaySixAm).where('dateOrderAdded','<=',todayTwelvePm).get();
const orderDocs = orderCol.docs.map(doc=>doc.data())
if(orderCol.size === 0 ){
//No orders placed - TODO: Send no orders placed email
console.log('No orders placed');
}else{
//orders! - TODO: Send order placement summary
console.log('Orders placed');
}
return null;
}
The browser seems to set the hours to 6 and 12 appropriately but on the cloud function it remains at 2 and 8.
Since dates are UTC in cloud functions the hours 2 and 8 make sense but I explicitly set the hours to 6 and 12 and I'm not sure why I'm not seeing the change being set.
Instead of relying on the Date of the Cloud Functions, I recommend you to create the unix timestamp by your own.
The next lines will help to generate the appropiate time, according to a UTC-4 timezone:
Edit: For Nodejs 10 runtime the format varies so I had to adjust the date in order to avoid generating an incorrect date, if the format varies on the runtime of your choose, just adjust the date to be valid
var date = new Date( ).toLocaleString("es-VE", { "timeZone": "America/Caracas" }).split(", ")[0].split("/");
let today = date[2] + "-" + date[0] + "-" + date[1];
let todaySixAm = new Date(`${today}T06:00:00.000-04:00`);
let todayTwelvePm = new Date(`${today}T12:00:00.000-04:00`);
console.log("Today 6:00 am GMT-4 " + todaySixAm.valueOf()); //1609322400000
console.log("Today 12:00 pm GMT-4:" + todayTwelvePm.valueOf()); //1609344000000
This way you don't have to rely on the local time of the server hosting your code.
Taking the suggestion from #RobG in the comments I got it to work with setUTC hours.
"use strict";
const functions = require("firebase-functions");
const admin = require("firebase-admin");
exports.sendTwelvePmOrderSummary = functions.pubsub
.schedule("0 12 * * *")
.timeZone("America/Caracas")
.onRun(async context => {
//Timezone of stored data is GMT -4
//For query to be run correctly use UTC hours
//6AM = 6+ 4 = 10 UTC hours
const todaySixAm = new Date(new Date().setUTCHours(10,0,0,0));
//Set time to 11:59:59:999AM - 12 + 4 = 16
//Set it to just before 12PM so results from other CRONs won't be duplicated
const todayTwelvePm = new Date(new Date().setUTCHours(15, 59, 59,999));
//Get all orders in-between today at 6AM and today at 12PM
const orderCol = await admin.firestore().collection('orders').where('dateOrderAdded','>=',todaySixAm).where('dateOrderAdded','<=',todayTwelvePm).get();
const orderDocs = orderCol.docs.map(doc => doc.data());
if (orderCol.size === 0)
return { message: "No orders placed - TWELVE PM Summary" };
console.log('Orders placed!');
return null;
}

Removing offset from a javascript Date

I'm trying to remove the javascript Date offset, so that the local time a date was recorded in from a given country can be displayed.
I have a test that only works when the timezone is GMT
describe('.removeLocalDateOffset()', () => {
it('removes the timezone offset', () => {
const input = '2017-10-03T08:53:12.000-07:00';
const result = removeLocalDateOffset(input);
expect(result.toISOString()).toBe('2017-10-03T08:53:12.000Z');
});
});
And the function I want to do the job:
const removeLocalDateOffset = (date) => {
const originalDate = new Date(date);
return new Date(Date.UTC(
originalDate.getFullYear(),
originalDate.getMonth(),
originalDate.getDate(),
originalDate.getHours(),
originalDate.getMinutes(),
originalDate.getSeconds(),
));
}
Any suggestions for how I can do this using the methods of the new Date() object? Ideally not doing a .slice() on a substring.
Test Results. I am currently in GMT:
Expected: "2017-10-03T08:53:12.000Z"
Received: "2017-10-03T16:53:12.000Z"

How to store and display working hours for different timezones correctly in javascript?

So I'm trying to implement booking (reservations) on the website which offers online services and I want the dates and time (working days and hours) for the end user to be shown correctly, taking into consideration timezones, DST and and all that tricky stuff. The actual location of the service providers are on timezone +5, and the working hours are from 8am to 5pm. So what I want the actual user, for example, on timezone +4 to see is working hours being 7am - 4pm.
For the time being I'm using Angular Material Datepicker to store dates, and Angular Material Select with hardcoded hours.
But this is not optimal at all, and I could only get away with notifying users that the time shown is of specified timezone.
I also tried to follow this guide, but to no avail.
I have installed moment and moment-timezone but cannot figure it out yet.
I store booked dates and hours in firebase, and retrieve them with angular/fire like so
table: Table;
this.db.list('timetable').valueChanges()
.subscribe(table => this.table = table);
Then I grab the value from the datepicker input and check which working hours are available
selectedDate: string;
hours = hours; // a list of objects with hardcoded working hours in it, like {hour: "8:00", booked: false}, {hour: "9:00", booked: true} etc.
selectDate(e: MatDatepickerInputEvent<Date>) {
this.selectedDate = new Date(e.target.value).toLocaleDateString();
const bookedHours: string[] = [];
this.table.forEach((booking) => {
if (this.selectedDate === booking.date) {
bookedHours.push(booking.hour);
}
});
this.hours.forEach(time => {
if (bookedHours.includes(time.hour)) {time.booked = true;
} else { time.booked = false; }
});
}
And if 10am is booked, for example, it looks like this:
I know that the implementation is poor and hacky and I'm open for suggestions on that as well.
As I posted above momentjs and moment-timezone were suboptimal and couldn't get them figure out well. I ended up using luxon, by far the easiest library to manipulate time and dates.
Apart from regular npm installation, typing files are also necessary:
npm i luxon --save
npm i #types/luxon --save-dev
I created a helper service in my angular app, added luxon:
import {DateTime, Interval} from 'luxon';
and the function that receives a JS date and returns working hours in the user's local time.
getHours(date: Date) {
const hours: DateTime[] = [];
// Convert user date to local date
const userSelectedDate = this.userDate(date);
const serviceLocalTime = userSelectedDate.toUTC().setZone(service_ZONE),
// Set working hours for the date
serviceWorkStart = serviceLocalTime.set(service_OBJECT),
serviceWorkEnd = serviceLocalTime.set(service_OBJECT).plus({hour: TOTAL_WORKING_HOURS});
// Convert back to user date with hours
const userWorkStart = serviceWorkStart.toLocal(),
userWorkEnd = serviceWorkEnd.toLocal(),
userWorkingHours = Interval.fromDateTimes(userWorkStart, userWorkEnd).divideEqually(TOTAL_WORKING_HOURS);
userWorkingHours.forEach(hour => {
if (hour.start.day < userSelectedDate.day) {
const dayUp = hour.start.plus({day: 1});
if (dayUp.toUTC().setZone(service_ZONE).weekday === 3 || dayUp.toUTC().setZone(service_ZONE).weekday === 7) {
// Day-offs are not added to the list
} else { hours.push(dayUp); }
} else {
if (hour.start.toUTC().setZone(service_ZONE).weekday === 3 || hour.start.toUTC().setZone(service_ZONE).weekday === 7) {
// Day-offs are not added to the list
} else { hours.push(hour.start); }
}
});
return hours.sort((a, b) => a.hour - b.hour);
}
The component's code was also refactored.
selectDate(e: MatDatepickerInputEvent<Date>) {
this.selectedDateHours = [];
if (this.bookForm.controls.date.invalid) {
this.bookForm.controls.hours.reset();
this.selectDisabled = true;
} else {
this.selectDisabled = false;
const dateInput = this.dtService.getHours(e.target.value);
dateInput.forEach(hour => {
if (this.table.some(booking => booking.hour === hour.toUTC().toISO())) {
this.selectedDateHours.push({hour: hour, booked: true});
} else {
this.selectedDateHours.push({hour: hour, booked: false});
}
});
}
}
If anybody has a more elegant solution, I'd be happy to know:)

Showing next available dates with 24h margin with Angular and MomentJS

I'm trying to display an array of possible delivery dates using AngularJS and MomentJS.
The issue is that it needs to meet certain conditions: Delivery dates are only Monday, Wednesday and Fridays.
Also, when the page loads, it recognizes the current date and it will only display the next available date that is minimum 24h away (e.g., if I load the page on a Sunday at 1pm, the first available date will be Wednesday, as Monday doesn't meet the 24h margin).
So far I could only think if dealing with the issue doing conditionals for every day of the week, but I'm pretty sure there has to be a neater way of dealing with it.
Here's what I did so far:
$scope.today = moment();
$scope.$watch('today', function () {
if ($scope.today = moment().day('Sunday')){
$scope.nextdateone = moment().add(3, 'd');
$scope.nextdatetwo = moment().add(5, 'd');
$scope.nextdatethree = moment().add(8, 'd');
$scope.nextdatefour = moment().add(10, 'd');
}
else if ($scope.today = moment().day('Monday')){
$scope.nextdateone = moment().add(2, 'd');
$scope.nextdatetwo = moment().add(4, 'd');
$scope.nextdatethree = moment().add(7, 'd');
$scope.nextdatefour = moment().add(9, 'd');
}
else if ...
});
This was the logic I came up with, but it doesn't really work as of now...
Any tips?
The delivery dates "Monday, Wednesday and Fridays", which (according to http://momentjs.com/docs/#/get-set/day/) you can represent as 1, 3 and 5.
So I would create a array with those dates, and then given the current day I would iterate that array of delivery dates to find the most suitable one... something like this:
const deliveryDates = [1, 3, 5];
const getDeliveryDate = (today) => {
let deliveryIndex = -1;
deliveryDates.some((date, index) => {
// If today is a delivery date, then schedule for the next delivery
if (today === date) {
deliveryIndex = index + 1;
return true;
}
// If today is before the current delivery date, store it
if (today < date) {
deliveryIndex = index;
return true;
}
});
// If delivery date is out of bounds, return the first delivery date
return deliveryIndex === deliveryDates.length || deliveryIndex === -1 ? 0 : deliveryIndex;
};
const getNextDelivery = (today) => {
return deliveryDates[getDeliveryDate(today)];
};
console.log(moment().day(getNextDelivery(moment().day())));
You can check a working example here:
https://jsbin.com/jawexafiji/edit?js,console

Categories