Get time calculation from momentJS duration - javascript

I have a field in my table that I would like to sort. That field contains strings from MomentJS duration calculation. For example "20 Minutes", "10 Hours 1 Minute" and so on.
If I want to use a sort function I need to format that string back to lowest resolution of time I have which is minutes.
Like this: a: "20 Minutes" => 20.
b: "10 Hours 1 Minute" => 10*60 + 1 = 601.
So when sorting in a descending order b will come before a.
I made this function that does work but it is really hardcoded so I would really like to see other ways to solve it. Thanks!
function getTimeFromDuration(duration) {
const str = duration.split(' ');
const durationArray = ['Minute', 'Minutes', 'Hour', 'Hours', 'Day', 'Days'];
const calcArr = [];
let sum = 0;
for (let i=0; i<str.length; i++) {
const isNum = !isNaN(str[i]);
const isDuration = durationArray.includes(str[i]) > -1;
if (isNum) {
calcArr.push(parseInt(str[i]));
}
else if (isDuration) {
switch(str[i]) {
case 'Minute':
case 'Minutes':
calcArr.push(1);
break;
case 'Hour':
case 'Hours':
calcArr.push(60);
break;
case 'Day':
case 'Days':
calcArr.push(60*24);
break;
}
}
}
for (let j=0; j<calcArr.length; j=j+2) {
sum+= calcArr[j]*calcArr[j+1];
}
return sum;
}
console.log(getTimeFromDuration('1 Day 1 Hour 20 Minutes'));

I am not aware of a parsing function in Moment or elsewhere that will handle these types of strings. If your input strings are consistently formatted and limited to days, hours and minutes, then parsing them should not be all that difficult. Following is an approach along the lines of your original function, just a bit more compact due to some effort put into splitting the string into each duration "chunk" and then normalizing the duration descriptor (lowercase and singular) so you can easily access values from a multiplier object.
That said, if your original data contains the Moment duration object rather than just the string representation, then there are easier ways to do this.
const getMinutes = (s) => {
const multipliers = { day: 1440, hour: 60, minute: 1 };
const durations = s.split(/\s(?=\d)/);
let minutes = 0;
for (const d of durations) {
let [n, interval] = d.split(' ');
n = Number(n);
interval = interval.toLowerCase();
if (interval.endsWith('s')) {
interval = interval.slice(0, -1);
}
minutes += n * multipliers[interval];
}
return minutes;
};
const result = getMinutes('1 Day 1 Hour 20 Minutes');
console.log(result);
// 1520

Related

How to map array of numbers to timing values

Hi I have an array of numbers
let timearray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23];
But the problem is I am displaying the array value in DOM, but I want it such a way that the values been mapped accordingly. Ex: If 0 -> 12:00am, if its 18 -> 6:00pm. How can I do that? Any idea guys? I can only think of map using if else. But that seems pretty ugly. Any idea guys?
You need to define your array like this:
const timearray = [
'12:00am',
'01:00am',
'02:00am',
'03:00am',
...
'10:00pm',
'11:00pm',
]
console.log(timearray[3])
// 03:00am
Here's an example map function you can use (and improve based on your needs):
const timeArray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23];
const result = timeArray.map(t => {
const ampm = t % 12;
return `${ampm === 0 ? 12 : ampm}:00${t >= 12 ? 'pm' : 'am'}`;
});
console.log(result)
This is exactly what Array.map is for:
let timearray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
let mapped = timearray.map(function(value) {
switch (value) {
case 0: return "12:00am"
// ..
case 18: return "6:00pm"
}
})
Or you can use an object
let timearray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
let map = {
0: "12:00am",
18: "6:00pm"
}
let mapped = timearray.map(function(value) {
return map[value]
})
If you simply want to convert 24 hour time to 12 hour time AND NOT MAP THE VALUES then google for "24 hour time to 12 hour time" and you'll find an answer.
To convert integers to hours in 12-hour format with am/pm format you can use:
let timearray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23];
timearray.forEach(x => {
var suffix = x >= 12 ? "pm":"am";
hours = ((x + 11) % 12 + 1) + suffix;
console.log(hours);
}
);
Based on code from this answer: Converting 24 hour time to 12 hour time w/ AM & PM using Javascript
You may also consider leveraging a library such as, moment.js (https://momentjs.com/) in this situation.
Here is an example of usage in your case:
var time = moment(2,"h:hh A");
console.log(time);
// 02:00am
The benefit from this approach is that you would be able to easily format the output, if required to.

How can I convert the sum of minutes, hours in the 'timelite.js' or 'moment.js'?

I have array let times = ['04:20:10', '21:15:10']. I use method add:
let time1 = add([times]). Next I use method str: str([time1]). I kept
25:35:30. I would like to receive the following result: 25h 35 min 30 sec. If the sum would come in at 30:00:00 I would like to receive the result 30h. In the case of the sum of 00:30:00 I would like to receive a result of 30 min. In the case of 00:00:20 - the result 20 sec. How to get such a result in the 'timelite.js' or 'moment.js' library?
Maybe could be easier if you just format the time.
const formatTime = (time) => {
const [hour, minute, sec] = time.split(':')
return `${hour} h ${minute} min ${sec} sec`
}
const time = '25:35:30'
console.log(formatTime(time))
// 25 h 35 min 30 sec
This is just a simple example. To achieve what you want you need to put some more logic, but yeah it's a good start.
const convert = (time) => {
const [hour, minute, sec] = time.split(':');
if (hour !== "00") {
return `${hour} h ${minute} m ${sec} s`;
}
if (minute !== "00") {
return `${minute} m ${sec} s`;
}
return `${sec} s`;
}
console.log(convert('00:30:00');
//30 min 00s

how to create an array of times with half hour intervals instead of declare in variable

i have an long const values below like this ,is it possible to create in function in typescript to reduce a long const.
export const DepartTime =
['00.00', '00.30', '01.00', '01.30', '02.00', '02.30', '03.00', '03.30', '04.00', '04.30', '05.00', '05.30', '06.00', '06.30', '07.00', '07.30', '08.00', '08.30', '09.00', '09.30', '10.00', '10.30', '11.00', '11.30', '12.00', '12.30', '13.00',
'13.30', '14.00', '14.30', '15.00', '15.30', '16.00', '16.30', '17.00', '17.30', '18.00', '18.30', '19.00', '19.30',
'20.00', '20.30', '21.00', '21.30', '22.00', '22.30', '23.00', '23.30'
];
These values need to be bind in angular dropdown.
export class SelectOverviewExample implements OnInit {
days=[];
times =[];
ngOnInit(){
[...this.days] = weekdays;
[...this.times]=DepartTime;
}
in html.
<mat-form-field>
<mat-select placeholder="select Weekdays">
<mat-option *ngFor="let time of times" [value]="time">
{{time}}
</mat-option>
</mat-select>
</mat-form-field>
or any third party framework will support
I have tried this and getting below result:
var toInt = time => ((h,m) => h*2 + m/30)(...time.split(':').map(parseFloat)),
toTime = int => [Math.floor(int/2), int%2 ? '30' : '00'].join(':'),
range = (from, to) => Array(to-from+1).fill().map((_,i) => from + i),
eachHalfHour = (t1, t2) => range(...[t1, t2].map(toInt)).map(toTime);
console.log(eachHalfHour('00:00', '23:30'))
But i need single digit have start with 00:00,00:30,01.00,01.30 ... 09.30.
O/p
by using moment js , i have get a solution but need 0 prefix for single digits
const locale = 'en'; // or whatever you want...
const hours = [];
moment.locale(locale); // optional - can remove if you are only dealing with one locale
for(let hour = 0; hour < 24; hour++) {
hours.push(moment({ hour }).format('H:mm'));
hours.push(
moment({
hour,
minute: 30
}).format('H:mm')
);
}
console.log(hours);
Short answer: change your moment.format to .format('HH:mm'));
You can ensure you get the double digits by just adding an extra 'H' to your existing moment config. ie go from this:
const locale = 'en'; // or whatever you want...
const hours = [];
moment.locale(locale); // optional - can remove if you are only dealing with one locale
for(let hour = 0; hour < 24; hour++) {
hours.push(moment({ hour }).format('H:mm'));
hours.push(
moment({
hour,
minute: 30
}).format('H:mm')
);
}
console.log(hours);
to this
const locale = 'en'; // or whatever you want...
const hours = [];
moment.locale(locale); // optional - can remove if you are only dealing with one locale
for(let hour = 0; hour < 24; hour++) {
hours.push(moment({ hour }).format('HH:mm'));
hours.push(
moment({
hour,
minute: 30
}).format('HH:mm')
);
}
console.log(hours);
Hope that helps! :)
I don't think you can reduce it. This may not help, but here is JS version :
var foo = [];
for (var i = 0; i <= 48; i++) {
var n = i%2==0 ? i/2+'.00' : (i+1)/2-1+'.30';
if(n<10) //zero-left padding
n = '0'+n;
foo.push(n);
}
console.log(foo);
Below code works for me I have tried using https://stackoverflow.com/a/8043061/9516784
var hrs = [];
for (var i = 0; i <= 48; i++) {
var n = i%2==0 ? i/2+'.00' : (i+1)/2-1+'.30';
if(n<10) {
n = '0'+n;
}
hrs.push(n);
}
You can just use division by 2.
let DepartTime = [...Array(48)].map((e, i) => {
return (i/2<10 ? '0' : '') + (i/2 - i/2 % 1) + (i/2 % 1 != 0 ? '.30' : '.00');
});
console.log(DepartTime);
Passing a string in hh:mm format to your function is making it more complicated than necessary when you could just pass numbers to it.
You can use padStart to ensure a number converted to a string has leading zeroes.
As for the loop, this seems like the perfect situation to use a generator.
function* generateTimes(startHour, stopHour) {
for (let h = startHour; h < stopHour; ++h) {
const hh = h.toString().padStart(2, '0');
yield `${hh}.00`;
yield `${hh}.30`;
}
}
console.log([...generateTimes(0, 24)]);
Below Solution work for me:
const hours = [];
Array.apply(null, { length: 24 }).map((value, hour) => {
hours.push(
moment({ hour }).format('HH:mm'),
moment({ hour, minute: 15 }).format('HH:mm'),
moment({ hour, minute: 30 }).format('HH:mm'),
moment({ hour, minute: 45 }).format('HH:mm')
);
});
console.log(hours);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
Based on your requirements you can push those values to the hours array.
Here is a function that will return the key-value pair based on the given interval(minutes: 60, 30, 15, 10, 5), where the key is in 24-hour format and the display value is in 12-hour format.
// Interval values can be(minutes): 60 | 30 | 15 | 10 | 5
function getTimeDDL(intervalMinutes) {
let formatHour = (hour, minutes) => {
let minutesPadded = minutes.toString().padStart(2, "0");
return {
value: (hour < 10 ? "0" + hour : hour) + `:${minutesPadded}`,
viewValue: hour < 12 || hour == 24 ? (hour < 24 ? hour : 12) + `:${minutesPadded} AM` : (hour - 12 || 12) + `:${minutesPadded} PM`,
};
};
let timesDDL = [];
let hour = 1;
while (hour <= 24) {
timesDDL.push(formatHour(hour, 0));
if (hour < 24) {
let steps = 60 / intervalMinutes;
let i = 1;
while (i < steps) {
let minutes = intervalMinutes * i;
timesDDL.push(formatHour(hour, minutes));
i++;
}
}
hour++;
}
return timesDDL;
}
// Interval values can be(minutes): 60 | 30 | 15 | 10 | 5
console.log(getTimeDDL(30));
/** Output
[
{
"value": "01:00",
"viewValue": "1:00 AM"
},
{
"value": "01:30",
"viewValue": "1:30 AM"
},
..
..
{
"value": "24:00",
"viewValue": "12:00 AM"
}
]
**/

How To Get The Sum of Two Times Using Moment.js?

I want to add two times like time1 = '00:05' and time2 = '10:00'. I want the result like the following after sum: result='10:05'. I used moment for that, this is what I used:
let x = moment.duration(moment(this.time1, "hh:mm A").add(moment(this.time2, "hh:mm A")));
let result = moment.utc(x.asMilliseconds()).format('HH:mm:ss');
but I got nothing, how can I do it?
You can't add time this way with moment because you are asking it to add two times, not a time plus a duration. If you want to add ten minutes, use the add() function with a duration.
moment(this.time2, "hh:mm A").add(10, 'minutes')
More here: https://momentjs.com/docs/#/manipulating/add/
It's not really clear in your question what 00:05 PM means. That doesn't look like a valid time. Moment will interpret it as 12:05pm, but it looks like you want to interpret it as 5 minutes. (That's the only way you get 10:05 as an answer). You can do this with moment if you don't include the PM part of the string.
moment.duration('00:05')
Is a duration of five minutes. You can add this to your time with:
moment('10:00 PM', '"hh:mm A"').add(moment.duration('00:05'))
// 22:05:00
Adding two periods works but it is currently not obvious in moment, how to format it like you want. Until they add format() to durations this will have to do:
var d = moment.duration('03:10:10').add(moment.duration('01:20:30'))
moment.utc(d.as('milliseconds')).format("HH:mm:ss")
// '04:30:40'
See Example For Add & Diff using moment.js .. cheers😀
// addTimes(["01:00", "00:30", "00:50"]) ---- 02:20
addTimes(times) {
let duration = 0;
times.forEach(time => {
duration = duration + moment.duration(time).as('milliseconds')
});
return moment.utc(duration).format("HH:mm")
}
// subtractTimes(["05:00", "00:30", "00:20"]) --- 04:10
subtractTimes(times) {
let totalDiff = 0
for (let i = 0; i < times.length; i++) {
let duration = moment.duration(times[i]).as('milliseconds')
if (i == 0) {
totalDiff = duration
}
if (i > 0) {
totalDiff = totalDiff - duration
}
}
return moment.utc(totalDiff).format("HH:mm")
}
function addTwoHours(firstTime = "20:40", secondTime = "18:40") {
firstTime = firstTime.split(':');
secondTime = secondTime.split(':');
const now = moment();
const expiration = moment().add({ hour: firstTime[0], minute: firstTime[1] }).add({ hour: secondTime[0], minute: secondTime[1] });
const diff = expiration.diff(now);
const diffDuration = moment.duration(diff);
return {
"years": diffDuration.years(),
"months": diffDuration.months(),
"days": diffDuration.days(),
"hours": diffDuration.hours(),
"minutes": diffDuration.minutes(),
"yourAnswer" : `${expiration.diff(now, 'hours')}:${diffDuration.minutes()}`
}
}
console.log(addTwoHours());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.min.js"></script>

How to parse a duration string into seconds with Javascript?

I'm trying to parse a user input string for duration (into seconds) with Javascript.
Here are some example inputs that I'd like to be able to deal with:
"1 hour, 2 minutes"
"1 day, 2 hours, 3 minutes"
"1d 2h 37m"
"1 day 2min"
"3days 20hours"
The key components are 1) days, 2) hours 3) minutes, but some components may not always be included.
My plan of attack is to use .match and regex. As long as I can get the first letter of the word, I'll know what the preceding number is for and be able to handle all the different formats of the words (e.g. hours, hour, hr, h). However, since I'm learning regex for this, it's turned out to be much more complicated than I thought.
Any help or suggestions would be greatly appreciated!
Is the order of days/hours/minutes in your string guaranteed? If not, it may be easier to just do a separate RegEx for each. Something like this?
function getSeconds(str) {
var seconds = 0;
var days = str.match(/(\d+)\s*d/);
var hours = str.match(/(\d+)\s*h/);
var minutes = str.match(/(\d+)\s*m/);
if (days) { seconds += parseInt(days[1])*86400; }
if (hours) { seconds += parseInt(hours[1])*3600; }
if (minutes) { seconds += parseInt(minutes[1])*60; }
return seconds;
}
You can use this code to break your string into a number, followed by a unit string:
function getPieces(str) {
var pieces = [];
var re = /(\d+)[\s,]*([a-zA-Z]+)/g, matches;
while (matches = re.exec(str)) {
pieces.push(+matches[1]);
pieces.push(matches[2]);
}
return(pieces);
}
Then function returns an array such as ["1","day","2","hours","3","minutes"] where alternating items in the array are the value and then the unit for that value.
So, for the string:
"1 day, 2 hours, 3 minutes"
the function returns:
[1, "day", 2, "hours", 3, "minutes"]
Then, you can just examine the units for each value to decide how to handle it.
Working demo and test cases here: http://jsfiddle.net/jfriend00/kT9qn/. The function is tolerant of variable amounts of whitespace and will take a comma, a space or neither between the digit and the unit label. It expects either a space or a comma (at least one) after the unit.
You can use optional substrings in the regex to match any combination as you describe:
/(\d+)\s*d(ays?)?\s*(\d+)\s*h(ours?)?\s*(\d+)\s*m(in(utes?)?)?/
This requires at least a d, h, and m but accepts common shortenings.
var str = "1 day, 2 hours, 3 minutes";
var seconds = 0;
str.replace (/\s*,?(\d+)\s*(?:(d)(?:ays?)?|(h)(?:ours?)?|(m)(?:in(?:utes?)?)?)/g, function (m0, n, d, h, m) {
seconds += +n * (d ? 24 * 60 * 60 : h ? 60 * 60 : 60);
return m0;
});
Here I use replace not to change the string but to process the matches one by one. Note days, hours aminutes can be in any order.
A more general version of the accepted answer that accepts months and seconds as well.
const getSeconds = str => {
let seconds = 0;
let months = str.match(/(\d+)\s*M/);
let days = str.match(/(\d+)\s*D/);
let hours = str.match(/(\d+)\s*h/);
let minutes = str.match(/(\d+)\s*m/);
let secs = str.match(/(\d+)\s*s/);
if (months) { seconds += parseInt(months[1])*86400*30; }
if (days) { seconds += parseInt(days[1])*86400; }
if (hours) { seconds += parseInt(hours[1])*3600; }
if (minutes) { seconds += parseInt(minutes[1])*60; }
if (secs) { seconds += parseInt(secs[1]); }
return seconds;
};

Categories