Timezone parsing inferno - javascript

I have this ISO date:
var v = '2013-07-09T13:27:29.000Z';
If I do this:
var g = moment(v).format();
Console gives me g value as the correct local time 2013-07-08T17:25:08-03:00
But if I do this to get the time ago:
console.log(moment(g).startOf('day').fromNow());
Moments calculates the time ago using original v ISO instead of the formatted g.
Why?
Edit: I have tested another time plugin (jquery.timeago) and the same error parsing to local time happens when passing the original v value.
What am I doing wrong here? This code is being executed on client side, so all plugins were supposed to return the correct local time for the user.
Edit 2: More relevant info on what I'm trying to do:
var v = notifs.ntime; //2013-07-09T13:27:29.000Z
var m = moment(v).zone(v).format(); // 2013-07-09T13:27:29+00:00 (wrong local)
var m2 = moment(v).format(); // 2013-07-09 10:27:29 (correct local)
var r = moment(v).zone(v).startOf('day').fromNow() // 21 hours ago (wrong)
var r2 = moment(v).startOf('day').fromNow() // 18 hours ago (wrong)
console.log(v);
console.log(m);
console.log(m2);
console.log(r);
console.log(r2);
console.log('-------------------');
/*
2013-07-09T13:27:29.000Z
2013-07-09T13:27:29+00:00
2013-07-09T10:27:29-03:00
21 hours ago
18 hours ago
-------------------
I expected "8 hours ago" as now is 18:10 and (v) was set on 10:27 (m2)
*/

I'm not quite sure I follow what you're looking for.
By using your g variable, you are essentially doing this:
moment(moment(v).format()).startOf('day').fromNow()
That is a bit redundant. It is the exact same thing as this:
moment(v).startOf('day').fromNow()
That will tell you how much time has passed between now and the start of the local day that v fell on.
If you are looking for how much time as passed between now and the start of the UTC day that v fell on, then use this instead:
moment.utc(v).startOf('day').fromNow()
Or using a slightly different syntax, but identical in effect:
moment(v).utc().startOf('day').fromNow()
If you were looking for something else, please clarify.
Walkthrough
Let's walk through what happens when I run your original code on my computer. I am in Arizona, USA, which is at UTC-7 with no daylight saving time.
// we'll start with the string you provided
var v = '2013-07-09T13:27:29.000Z';
// and the redundant moment, but we'll take a look at the formatting
var g = moment(v).format();
console.log(g); // 2013-07-09T06:27:29-07:00
console.log(moment(g).format()) // 2013-07-09T06:27:29-07:00 (it didn't change)
// start of the local day is midnight in the local time zone
console.log(moment(g).startOf('day').format()) // 2013-07-09T00:00:00-07:00
// here's my time right now
console.log(moment().format()) // 2013-07-09T13:56:54-07:00
// and your final bit of code
console.log(moment(g).startOf('day').fromNow()); // 14 hours ago
As you can see it is performing correctly, using the local time zone when determining the start of day. Of course, when you run this in a different time zone, it is going to return different results. That's because every time zone has it's own concept of each day. If you wanted consistent output, then you would remove the startOf('day') call.
Recommendation
After your last update stating that you expected 8 hours, then it seems you didn't really want the time from the startOf('day') at all. Just do this:
moment(v).fromNow()
There's no need to involve the .zone() either.

Related

How to get time range of todays date with timezone in javascript and use it in mongo query

I want to run a query in mongo which will get data till 7:00 am, then 7 am to 11 am and then 12 pm to 4 pm of particular that day only. Now these time are stored in different timezone. So I want to know how get data from mongo.
I tried using momentJs to get the the time range but not able to get correct timezone.
var dayStart = moment().zone(timezone).startOf('day');
var now = moment().zone(timezone);
var duration = moment.duration(now - dayEnd);
and my mongo query is
db.CollectionName.find({"created_at": {
'$gte': dayStart,
'$lt': now
}});
I want the three result seperately that is till 7am, 7am-11am & 12pm-4pm. Any help please.
I would still advise against using moment().zone() - at the very least use moment().utcOffset() as it more clearly communicates the approach of the function. However, since you are using .zone() now I will share an example to get you started.
We assume the following:
when a record is saved to your database, the created_at value defaults to UTC
you are the only user of concern
you are located in the Kolkata timezone and when you say 7am, you expect it to be relative to that location on earth
Using the zone feature, you would then need to a value of -330 to translate from UTC to Kolkata. (Since Kolkata is UTC+5:30, we must convert to minutes and then subtract that value 5 * 60 + 30 = 330.)
Given all of that, your values will be as follows:
var dayStart = moment().utc().zone(-330).startOf('day');
var now = moment().utc().zone(-330);
var duration = moment.duration(now.diff(dayStart)).as('minutes');
you had a few typos in your example: dayEnd was an undefined variable, I assumed you meant to use dayStart. also, duration needs to use diff and an argument to format the output, I have used minutes for this example.
You can test further and play around with this example:
https://jsfiddle.net/dusthaines/48wubezy/5/

Incorrect time while copy pasting a date time value using google apps script [duplicate]

I'm trying to get from a time formatted Cell (hh:mm:ss) the hour value, the values can be bigger 24:00:00 for example 20000:00:00 should give 20000:
Table:
if your read the Value of E1:
var total = sheet.getRange("E1").getValue();
Logger.log(total);
The result is:
Sat Apr 12 07:09:21 GMT+00:09 1902
Now I've tried to convert it to a Date object and get the Unix time stamp of it:
var date = new Date(total);
var milsec = date.getTime();
Logger.log(Utilities.formatString("%11.6f",milsec));
var hours = milsec / 1000 / 60 / 60;
Logger.log(hours)
1374127872020.000000
381702.1866722222
The question is how to get the correct value of 20000 ?
Expanding on what Serge did, I wrote some functions that should be a bit easier to read and take into account timezone differences between the spreadsheet and the script.
function getValueAsSeconds(range) {
var value = range.getValue();
// Get the date value in the spreadsheet's timezone.
var spreadsheetTimezone = range.getSheet().getParent().getSpreadsheetTimeZone();
var dateString = Utilities.formatDate(value, spreadsheetTimezone,
'EEE, d MMM yyyy HH:mm:ss');
var date = new Date(dateString);
// Initialize the date of the epoch.
var epoch = new Date('Dec 30, 1899 00:00:00');
// Calculate the number of milliseconds between the epoch and the value.
var diff = date.getTime() - epoch.getTime();
// Convert the milliseconds to seconds and return.
return Math.round(diff / 1000);
}
function getValueAsMinutes(range) {
return getValueAsSeconds(range) / 60;
}
function getValueAsHours(range) {
return getValueAsMinutes(range) / 60;
}
You can use these functions like so:
var range = SpreadsheetApp.getActiveSheet().getRange('A1');
Logger.log(getValueAsHours(range));
Needless to say, this is a lot of work to get the number of hours from a range. Please star Issue 402 which is a feature request to have the ability to get the literal string value from a cell.
There are two new functions getDisplayValue() and getDisplayValues() that returns the datetime or anything exactly the way it looks to you on a Spreadsheet. Check out the documentation here
The value you see (Sat Apr 12 07:09:21 GMT+00:09 1902) is the equivalent date in Javascript standard time that is 20000 hours later than ref date.
you should simply remove the spreadsheet reference value from your result to get what you want.
This code does the trick :
function getHours(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var cellValue = sh.getRange('E1').getValue();
var eqDate = new Date(cellValue);// this is the date object corresponding to your cell value in JS standard
Logger.log('Cell Date in JS format '+eqDate)
Logger.log('ref date in JS '+new Date(0,0,0,0,0,0));
var testOnZero = eqDate.getTime();Logger.log('Use this with a cell value = 0 to check the value to use in the next line of code '+testOnZero);
var hours = (eqDate.getTime()+ 2.2091616E12 )/3600000 ; // getTime retrieves the value in milliseconds, 2.2091616E12 is the difference between javascript ref and spreadsheet ref.
Logger.log('Value in hours with offset correction : '+hours); // show result in hours (obtained by dividing by 3600000)
}
note : this code gets only hours , if your going to have minutes and/or seconds then it should be developped to handle that too... let us know if you need it.
EDIT : a word of explanation...
Spreadsheets use a reference date of 12/30/1899 while Javascript is using 01/01/1970, that means there is a difference of 25568 days between both references. All this assuming we use the same time zone in both systems. When we convert a date value in a spreadsheet to a javascript date object the GAS engine automatically adds the difference to keep consistency between dates.
In this case we don't want to know the real date of something but rather an absolute hours value, ie a "duration", so we need to remove the 25568 day offset. This is done using the getTime() method that returns milliseconds counted from the JS reference date, the only thing we have to know is the value in milliseconds of the spreadsheet reference date and substract this value from the actual date object. Then a bit of maths to get hours instead of milliseconds and we're done.
I know this seems a bit complicated and I'm not sure my attempt to explain will really clarify the question but it's always worth trying isn't it ?
Anyway the result is what we needed as long as (as stated in the comments) one adjust the offset value according to the time zone settings of the spreadsheet. It would of course be possible to let the script handle that automatically but it would have make the script more complex, not sure it's really necessary.
For simple spreadsheets you may be able to change your spreadsheet timezone to GMT without daylight saving and use this short conversion function:
function durationToSeconds(value) {
var timezoneName = SpreadsheetApp.getActive().getSpreadsheetTimeZone();
if (timezoneName != "Etc/GMT") {
throw new Error("Timezone must be GMT to handle time durations, found " + timezoneName);
}
return (Number(value) + 2209161600000) / 1000;
}
Eric Koleda's answer is in many ways more general. I wrote this while trying to understand how it handles the corner cases with the spreadsheet timezone, browser timezone and the timezone changes in 1900 in Alaska and Stockholm.
Make a cell somewhere with a duration value of "00:00:00". This cell will be used as a reference. Could be a hidden cell, or a cell in a different sheet with config values. E.g. as below:
then write a function with two parameters - 1) value you want to process, and 2) reference value of "00:00:00". E.g.:
function gethours(val, ref) {
let dv = new Date(val)
let dr = new Date(ref)
return (dv.getTime() - dr.getTime())/(1000*60*60)
}
Since whatever Sheets are doing with the Duration type is exactly the same for both, we can now convert them to Dates and subtract, which gives correct value. In the code example above I used .getTime() which gives number of milliseconds since Jan 1, 1970, ... .
If we tried to compute what is exactly happening to the value, and make corrections, code gets too complicated.
One caveat: if the number of hours is very large say 200,000:00:00 there is substantial fractional value showing up since days/years are not exactly 24hrs/365days (? speculating here). Specifically, 200000:00:00 gives 200,000.16 as a result.

momentjs days difference between two GMT dates

I'm trying to get difference of days between two GMT dates using moment
but I couldn't find it.
I'm on IST(+05:30) and I have some GMT dates(-05:00) in db,
I tried using following command
temp2.diff(temp1, "days")
here is a screenshot of all the commands tried in console
there we can clears see that dates are different and still shows the difference is 0
here is how I'm initializing moment objects of 'America/New_York'
var temp1 = moment.tz(new Date('Mon Jan 25 2016 22:00:00 GMT-0600'), 'America/New_York');
var temp2 = moment.tz(new Date('Tue Jan 26 2016 00:00:00 GMT-0600'), 'America/New_York');
any help appreaciated, thanks.
Well, there is less than 24 hours difference between those dates, so it's correct. The documentation says:
By default, moment#diff will return number rounded down. If you want the floating point number, pass true as the third argument.
> temp2.diff(temp1, "days", true)
0.08333333333333333
If you don't care about the hours at all, set them to 0 before you do the comparison
> temp2.hours(0).diff(temp1.hours(0), "days")
1
A few things:
You say that you are retrieving these values from a database, but then you show us loading them via the Date constructor from a string value. If you are really storing a string in your database, especially in that particular format, then you have much larger problems than the one you asked about! Please show us precisely how you load the values from your database to begin with.
You shouldn't rely on the Date object for parsing, especially when you are already using moment, which has much better parsing routines of its own.
You said these values where in America/New_York, but then you show an offset of -0600. That's never used in that time zone. The offset for the value you showed would be -0500.
You also said "I have some GMT dates(-05:00)" - which doesn't make any sense. GMT is +00:00. GMT-0500 means "5 hours behind GMT". Thus, you no longer have a "GMT date".
Be aware that the JavaScript Date object can only use the time zone of where the code is running. You cannot run it in any other time zone.
While Felix is correct in how you can show decimals with the diff function, you should realize that diff is giving you the actual elapsed time between the two moments in time you asked about. However, you seem to be wanting to know the total number of calendar days separating the two days that the moments fall into within the named time zone. To do that, you'd need to ignore the time portion. Using startOf('day') is an easy way to do that. Consider:
var a = moment.parseZone("2016-01-25T23:00:00-05:00");
var b = moment.parseZone("2016-01-26T01:00:00-05:00");
b.diff(a, 'days', true) // 0.08333333333333333 (not what you want)
b.startOf('day').diff(a.startOf('day'), 'days') // 1 (that's better!)
moment(b).startOf('day').diff(moment(a).startOf('day'),'days') // 1 (best approach)
Note a few things with this code:
The code in the last line is the best approach, as it leaves the original values of a and b alone. Otherwise, they would be modified. (Moments are mutable.)
You seem to already have the correct local time and offset, and thus there's no need to use moment-timezone's tz function. You can just use parseZone. Of course if this was just a side effect of your example, then you could still use moment-timezone, but I'd strongly recommend against using the Date constructor still.

Why is local time not different from UTC in moment.js?

I have the following code that makes use of moment.js:
var Now = moment();
var UTC = moment().utc();
if (moment().isBefore(UTC)){
$("#was").html("Time difference : " + Now.from(UTC)).fadeIn('fast');
} else {
$("#was").html("Time difference : " + UTC.fromNow()).fadeIn('fast');
}
Result is always: "A few seconds ago". Can you tell me what I am doing wrong?
Although Now and UTC will display differently, they are the same "moment in time". To understand this you have to grasp how moment.js works internally. Here is some info from the official moment.js documentation (emphasis mine):
By default, moment parses and displays in local time.
If you want to parse or display a moment in UTC, you can use moment.utc() instead of moment().
So the difference is all in parsing and displaying. Internally, moment objects have the same timestamp. A little test to demonstrate this is to append (and run) the followin after your code:
console.log(Now.valueOf());
console.log(UTC.valueOf());
console.log(Now.valueOf() - UTC.valueOf()); // will be "a few secods" at most ;)
Update: If your intention was to create a moment of, say, 5 hours ago, then:
var hours_ago = 5;
var earlier = moment().subtract('hours', hours_ago); // 5 hours ago
var earlier_yet = moment().subtract({'days': 2, 'hours': 3}) // 2 days, 3 hours ago

Momentjs Grabbing today's date and setting time causes it to fast-forward 24 hours

I'm trying to time out email messages based on user preferences. My morning calculations are working correctly but it's the evening emails that are never getting sent because dates aren't behaving as expected.
First, here's the code I'm using to grab the time and perform adjustments based on user location, etc.
var time = moment();
var machineTZ = time.zone();
var userTZ = 420;
var diffTZ = userTZ - machineTZ;
var oneHour = moment(time).add('minutes', 60);
var morningRun = moment().startOf('day');
morningRun.hour(7).minute(0);
morningRun.add('minutes', diffTZ);
var eveningRun = moment().startOf('day');
eveningRun.hour(19).minute(30);
eveningRun.add('minutes', diffTZ);
I'm checking every hour to see if it's time to schedule another email to go out. Right now this is hard-coded, but when I begin to add user preferences they'll be able to select their local time they'd like things to go out at.
I've been debugging my values to troubleshoot. Here's output from a job that ran early morning (from the server's perspective):
lastRun: 2013-10-12T00:06:55.088Z (this one is being run at 1 am)
morningRun: 2013-10-11T14:00:00.000Z
eveningRun: 2013-10-12T02:30:00.000Z
The run numbers are as I would expect them to be. In two hours time I want the evening email to go out (7:30pm my time = 2:30am the following day server-time).
Looking again an hour later we see:
lastRun: 2013-10-12T01:06:58.267Z (this one is at 2 am)
morningRun: 2013-10-12T14:00:00.000Z
eveningRun: 2013-10-13T02:30:00.000Z <---- what?
All of a sudden my calculation for my evening has flipped over the date line, even though it's still 10/12 (not 10/13 yet). Because of this my check to see if I should schedule the email fails since it now thinks I need to send the email in 24 hours, not 30 minutes.
Been battling with this weird inconsistency for a while, I thought I had figured out why it was doing it and adjusted my calculations using the time zone stuff above but that didn't do the trick :(. This certainly seems like some sort of weird bug as I would expect this to be happening:
//Today is 10/12
var eveningRun = moment().startOf('day'); //10/12/2013 - 00:00:00
eveningRun.hour(19).minute(30); //10/12/2013 - 19:30
eveningRun.add('minutes', diffTZ); //10/13/2013 - 2:30 am
This works until at some point it decides that "today" is actually 10/13 and sets the evening run is to take place on 10/14 instead. Help is greatly appreciated, or if this is a bug would love to know what I can do to work around this issue until it's resolved.
There's no need to calculate machineTZ, diffTZ or add any minutes. Just do this instead:
moment().zone(userTZ).startOf('day').hour(7).minute(0)
But do keep in mind that a value such as 420 is not a time zone, it's a time zone offset. It only tells you what the offset is for a particular point in time. Since you are applying it unilaterally, you may get incorrect results during daylight saving time.
Instead, you should try using the moment-timezone addon, and do something like this instead:
moment().tz("America/Los_Angeles").startOf('day').hour(7).minute(0)
See also the timezone tag wiki, in particular the sections titled "Time Zone != Offset" and "The IANA/Olson Time Zone Database".

Categories