In pure JS, this would be how.
How can I find out the number of minutes since midnight for a given moment object (without extracting to Date)?
Must take into account DSTs
Minutes should be rounded
Must work with local time (not convert to UTC)
// Your moment
var mmt = moment();
// Your moment at midnight
var mmtMidnight = mmt.clone().startOf('day');
// Difference in minutes
var diffMinutes = mmt.diff(mmtMidnight, 'minutes');
By default, moment#diff will return number rounded down. If you want the floating point number, pass true as the third argument. Before 2.0.0, moment#diff returned rounded number, not a rounded down number.
Consider this pseudocode because I haven't test to see if the difference takes into account DST.
This is what I have at the moment:
if (!moment.isMoment(mmt)) {
return 0;
}
var hh = mmt.get('hour');
var mm = mmt.get('minute');
return hh*60 + mm;
I am not sure if it takes into account various edge cases; comment if this is the case, or provide an alternate answer.
Related
Let us consider following example
const date = new Date(0);
date.setSeconds(60*60*24-1);
console.log(date.toISOString().substr(11, 8));
outputs
23:59:59
I am searching for elegant way to have
const date = new Date(0);
date.setSeconds(60*60*24+1);
console.log(date.toISOString().substr(11, 8));
output
24:00:01
instead of
00:00:01
by elegant I mean without implementing my own Date object... Maybe it would be possible to set a custom length of the day? Increase from 24h to 99h?
You can set the hour cycle in the hour cycle option (hc) and the language parameter available in the Intl.DateTimeFormat constructor and toLocaleTimeString, e.g.
console.log(
new Date(2020,7,1,0,5).toLocaleTimeString('en-CA-u-hc-h24')
);
Whether the hour cycle setting "works" or not seems to depend on the language chosen and how it's set, so maybe not that reliable. Test thoroughly in different implementations.
Many thanks to #RobG who understood my question. The hour cycle is what I needed, unfortunately according to the docs it is restricted to values h11, h12, h23, h24 and I would need h99 which is not available.
Eventually I had to make my own ugly solution as it appears such a use case was not predicted in the standard, so here it is
function unfortunatelyWeHadToWriteIt(nbsec) {
// compute numerical values
const nbhours = Math.floor(nbsec/(60*60));
const nbminutes = Math.floor((nbsec - nbhours*60*60)/60)
const nbseconds = Math.floor(nbsec - nbhours*60*60 - nbminutes*60);
// convert them to padded strings
const hours = String(nbhours).padStart(2, '0');
const minutes = String(nbminutes).padStart(2, '0');
const seconds = String(nbseconds).padStart(2, '0');
// format
return `${hours}:${minutes}:${seconds}`;
}
so let's compare it to the Date formatting
// 27 hours, 13 minutes and 6 seconds
const nbsec = 60*60*27+13*60+6;
what Date will give us
const date = new Date(0);
date.setSeconds(nbsec);
console.log(date.toISOString().substr(11, 8));
outputs 03:13:06, it overflows at value of 24 hours. Now let's apply the unfortunatelyWeHadToWriteIt function
console.log(unfortunatelyWeHadToWriteIt(nbsec))
outputs 27:13:06 which does not overflow.
Just to give you guys some context, I am playing games and displaying the playtime. It is more convenient to show number of hours than number of days of gameplay...
I am trying to find time difference in hours between two time but somehow its not correct. I might be doing something wrong but can't figure out where. The time difference I want is from a certain date with any time to some end date at 00:00:00
The following is how I find the difference:
async function getSubscriptionValidity(org_id) {
// Get expiry date of the organisation
let validity = await Customers.findOne({ orgId: org_id }, "expiresOn -_id");
let now = moment();
let expiryDate = moment(validity.expiresOn).startOf("day");
// Calculate time remaining to expiry in hours
let difference = expiryDate.diff(now, "hours");
console.log(now);
console.log(expiryDate);
console.log(difference);
}
Result
moment("2019-04-09T21:30:43.579") // Start Date and time
moment("2019-04-10T00:00:00.000") // End date and time
2 // Shouldn't this be 3 ?
By default, moment#diff will truncate the result to zero decimal places, returning an integer. If you want a floating point number, pass true as the third argument.
https://momentjs.com/docs/#/displaying/difference/
In javascript application, I don't have Dates but only timings of a day in 24hrs format. How can I properly find the difference between them?
All my google search results are giving Date Time difference calculations only.
For example, If I want to find the difference between (4.15pm to 2.45 pm),
In my code I have,
var startTime = "14.45"; var endTime = "16.15";
Now var diffTime = endTime - startTime, which should give 1.30.
To get clarity on my question, you can refer my related question SO, to understand what I am trying to achieve.
Convert your string into the smallest unit required, which is minutes in your case. Do the arithmetic to get the result in minutes. Again do arithmetic to find hours and minutes from the result. You can also add logic to check if hours is zero and change to 24 in that case. But as comments point out, bare times cannot be compared if not of the same date.
function getMinutes(timeString){
var hours = parseInt(timeString.split('.')[0], 10);
hours = hours === 0? 24 : hours;
var minutes = parseInt(timeString.split('.')[1], 10);
var totalMinutes = (hours * 60) + minutes;
return totalMinutes;
}
var startTime = "23.45";
var endTime = "0.15";
var differenceInMinutes = getMinutes(endTime) - getMinutes(startTime);
var resultString = Math.floor(differenceInMinutes/60) + "." + (differenceInMinutes%60);
Another way can be by creating dummy dates using date constructor and keeping date part as some arbitrary date (same for end and start) and do common date comparison methods.
You seem to have 2 string variables in javascript. So talking about times and dates is really too early. They pretty much look like floating point numbers but if this was the case then the only arithmetic you could apply to floating point numbers is floating point arithmetic (+, -, *, /). So the first step for you would be to parse those string variables to the corresponding floating point numbers:
var startTime = '14.45';
var endTime = '16.15';
var startTimeFloat = parseFloat(startTime);
var endTimeFloat = parseFloat(endTime);
if (isNaN(startTimeFloat) || isNaN(endTimeFloat)) {
alert('Please input valid floating point numbers before being able to do any arithmetic on them');
} else {
var difference = endTimeFloat - startTimeFloat;
// Obviously here you should take into account the case where
// the start number is bigger than the end number in which case
// you would get a negative value for the difference. If you care only
// about the absolute value of the difference then you may use the
// Math.abs javascript method. Just like that:
// var difference = Math.abs(endTimeFloat - startTimeFloat);
alert(difference);
}
I am trying to create a count up timer based on an object's modified time (formatted like: 2014-02-19T18:49:15) and comparing it to the current time using Moment.js. But when it reaches 60 minutes, it restarts back to 0. I think this is because I am comparing just the minutes here or at least using math to change the milliseconds to a whole number format? I am not quite sure, I got excited when I got the minutes to format correctly. The difference in minutes is all I want to return. So for example after an hour and a half I want a returned value of '90'.
function() {
return function(entry) {
var elpTime = Math.floor(((Date.parse(moment()) - Date.parse(entry.arrival_time)) / (1000*60)) % 60);
return elpTime;
}
};
Here is an example of the object.
{
patient_id: 198,
arrival_time: "2014-02-19T18:49:15",
last_modified: "2014-02-19T18:49:15"
}
I know I am missing something probably obvious. But any help is appreciated. Thanks!
(Should be noted that I am using this as apart of a filter function in Angular.js. But I stripped it out since I didn't think it was nessacary)
Since you are using Moment.js you can use the built in diff function to return the time period between two moments.
function() {
return function(entry) {
var elpTime = moment(entry.arrival_time).diff(moment(),'minutes');
return elpTime;
}
};
From the documentation:
The supported measurements are years, months, weeks, days, hours, minutes, and seconds.
By default, moment#diff will return number rounded down. If you want the floating point number, pass true as the third argument. Before 2.0.0, moment#diff returned rounded number, not a rounded down number.
Example from documentation:
var a = moment([2008, 6]);
var b = moment([2007, 0]);
a.diff(b, 'years'); // 1
a.diff(b, 'years', true); // 1.5
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.