Mongodb stores date object in UTC. I need to query client entered tasks based on "today". Today varies from the user's perspective, who can be in Eastern time zone or Pacific. Here's the issue I ran into:
Record is entered as "2017-05-10 15:15:11.283Z". When I query the server later at night, the server date will be already 2017-05-11, however from the client's perspective it is still 2015-05-10.
I need to find all the tasks client entered "today".
I tried this:
let start = new Date();
start.setHours(0,0,0,0);
let end = new Date();
end.setHours(23,59,59,999);
But while that works during the day (same day), it does not show the records when the client queries at night, but before midnight.
Based on the comments below and my own brainstorming, I now pass client date to the server, and convert to UTC, like this:
let clientDate = req.params.month +'/'+ req.params.date +'/'+ req.params.year; //'5/10/2017'
console.log('clientDate ' + clientDate);
let start = new Date(clientDate);
start.setHours(0,0,0,0);
start = convertDateToUTC(start);
let end = new Date(clientDate);
end.setDate(end.getDate() + 1); //need this, otherwise queries next day
end.setHours(0,0,0,0);
end = convertDateToUTC(end);
console.log('start ' + start +' end ' + end);
return Taks.find({goalId: {$in: goalIds}, createdAt: {$gte: start, $lt: end}}).lean() //only today
//using function found on stack overflow
function convertDateToUTC(date) {
return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
}
It works correctly, it just seems hacky to supply client date to the server.
If I understand you correctly, dates are stored in the database strings with timezone offset +00:00. You want to get Date objects for the start and end of "today" in the client's time zone (whatever that might be).
The following assumes (possibly incorrectly) that the values in req.params are UTC, but are being treated as local on the host system doing the processing:
let clientDate = req.params.month +'/'+ req.params.date +'/'+ req.params.year; //'5/10/2017'
console.log('clientDate ' + clientDate);
let start = new Date(clientDate);
That is why your dates are out by the timezone offset and you have to shift them back.
You should not create a string then leave it up to the Date constructor to parse it. Give the values directly to the Date constructor.
Next, you seem to want create a Date for the start and end of the local day that the UTC date falls on, so the following should do that:
var req = {
params: {
year: '2017',
month: '5',
date: '10'
}
};
// No parsing, treat date values as UTC
var clientDate = new Date(Date.UTC(req.params.year, req.params.month - 1, req.params.date));
// Copy date
var start = new Date(clientDate);
// Set to local start of day
start.setHours(0,0,0,0);
// Copy start to end and add 24 hours
var end = new Date(start);
// Adjusting UTC hours avoids daylight saving issues
// Could also just add 1 to the date
end.setUTCHours(end.getUTCHours() + 24)
console.log('Client date: ' + clientDate.toISOString() +
'\nstart date : ' + start.toString() +
'\nend date : ' + end.toString());
For me, being east of Greenwich, the start of 10 May UTC falls on 10 May in my local timezone so I see start and end dates for 10 May. For users west of Greenwich, 10 May starts on 9 May, so they will see start and end dates for 9 May.
I hope that makes sense (and is what you were after).
If, on the other hand, the values in req.params are client values, you must know the client timezone offset in order to adjust them to the correct date.
Related
I'm trying to compare this string 8/26/2019 6:53:13 which is in EST to a new Date() object to simply see if it's in the past. This works fine locally but on deployment heroku's new Date comes through in UTC format. So I had to do this hack
if(process.env.NODE_ENV){
timeSubmitted.setHours(timeSubmitted.getHours() + 5); // HACK but does work
}
I tried to get todays date and time in UTC format as an object not a string so I can compare it to other times in UTC format.
var date = new Date('2014-02-27T10:00:00')
//Thu Feb 27 2014 10:00:00 GMT-0500 (Eastern Standard Time) //this is an object
let d = moment.utc(new Date()).format()
//Does convert right now to a UTC time string, but then when I try convert it to an object
new Date(d)
//it goes back to EST
All together this does work, but isn't ideal because of the hardcoded number 5.
//SET DATE RANGE
const startDate = new Date();//This gets converted from EST to UTC
startDate.setMinutes(startDate.getMinutes() - 2); //Google spreadsheets saves minutes a little in the past
//startDate = new Date(startDate.getTime() + startDate.getTimezoneOffset() * 60000);
const endDate = new Date();
endDate.setDate(endDate.getDate() + 5)
console.log('startDate ' ,startDate,'endDate ',endDate)
rows.forEach(row=>{
//row.timestamp looks like this '8/26/2019 6:53:13' in EST
var date= row.timestamp.split(" ")[0].split('/')
var time=row.timestamp.split(" ")[1].split(':')
var timeSubmitted = new Date(date[2], date[0] - 1, date[1], time[0], time[1], time[2]); //So this is in EST
//but then when deploying to heroku i had to do this hack.
if(process.env.NODE_ENV){
timeSubmitted.setHours(timeSubmitted.getHours() + 5); // HACK -- It's the only way I could get this time to be in UTC/timeSubmitted = new Date(timeSubmitted.getTime() + timeSubmitted.getTimezoneOffset() * 60000);
}
console.log('timeSubmitted',typeof timeSubmitted, typeof startDate, timeSubmitted, startDate, timeSubmitted >= startDate, timeSubmitted < endDate)
if(timeSubmitted >= startDate && timeSubmitted < endDate){ //Within the date range so check if the names are in the roster
slackers = slackers.filter(function(a){return a.fullname !== row.whatsyourname})
}
})
messageSlackers(slackers, id)
Timezones are just a factor taken into consideration when generating a human-readable string from a date.
But a date is a point in time, regardless of timezones. Time doesn't know about timezones! Humans invented timezones.
You're probably stringising your Date objects in some manner that uses your local timezone by default (didn't show us this code). This doesn't matter.
Comparing two Dates works. Always. You don't have to worry about some hidden timezone component ruining it. Just compare your dates and you'll see that it works fine.
tl;dr: Dates are not in a "format". Dates are dates.
I submit a date to nodejs server in the following format
2018-11-02T00:36:00+05:30 // Actual time is 12:36AM
When I check the document in the database (studio 3T) the format looks like
2018-11-01T19:06:00.000Z // minus 5.30 hours from the actual time 12:36AM
Now the situation is that i have to search all the documents by starting and ending of the day for any given date.
How can I pull all the documents for the date 2018-11-02
If I query to find the documents by start time and end time of 2018-11-02 For Example with moment js start and end day:
I get today as 2018-11-01T18:30:00.000Z and tomorrow as 2018-11-01T23:59:59.999Z
What I tried
const today = req.body.date;
const tomorrow = moment(today).endOf('day').toISOString();
"startDate" : { "$gte": new Date(today), "$lt": new Date(tomorrow) }
// today is 2018-11-01T18:30:00.000Z tomorrow is 2018-11-01T23:59:59.999Z
It gives an incorrect end date.
I have just add one day to the start day of the today in ISO Format
const today = moment(new Date(req.body.date)).startOf('day');
const tomorrow = moment(new Date(today)).add(1, 'days').toISOString();
"startDate" : { "$gte": new Date(today), "$lt": new Date(tomorrow) }
I have a Node.js server that triggers function based on timezones. Specifically, I'm using moment-timezone and from a fixed date and time input I need to trigger action at that same input but in different time zones.
So if I set in my server that the action should be triggered at 1:00 pm UK time and the user is in New York, I want the action to be triggered at 1:00 pm in New York.
That's what I am doing now:
exports.time_to_trigger = function(hour, date) {
var user_timezone = "Asia/Tokyo";
// create date object from date + hour strings
var dateObj = moment(date + hour, process.env.DATE_FORMAT + " HH:mm a");
// create offset
var max_value = moment(dateObj).add(3, 'minutes');
var low_value = moment(dateObj).add(-3, 'minutes');
console.log(max_value); // -> moment("2018-01-25T13:03:00.000")
console.log(low_value); // -> moment("2018-01-25T12:57:00.000")
// get the now value of the user timezone
var user_now = moment.tz(moment(), user_timezone);
console.log(user_now); // -> moment.parseZone("2018-01-24T13:01:00.038+09:00")
console.log(user_now.isAfter(low_value)); // -> false
console.log(user_now.isBefore(max_value)); // -> true
return (
user_now.isAfter(low_value) &&
user_now.isBefore(max_value)
)
}
As you can see from the comment, this is not working as the comparison with isAfter and isBefore take into consideration the time zone that I converted on purpose not to have this problem. How can I solve this?
Your issue is that you use timezone to get user_now but not to create dateObj. So dateObj is missing the timezone offset and your 2 dates are not comparable as you would wish.
To have all your dates on the same timezone:
// create date object from date + hour strings
var dateObj = moment.tz(date + hour, process.env.DATE_FORMAT + " HH:mm a", user_timezone);
Essentially I have two unix timestamps, representing the first and last days of a given month. Is it possible programmatically determine the timestamps for the first and last of the previous month?
For example, I have the following two timestamps:
1467331201 --> July 1, 2016
1469923201 --> July 31, 2016
Essentially, can I manipulate these two numbers in a consistent way in order to the unix time (or Date object) for June 1, 2016 and June 30, 2016, respectively? Problem that I'm running into is that you cannot simply subtract a given amount because the amount of days in a month is variable.
You could use this function:
function getPreviousMonthRange(unixTime) {
var dt = new Date(unixTime * 1000);
dt.setUTCDate(0); // flips to the last day of previous month
var unixLast = dt.getTime();
dt.setUTCDate(1); // back to the first day of that same month
var unixFirst = dt.getTime();
return [unixFirst / 1000, unixLast / 1000];
}
// given first and last date (only one is really needed)
var unixTimeFirst = 1467331201;
var unixTimeLast = 1469923201;
// get previous month's first & last date
var [first, last] = getPreviousMonthRange(unixTimeFirst);
// output
console.log('previous month first day: ', first, new Date(first*1000));
console.log('previous month last day: ', last, new Date(last*1000));
Take a look at the following example:
// Specify a timestamp
var timestamp = 1467331201;
// Create a date object for the time stamp, the object works with milliseconds so multiply by 1000
var date = new Date(timestamp * 1000);
// Set the date to the previous month, on the first day
date.setUTCMonth(date.getUTCMonth() - 1, 1);
// Explicitly set the time to 00:00:00
date.setUTCHours(0, 0, 0);
// Get the timestamp for the first day
var beginTimestamp = date.getTime() / 1000;
// Increase the month by one, and set the date to the last day of the previous month
date.setUTCMonth(date.getUTCMonth() + 1, 0);
// Explicitly set the time to 23:59:59
date.setUTCHours(23, 59, 59);
// Get the timestamp for the last day
var endTimestamp = date.getTime() / 1000;
// Print the results
console.log('Timestamps for previous month: ');
console.log('Begin timestamp: ' + beginTimestamp);
console.log('End timestamp: ' + endTimestamp);
A timestamp must be specified in the variable on the top, this might be one of the two timestamps you suggested in your question, anywhere in a month.
This code then calculates the begin and end timestamp for the previous month as you've requested, and prints the results to the console.
Please note, that in this example the begin timestamp uses 00:00:00 as time, and the end timestamp uses 23:59:59 as time (the last second of that day). This can be configured the way you'd prefer.
In this case, we're working with the ...UTC... Date functions, because a Unix timestamp is in UTC time, not in the timezone the user is in.
The statement date.setMonth(date.getMonth() + 1, 0); is used to select the last day in the month. The next month is selected first, but because the day is set to 0 (and not 1) one day is subtracted giving you the preferred result. This is described here.
You can consider using Moment.js. I'm sure this is not exactly how you'd end up handling it but see below for an example of some helpful methods.
var lastDayOfJuly = moment(1469923201);
var firstDayOfJuly = lastDayOfJuly.startOf('month');
var lastDayOfJune = firstDayOfJuly.subtract(1, 'day');
var firstDayOfJune = lastDayOfJune.startOf('month");
Moment.js
I am trying to get the current UTC date to store in my database. My local time is 9:11 p.m. This equates to 1:11 a.m. UTC. When I look in my database, I notice that 1:11 p.m. is getting written to. I'm confused. In order to get the UTC time in JavaScript, I'm using the following code:
var currentDate = new Date();
var utcDate = Date.UTC(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), currentDate.getHours(), currentDate.getMinutes(), currentDate.getSeconds(), currentDate.getMilliseconds());
var result = new Date(utcDate);
What am I doing wrong?
A lttle searching turned out you can do this:
var now = new Date(),
utcDate = new Date(
now.getUTCFullYear(),
now.getUTCMonth(),
now.getUTCDate(),
now.getUTCHours(),
now.getUTCMinutes(),
now.getUTCSeconds()
);
Even shorter:
var utcDate = new Date(new Date().toUTCString().substr(0, 25));
How do you convert a JavaScript date to UTC?
It is a commonly used way, instead of creating a ISO8601 string, to get date and time of UTC out. Because if you use a string, then you'll not be able to use every single native methods of Date(), and some people might use regex for that, which is slower than native ways.
But if you are storing it in some kind of database like localstorage, a ISO8601 string is recommended because it can also save timezone offsets, but in your case every date is turned into UTC, so timezone really does not matter.
If you want the UTC time of a local date object, use the UTC methods to get it. All javascript date objects are local dates.
var date = new Date(); // date object in local timezone
If you want the UTC time, you can try the implementation dependent toUTCString method:
var UTCstring = date.toUTCString();
but I wouldn't trust that. If you want an ISO8601 string (which most databases want) in UTC time then:
var isoDate = date.getUTCFullYear() + '-' +
addZ((date.getUTCMonth()) + 1) + '-' +
addZ(date.getUTCDate()) + 'T' +
addZ(date.getUTCHours()) + ':' +
addZ(date.getUTCMinutes()) + ':' +
addZ(date.getUTCSeconds()) + 'Z';
where the addZ function is:
function addZ(n) {
return (n<10? '0' : '') + n;
}
Modify to suit.
Edit
To adjust a local date object to display the same time as UTC, just add the timezone offset:
function adjustToUTC(d) {
d.setMinutes(d.getMinutes() + d.getTimezoneOffset());
return d;
}
alert(adjustToUTC(new Date())); // shows UTC time but will display local offset
Take care with the above. If you are say UTC+5hrs, then it will return a date object 5 hours earlier but still show "UTC+5"
A function to convert a UTC ISO8601 string to a local date object:
function fromUTCISOString(s) {
var b = s.split(/[-T:\.Z]/i);
var n= new Date(Date.UTC(b[0],b[1]-1,b[2],b[3],b[4],b[5]));
return n;
}
alert(fromUTCISOString('2012-05-21T14:32:12Z')); // local time displayed
var now = new Date();
var utc = new Date(now.getTime() + now.getTimezoneOffset() * 60000);