I am currently working with hours as numbers, such as 2230 being equivalent to 22:30. I would like to be able to add numbers to it and sum as if they were minutes added to hours
2030 + 60 = 2130 //and not 2090
2330 + 120 = 0230 //and not 2350
Is there a library or function to doing this? Or perhaps I should change the way I am handling hours?
I don't recommend doing this, but if you want to do it, you have to handle the fact that you're pretending an hour is 100 minutes. You do that by extracting the real hours and minutes from the fake value, doing the math on them, and then reassembling them, something along these lines:
function toHoursAndMinutes(value) {
// Get hours: `value` divided by 100
const hours = Math.floor(value / 100);
// Get minutes: the remainder of dividing by 100
const minutes = value % 100;
// Return them
return [hours, minutes];
}
function fromHoursAndMinutes(hours, minutes) {
// Reassemble the number where hours are worth 100
return hours * 100 + minutes;
}
function add(a, b) {
// Get `a`'s hours and minutes
const [ahours, aminutes] = toHoursAndMinutes(a);
// Get `b`'s
const [bhours, bminutes] = toHoursAndMinutes(b);
// Add the hours together, plus any from adding the minutes
const hours = ahours + bhours + Math.floor((aminutes + bminutes) / 60);
// Add the minutes together, ignoring extra hours
const minutes = (aminutes + bminutes) % 60;
// Reassemble
return fromHoursAndMinutes(hours, minutes);
}
Live Example:
function toHoursAndMinutes(value) {
// Get hours: `value` divided by 100
const hours = Math.floor(value / 100);
// Get minutes: the remainder of dividing by 100
const minutes = value % 100;
// Return them
return [hours, minutes];
}
function fromHoursAndMinutes(hours, minutes) {
// Reassemble the number where hours are worth 100
return hours * 100 + minutes;
}
function add(a, b) {
// Get `a`'s hours and minutes
const [ahours, aminutes] = toHoursAndMinutes(a);
// Get `b`'s
const [bhours, bminutes] = toHoursAndMinutes(b);
// Add the hours together, plus any from adding the minutes
// The % 24 wraps around
const hours = (ahours + bhours + Math.floor((aminutes + bminutes) / 60)) % 24;
// Add the minutes together, ignoring extra hours
const minutes = (aminutes + bminutes) % 60;
// Reassemble
return fromHoursAndMinutes(hours, minutes);
}
console.log(add(2030, 60));
console.log(add(2330, 120));
But again, I don't recommend this. Instead, work with time values (Date or just milliseconds-since-the-Epoch, etc.) and convert for display when you need to display it.
Note that 50 rather than 0250, for two reasons: 1. 2330 + 120 is 2450 which is 00:50, not 02:50, and numbers don't have leading spaces except in string representations.
Here's my implementation of it
function add(current, time) {
const hours = Math.floor(time / 60);
const minutes = time % 60;
const currentMinutes = parseInt(current.toString().slice(2));
const currentHours = parseInt(current.toString().slice(0, 2));
const newMinutes = (currentMinutes + minutes) % 60;
const additionalHours = (currentMinutes + minutes) > 60 ? 1 : 0;
const newHours = (currentHours + hours + additionalHours) % 24;
return `${newHours < 10 ? '0' : ''}${newHours}${newMinutes < 10 ? '0' : ''}${newMinutes}`;
}
console.log(add(2030, 60)); // 2130
console.log(add(2330, 120)); // 0130
here is the working code for your clock.
var nowTime = '2350'; //current time in String..
var newMin = 120; // min you want to add in int..
var tMinutes = parseInt(nowTime.toString().slice(2)); //taking out the min
var tHours = parseInt(nowTime.toString().slice(0, 2)); //taking out the hr
var newMinutes = (newMin + tMinutes) % 60;
var newHr = tHours + parseInt(((newMin + tMinutes) / 60));
var newTime = `${newHr >= 24 ? newHr-24 : newHr}${newMinutes}`;
newTime = newTime.length < 4 ? '0'+newTime : newTime;
console.log(newTime);
If you want to handle date math, a library is probably best, because date math is hard, and the source of so many bugs if done wrong. Now, knowing how to do date math is a great thing to learn though, and reading through the source code of date math libraries is a good way to do that. Luxon is a good library with duration objects that can do what you need easily, and has readable source code. Other duration libraries also exist, so take a look at a few of those and see what you like the best. You can also abuse he built-in Date library to act like a duration object, but I don't think that's worth it.
But libraries aside, let's analyze the problem and what you might want to consider in solving it.
First off, I would say your first problem is trying to use a data type that isn't designed for what you want. A single integer is not a good idea for representing two values with different units. That is sort of what T.J. meant when he said it's a presentation concept. You have one object, but it's not really an integer in behavior. And date is close, but not quite right. So let's make a class. Duration seems like a good name:
class Duration { … }
We know it has two parts, hours and minutes. Also, it seems a good idea to just use one unit and convert them. (You wouldn't have to, but it actually makes the math easier if you do the conversion):
class Duration {
constructor ({hours = 0, minutes = 0}) {
this.totalMinutes = hours * 60 + minutes
}
}
Now lets make some getters to get just the minutes section and the hours section:
class Duration {
…
// just minutes that don't fit in hours
get minutes () { return this.totalMinutes % 60 }
get hours () { return Math.floor(this.totalMinutes / 60) }
// and also let's present it as the string you wanted:
asDisplayString() { return `${this.hours*100 + this.minutes}` }
}
Now we need to add them together. Some languages would let you use + for this, but javascript has limits on what we can make + do, so we'll add our own method. Note that because of how our constructor works, we can have more than 60 minutes when we initialize the values. Is this a good idea? Maybe. Depends on how you want the object to behave. (While we'll go with it for this example, there are definite arguments against it, mostly because it is a bit confusing that get minutes doesn't return over 60 - but it's also makes a certain sense at the same time).
class Duration {
…
add (otherDuration) {
return new Duration({minutes: this.totalMinutes + otherDuration.totalMinutes})
}
}
And now we can add our duration objects together, and the math is taken care of for us.
class Duration {
constructor ({hours = 0, minutes = 0}) {
this.totalMinutes = hours * 60 + minutes
}
// just minutes that don't fit in hours
get minutes () { return this.totalMinutes % 60 }
get hours () { return Math.floor(this.totalMinutes / 60) }
// and also let's present it as the string you wanted:
asDisplayString() { return `${this.hours*100 + this.minutes}` }
add (otherDuration) {
return new Duration({minutes: this.totalMinutes + otherDuration.totalMinutes})
}
}
d1 = new Duration({hours:20, minutes: 30})
d2 = new Duration({minutes: 50})
console.log(d1.asDisplayString(), '+', d2.asDisplayString(), '=', d1.add(d2).asDisplayString())
Related
I am trying to convert milliseconds to ...(sec/min/hours/day) ago,
I have tried like the below code but I am not getting the expected result, the output is showing that 19143.4 Days. It should be 2 or 3 days.
function msToTime(ms) {
let seconds = (ms / 1000).toFixed(1);
let minutes = (ms / (1000 * 60)).toFixed(1);
let hours = (ms / (1000 * 60 * 60)).toFixed(1);
let days = (ms / (1000 * 60 * 60 * 24)).toFixed(1);
if (seconds < 60) return seconds + " Sec";
else if (minutes < 60) return minutes + " Min";
else if (hours < 24) return hours + " Hrs";
else return days + " Days"
}
console.log(msToTime(1653991056493))
In fact here your code seems to work fine.
Reading the Date documentation :
JavaScript Date objects represent a single moment in time in a platform-independent format. Date objects contain a Number that represents milliseconds since 1 January 1970 UTC.
So when you're doing new Date(1653991056493) it's 1653991056493ms after Jan 1st 1970 which is 19143.4 days.
If you want the ms between a date and the current date, you can just substract the current date with the timestamp
new Date() - 1653991056493
function msToTime(ms) {
let seconds = (ms / 1000).toFixed(1);
let minutes = (ms / (1000 * 60)).toFixed(1);
let hours = (ms / (1000 * 60 * 60)).toFixed(1);
let days = (ms / (1000 * 60 * 60 * 24)).toFixed(1);
if (seconds < 60) return seconds + " Sec";
else if (minutes < 60) return minutes + " Min";
else if (hours < 24) return hours + " Hrs";
else return days + " Days"
}
console.log(msToTime(new Date() - 1653991056493))
I interpretted the question slightly differently to the accepted answer and am posting this as it might help people seeking to do what I though was being asked:
namely to reduce an elapsed period of milliseconds to either (rounded) days OR (rounded) hours OR (rounded) minutes OR (rounded) seconds - dependent on which fits the scale of the elapsed duration (as one might want to do where, for example, a page is to report "comment made 2 days ago" or "comment made 10 seconds ago" etc. - just like SO does when reporting when answers or comments were made.
As with the accepted answer, the elapsed time has to first be calculated by subtracting the passed ms value from a new date value (and, since units smaller than seconds will never be needed, the elapsed value converted to seconds by dividing by 1000):
const now = new Date();
const secondsPast = Math.round((now-pastMs)/1000);
This value is then filtered down a series of if checks, each containing a conditional return statement if the relevant time unit has been reached. Thus, if the 'scale' is seconds (i.e the elapsed duration is less than a minute), the function returns the seconds value and exits immeadiately:
if (secondsPast<60) {return `${secondsPast} seconds`} // terminates here if true;
If the seconds value is greater than 60, minutes are checked and a return made if they are less than sixty. The process repeats until larger values are eventually returned as days if no other unit was appropriate. Note the use of Math.floor to only return whole numbers for the relevant unit.
(this is, I think, what the original question was trying to achieve).
function elapsedTime(pastMs) {
const now = new Date();
const secondsPast = Math.round((now-pastMs)/1000);
if (secondsPast<60) {return `${secondsPast} seconds`} // terminates here if true;
const minutesPast = Math.floor(secondsPast/60);
if (minutesPast<60) {return `${minutesPast} minutes`} // terminates here if true;
const hoursPast = Math.floor(minutesPast/60);
if (hoursPast<24) {return `${hoursPast} hours`} // terminates here if true;
return `${Math.floor(hoursPast/24)} days`;
} // end function elapsedTime;
console.log(elapsedTime(1653991056493))
I have seen many posts about decimal hours to hours:minutes, but I have not found any that work for more than a days worth.
I am using the following with moment.js..
function formatHours(decimalHours) {
var duration = moment.duration(decimalHours, 'hours');
var hours = duration.hours();
var minutes = duration.minutes();
var date = moment().hours(hours).minutes(minutes);
var result = date.format('HH:mm');
return result;
}
but this does not work for greater than 24 hours,eg I have a hours coming in representing over a year.
Is there a ways to handle this, and also format for the current locale (as moment should do)
Thanks in advance for any help
I have no idea what Moment.js is and I am not sure what you are trying to achieve exactly. However, if I understand it well, you want to be able to convert decimal hours, going as high as you want, into a date.
I can propose you this code:
function formatHours(decimalHours) {
let minutes = decimalHours % 1;
let hours = decimalHours - minutes;
minutes *= 60;
minutes = Math.round(minutes);
if (hours > 24) {
var day = Math.floor(hours / 24);
hours %= 24;
}
let date = new Date();
date.setHours(hours);
date.setMinutes(minutes);
if (day) {
date.setDate(day);
}
console.log(`${date}`);
}
formatHours(24.88);
This will handle any decimal input up to 672.00 (28*24), you can just improve it to handle month and so on.
As commenter asked, what format are you looking for?
I would recommend adding moment-duration-format plugin which seems to be written just for your usecase. Then you could simply write:
function formatHours (decimalHours) {
return moment.duration.format(hoursDecimal, 'hours').format('D:HH:mm');
};
// This is for moment.js in angular
list : any = ["9:12","4:35","9:11"]; // declare properly
for(var i=0 ; i < 3; i++){
var parts = this.list[i].split(":");;
var hours = parseInt(parts[0]) ;
var minutes = parseInt(parts[1]);
this.a = moment.duration(this.a._data.hours,'hours').add(hours,'hours').add(this.a._data.minutes,'minutes').add(minutes,'minutes');
}
var totalHours = (24 * this.a._data.days) + this.a._data.hours ;
var total = totalHours + ":" +this.a._data.minutes;
}
I want to parse the timestamp t= in youtube url("http://youtu.be/O4tzHn-EuHc?t=1h5m16s") and calculate the total number of seconds that i need to pass as a start parameter for embed youtube url "http://www.youtube.com/embed/XGSy3_Czz8k?start="
To get the hours, minutes and seconds i use reg-exp's as below. Let me know any improvement can be done in code to make it simple.
var url ="http://youtu.be/XGSy3_Czz8k?t=1h5m16s";
var timeStamp = url.match("t=(.)*?[&|(\s)]");
var hours = timeStamp[0].match(/(\d)+h/);
var minutes = timeStamp[0].match(/(\d)+m/);
var seconds = timeStamp[0].match(/(\d)+s/);
var totalTimeInSeconds = 0;
if (hours) {
hours = hours[0].replace("h","");
totalTimeInSeconds += hours * 60 * 60;
}
if (minutes) {
minutes = minutes[0].replace("m","");
totalTimeInSeconds += minutes * 60;
}
if (seconds) {
seconds = seconds[0].replace("s","")
totalTimeInSeconds += seconds * 1;
}
console.log("hours:"+hours);
console.log("minutes:"+minutes);
console.log("seconds:"+seconds);
console.log("TotalTimeInSeconds:"+ totalTimeInSeconds);
<iframe width="420" height="345"
src="http://www.youtube.com/embed/XGSy3_Czz8k?start="+totalTimeInSeconds>
</iframe>
I think a good source for getting comments on your code would be codereview.
You can get rid of your String.replace calls by slightly adjusting your regexes to read like this:
var hours = timeStamp[0].match(/(\d+)h/);
var minutes = timeStamp[0].match(/(\d+)m/);
var seconds = timeStamp[0].match(/(\d+)s/);
With these regexes you will capture all digits at once, and can than use them like this:
if (hours) {
totalTimeInSeconds += parseInt(hours[1], 10) * 60 * 60;
}
if (minutes) {
totalTimeInSeconds += minutes[1] * 60;
}
if (seconds) {
totalTimeInSeconds += seconds[1];
}
The use of parseInt is not necessary there,
but I'd probably introduce it to make it more explicit that conversion is taking place. I'd also suggest adjusting the regex for your timeStamp variable so that it already narrows down on the t parameter more.
I think the easiest is to use RegExp replace function:
var seconds = "1h5m16s".replace(/([0-9]+)h([0-9]+)m([0-9]+)s/, function(match, p1, p2 ,p3) {
return p1 * 60 * 60 + p2 * 60 + p3 * 1
})
Note p3 * 1 - it is a shortcut for parseInt. Also note that replace will return you a string - don't forget to convert to a number if needed.
Try this
var url ="http://youtu.be/XGSy3_Czz8k?t=1h5m16s";
var timeStamp = url.match("t=(.)*?[&|(\s)]");
timeStampSplitted = timeStamp[0].replace("t=","").replace("h", ":").replace("m", ":").replace("s", "").split(':');
// minutes are worth 60 seconds. Hours are worth 60 minutes.
var seconds = (+timeStampSplitted[0]) * 60 * 60 + (+timeStampSplitted[1]) * 60 + (+timeStampSplitted[2]);
I am using the following code to get difference between two dates in hrs, minutes and seconds, which I will save later in database as a string field. So I was wondering if it is possible to use Moment.js library or any Javascript functionally in order to get the total number of hours, minutes and seconds for all date differences saved in the database (ex. 02:24:33 + 03:12:20 + 12:33:33) as one final HH:MM:SS taking in consideration that the HH could exceed 24 if the total number of hours summed exceeded 24? Thanks
$('.set-start-time').on("dp.change",function (e) {
$('.set-end-time').data("DateTimePicker").setMinDate(e.date);
});
$('.set-end-time').on("dp.change",function (e) {
$('.set-start-time').data("DateTimePicker").setMaxDate(e.date);
var now = $('.set-start-time').data('DateTimePicker').getDate().toLocaleString();
var then = $('.set-end-time').data('DateTimePicker').getDate().toLocaleString();
console.log(moment.utc(moment(then,"DD/MM/YYYY HH:mm:ss").diff(moment(now,"DD/MM/YYYY HH:mm:ss"))).format("HH:mm:ss"));
//Output here come in format ex. 02:42:33
});
It's probably too late for this to matter, but maybe something like this would help:
http://jsfiddle.net/8s8v3evf/8/
tl;dr
once you have your diff:
function zeroPad(num, places) {
var zero = places - num.toString().length + 1;
return Array(+(zero > 0 && zero)).join("0") + num;
}
function diffAsString(diff_ms) {
var hours = Math.floor(diff_ms / 3600000);
diff_ms -= (hours * 3600000);
var minutes = Math.floor(diff_ms / 60000);
diff_ms -= (minutes * 60000);
var seconds = Math.floor(diff_ms / 1000);
return zeroPad(hours, 2) + ':' + zeroPad(minutes, 2) + ':' + zeroPad(seconds, 2);
}
Having two strings (start and end time) in such form "16:30", "02:13" I want to compare them and check if the gap is greater than 5 mins.
How can this be achieved in Javascript in an easy way?
function parseTime(time) {
var timeArray = time.split(/:/);
// Using Jan 1st, 2010 as a "base date". Any other date should work.
return new Date(2010, 0, 1, +timeArray[0], +timeArray[1], 0);
}
var diff = Math.abs(parseTime("16:30").getTime() - parseTime("02:13").getTime());
if (diff > 5 * 60 * 1000) { // Difference is in milliseconds
alert("More that 5 mins.");
}
Do you need to wrap over midnight? Then this is more difficult. For example, 23:59 and 00:01 will produce a difference of 23 hours 58 minutes and not 2 minutes.
If that's the case you need to define your case more closely.
You can do as following:
if (((Date.parse("16:30") - Date.parse("02:13")) / 1000 / 60) > 5)
{
}
// time is a string having format "hh:mm"
function Time(time) {
var args = time.split(":");
var hours = args[0], minutes = args[1];
this.milliseconds = ((hours * 3600) + (minutes * 60)) * 1000;
}
Time.prototype.valueOf = function() {
return this.milliseconds;
}
// converts the given minutes to milliseconds
Number.prototype.minutes = function() {
return this * (1000 * 60);
}
Subtracting the times forces the object to evaluate it's value by calling the valueOf method that returns the given time in milliseconds. The minutes method is another convenience method to convert the given number of minutes to milliseconds, so we can use that as a base for comparison throughout.
new Time('16:30') - new Time('16:24') > (5).minutes() // true
This includes checking whether midnight is between the two times (as per your example).
var startTime = "16:30", endTime = "02:13";
var parsedStartTime = Date.parse("2010/1/1 " + startTime),
parsedEndTime = Date.parse("2010/1/1 " + endTime);
// if end date is parsed as smaller than start date, parse as the next day,
// to pick up on running over midnight
if ( parsedEndTime < parsedStartTime ) ed = Date.parse("2010/1/2 " + endTime);
var differenceInMinutes = ((parsedEndTime - parsedStartTime) / 60 / 1000);
if ( differenceInMinutes > 5 ) {
alert("More than 5 mins.");
}