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;
}
Related
I need to add 4 hours to my moment js date. So for that i am using
/* this timestamp is this date 27-03-2045 00:00 */
const someday = moment(2374178400000);
const addedFourHours = someday.add(4, 'hours');
on 27 March the DST is already passed and i get exactly 4 hours added and the end date in addedFoursHours is Mon Mar 27 2045 04:00:00 GMT+0200.
But when i try date when DST is happening for example 26 March on midnight
/* this timestamp is this date 26-03-2045 00:00 */
const someday = moment(2374095600000);
const addedFourHours = someday.add(4, 'hours');
then i get Sun Mar 26 2045 05:00:00 GMT+0200. In previous case i got 04:00 time when i added 4 hours after midnight. Why in DST time i get 05:00 time ?
How can i solve this ?
You want to add 3, 4 or 5 hours depending on the date, but you want to set specific values for the hours. Read the hour value, add 4 and set the value:
const today = moment(2374178400000);
const four = moment(today).hours(today.hour() + 4);
const eight = moment(four).hours(four.hour() + 4);
const twelve = moment(eight).hours(eight.hour() + 4);
const sixteen = moment(twelve).hours(twelve.hour() + 4);
const twenty = moment(sixteen).hours(sixteen.hour() + 4);
const tomorrow = moment(twenty).hours(twenty.hour() + 4);
console.log(today);
console.log(four);
console.log(eight);
console.log(twelve);
console.log(sixteen);
console.log(twenty);
console.log(tomorrow);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.3/moment.min.js"></script>
What you want is then actually not adding 4 hours, but finding the next time where the hours are a multiple of 4.
So, remove all suggestion from your code that you are adding four hours (cf. your variable name).
You can do this:
const someday = moment(2374095600000);
console.log(someday.toString());
const hours = someday.hours();
someday.hours(hours + 4 - (hours % 4)); // Find next time that is multiple of 4.
console.log(someday.toString());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.3/moment.min.js"></script>
const cookieButton = document.getElementById('store-cookie')
cookieButton.addEventListener('click', e => {
const input = document.getElementById('fav-cookie').value
let date = new Date()
let minutes = 30;
date.setTime(date.getTime() + (minutes * 60 * 1000))
document.cookie = `favCookie=${input}; expires=${date.toTimeString()};`
})
I'm working on a coding problem meant to be only used in javaScript. It requires to make a cookie with the value of an input field (on a linked html), on pressing a button. The bonus for this question requires the cookie to expire after 30 minutes it's created. Currently, this code is just saving the
favCookie=input;
but it is not adding an expiration date. Any help would be appreciated!
Use toUTCString() instead.
const cookieButton = document.getElementById('store-cookie');
cookieButton.addEventListener('click', e => {
const input = document.getElementById('fav-cookie').value;
const date = new Date();
date.setTime(date.getTime() + (30* 60 * 1000));
document.cookie = `favCookie=${input}; expires=${date.toUTCString()};`;
});
Cookies are always tricky. I believe it is because your timestamp should be formatted like so:
2021-10-23T20:32:38.000Z
Instead you currently have set by toTimeString()
22:29:12 GMT+0200 (Central European Summer Time)
You could create 2 date variables, on the second use the setMinutes method to add 30mins to the current time then create a function to clear/change the value if and when the current date/time equals the new date.
var expire = new Date();
expire.setMinutes( expire.getMinutes() + 30 );
I have this below functionin my webapp to check if from OPEN to CLOSE time has elapse using a country timezone and it's working fine. Am trying to optimize my website, so my question is how can i make a function like this in javascript without the use of moment timezone? The moment timezone file is large and this is the only usage of it on my website.
function isOpen(openTime, closeTime, timezone) {
// handle special case
if (openTime === "24HR") {
return "open";
}
// get the current date and time in the given time zone
const now = moment.tz(timezone);
// Get the exact open and close times on that date in the given time zone
const date = now.format("YYYY-MM-DD");
const storeOpenTime = moment.tz(date + ' ' + openTime, "YYYY-MM-DD h:mmA", timezone);
const storeCloseTime = moment.tz(date + ' ' + closeTime, "YYYY-MM-DD h:mmA", timezone);
let check;
if (storeCloseTime.isBefore(storeOpenTime)) {
// Handle ranges that span over midnight
check = now.isAfter(storeOpenTime) || now.isBefore(storeCloseTime);
} else {
// Normal range check using an inclusive start time and exclusive end time
check = now.isBetween(storeOpenTime, storeCloseTime, null, '[)');
}
return check ? "open" : "closed";
}
const zone = "Asia/Kuala_Lumpur";
console.log("24HR", isOpen("24HR", undefined, zone));
console.log("2:00AM-8:00AM", isOpen("2:00AM", "8:00AM", zone));
console.log("8:00AM-10:00AM", isOpen("8:00AM", "10:00PM", zone));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.27/moment-timezone-with-data-10-year-range.min.js"></script>
You can use the Intl.DateTimeFormat constructor to do the same thing. As I understand it, you want to know if a store that is open say from 8:00 to 12:00 in say Asia/Kuala_Lumpur is currently open.
It's probably possible to convert your code more or less line by line, but I've just refactored it and simplified the logic (call me lazy…). This way it gets the current time in the desired location, converts it to minutes since midnight, then sees if that is before the start time or on or after the closing time.
For midnight (12:00 AM), the time converts to 0 minutes so if closingTime is 0, it's assumed to be end of day so is set to 1,440 (i.e. midnight at end of day).
The test times only work on the same day, if the open time runs over midnight you'll need to refactor it. I've just tested midnight to noon and noon to midnight so one should always show "open" and the other "closed".
You might also consider using Luxon, it does what moment.js + moment.tz does but uses the Intl object instead of included data.
Edit
To deal with times that go over midnight, you can either include dates in the time (not convenient if you want to use a regular daily schedule) or you can have an "inside" and "outside" test so that if closing time is before the open time, you test if the time is not between open and close times. That can be done by comparing openMin and close times and adjusting the test.
This will not deal with overlapping start and end times, but that doesn't really fit a regular daily schedule (though it might fit a weekly or longer schedule).
/* #param {string} location: IANA representative location
** #param {Date} date: date instance to get time from, default is now
** #returns {string} time in location in h:mm ap format
*/
function getTime(location, date = new Date()) {
return date.toLocaleString('en', {
timeZone: location,
hour : 'numeric',
minute: '2-digit',
dayPeriod: 'short'
});
}
/* #param {string} time: h:mm A
** #returns {number} time converted to minutes
*/
function timeToMin(time) {
let [h, m] = time.match(/\d\d?/g);
h = h%12;
if (/pm$/i.test(time)) h +=12
return h * 60 + parseInt(m);
}
/* #param {string} openTime: opening time in h:mm ap format
** #param {string} closeTime: closing time in h:mm ap format
** #param {string} location: IANA representative location
** #return {string} open if current time is within openTime and closeTime in location,
** closed otherwise
*/
function isOpen(openTime, closeTime, location) {
if (openTime == '24HR') return 'open';
let nowTime = getTime(location);
let nowMin = timeToMin(nowTime);
let openMin = timeToMin(openTime);
let closeMin = timeToMin(closeTime) || 1440;
// Open and close on same day
if (openMin < closeMin) {
return nowMin < openMin || nowMin >= closeMin ? 'closed' : 'open';
// Close on day after open
} else {
return nowMin >= openMin && nowMin < closeMin ? 'open' : 'closed';
}
}
// Time in KL
let loc = "Asia/Kuala_Lumpur";
console.log(`In ${loc} it's ${getTime(loc)}`);
// Examples
[["24HR", undefined, loc], // Open 24 hrs
["12:00AM", "12:00PM", loc], // Midnight to noon
["12:00PM", "12:00AM", loc], // Noon to midnight
["6:30PM", "04:00AM", loc], // Over midnight
].forEach(args => console.log(
`${args[0]}${args[1]? '-' + args[1] : ''} ${isOpen(...args)}`
));
I am using a formatted date string (YYYYMMDD) to determine if certain content should be provided to a user. When I deploy and run my function for/from firebase, it reports the correct date early in the day (when I generally do my development...animal I know) and it incorrectly reports late at night.
I guess that it is using GMT and not EDT but it should at least be based on the server location (us-central1) or even where the user is (Eastern) but shouldn't be off by over 2 hours, right?
Here is a boiled-down version of the code...
exports.manageQuestionList = functions.firestore.document('/users/{userId}/questions/{questionId}').onUpdate(async (change, context) => {
// Prepare a number that represents today's date
let theDate = new Date()
theDate.setDate(theDate.getDate())
let yearString = String(theDate.getFullYear())
let monthString = String(theDate.getMonth()+1)
if (monthString.length === 1) {
monthString = '0' + monthString
}
let dayString = String(theDate.getDate())
if (dayString.length === 1) {
dayString = '0' + dayString
}
let dayID = yearString + monthString + dayString
console.log(dayID)
}
Right now it is 22:41 in the Eastern Time zone on July 29 in 2019... The result is expected to be 20190729. This gives a result to the log of 20190730 however.
Edit: Thanks for the help! Is there somewhere in the documents I might have figured this out on my own? I'll have to test this again tonight but when I run from nearly identical code on the client-side (shown below) it returns one date/time and when I run on a function, it is the next day (late at night).
export const getDayID = (addDays) => {
if (!addDays) {
addDays = 0
}
// Prepare a number that represents today's date
let theDate = new Date()
theDate.setDate(theDate.getDate() + addDays)
let yearString = String(theDate.getFullYear())
let monthString = String(theDate.getMonth()+1)
if (monthString.length === 1) {
monthString = '0' + monthString
}
let dayString = String(theDate.getDate())
if (dayString.length === 1) {
dayString = '0' + dayString
}
let dayID = yearString + monthString + dayString
return dayID
}
The date is almost certainly working correctly. If the system's sense of date was off, Google Cloud services would be extremely unreliable, as nothing would be able to synchronize correctly.
Instead of depending on the system's sense of timezone, or speculating about what it ought to be, you should instead apply your own formatting rules based on a timezone of your explicit choice. This means you should be using a library such as moment.js along with its companion library moment timezone to take full control of that.
I'm trying to convert date/time into local time, instead of UTC.
I'm getting the date and time from an API and this is giving it on UTC. So I need to convert it from UTC to local time, because after 6:00 pm it displays the date for the next day.
I've tried moment and 'toString', and it works on localhost but if I host it it still gives me UTC
let dateTesting = subscription.current_period_started_at._
let dateTesting2 = moment.utc(dateTesting).local().format('MM-DD- YYYY HH:mm:ss')
let dateTesting3 = dateTesting.toString("MM/dd/yyyy HH:mm")
console.log('- now in UTC', dateTesting)
console.log('dateTesting2 -------', dateTesting2)
console.log('without moment', dateTesting3)
The result from the code above is:
now in UTC 2019-01-09T17:16:25Z
dateTesting2 ------- 01-09-2019 17:16:25
without moment 2019-01-09T17:16:25Z
I want the date to appear the same as in my computer (-6 hours)
You could force the issue with something like
`moment.utc(dateTesting).local().format('MM-DD-YYYY HH:mm:ss').subtract(6, 'hours)`
If you know your TIME_ZONE (sure), you can use:
const TIME_ZONE = -6;
const now = new Date(dateTesting);
now.setHours(now.getHours + TIME_ZONE) // or now.setMinutes(now.getMinutes()+ 60 * TIME_ZONE))
// because sometime TIME_ZONE is not `int`, example 4.5, 5.5, v.v,
//use with variable now, it's time same your time
If you run anywhere, you can get UTC time and convert it to your time, or you can use getTimezoneOffset:
const TIME_OFFSET = 360; // TIME_OFFSET = TIME_ZONE * 60
const now = new Date(time_of_any_where);
const thatOffset = now.getTimezoneOffset();
now.setMinutes(now.getMinutes+ thatOffset - TIME_OFFSET)
// Do something with `now`