Fast Time conversion
I'm trying to write a very fast time-to-string and string-to-time function.
I noticed that on low CPU mobile devices this function has, even if minimal, impact on the performance of the overall canvas animation.
Milliseconds to Time string (HH:MM:SS.mss)
In this function: is there a shorter & faster way to pad the numbers?
function ms2TimeString(a,ms,s,m,h){// time(ms)
return ms=a%1e3|0,
s=a/1e3%60|0,
m=a/6e4%60|0,
h=a/36e5%24|0,
(h>0?(h<10?'0'+h:h)+':':'')+ //display hours only if necessary
(m<10?'0'+m:m)+':'+
(s<10?'0'+s:s)+'.'+
(ms<100?(ms<10?'00'+ms:'0'+ms):ms)
}
Here are some other ways to pad the numbers, but I guess executing new array
,join
,slice
or substr
has more impact on the performance than (m<10?'0'+m:m)
.
function padL2(a){//number or string
return a+='','00'.substr(0,2-a.length)+a;
}
function padL(a,b,c){//string,length=2,char=0
return (new Array(b||2).join(c||0)+a).slice(-b);
}
Time string (HH:MM:SS.mss) to Milliseconds
This function uses 2 split
and the array a
is not cached.
function timeString2ms(a,b,c){// time(HH:MM:SS.mss)
return c=0,
a=a.split('.'),
!a[1]||(c+=a[1]*1),
a=a[0].split(':'),b=a.length,
c+=(b==3?a[0]*3600+a[1]*60+a[2]*1:b==2?a[0]*60+a[1]*1:s=a[0]*1)*1e3,
c
}
UPDATE
the first most important part of this function should be the performance, the second is to have a very short code. it should be a one line function.
ms2TimeString (4 updates)
function ms2TimeString(a,k,s,m,h){
return k=a%1e3, // optimized by konijn
s=a/1e3%60|0,
m=a/6e4%60|0,
h=a/36e5%24|0,
(h?(h<10?'0'+h:h)+':':'')+ // optimized
(m<10?0:'')+m+':'+ // optimized
(s<10?0:'')+s+'.'+ // optimized
(k<100?k<10?'00':0:'')+k // optimized
}
Description:
function ms2TimeString(inputInMilliseconds,milliseconds,seconds,minutes,hours){
The milliseconds, seconds, minutes, hours are just placeholders. They are undefined variables which I set inside the function before I need them. Sometimes they are really useful so you don't have to write multiple times var
.
return
I start with return (another point why I don't want use var
) as this function is meant to be a one-line-function, even if it's a long function.
milliseconds=inputInMilliseconds%1e3,
I removed the unecessary |0
.
The strange number 1e3
means 1000
.
1e7 means that the first number is followed by 7 zeros
seconds=inputInMilliseconds/1e3%60|0,
To get only the seconds I need to divide the milliseconds per 1000 (so 1e3) then apply the modulo calculation and finally floor the number.
But there is a trick. Using the bitwise operator |
or >>
, you can floor numbers up to 32bit, so numbers up to 10 decimal digits. As this function is meant to be for a a/v player that's enough. And the cool thing is that this bitwise operator is much much faster than the Math.floor
function.
minutes=inputInMilliseconds/6e4%60|0,
Same as above, ms/60000%60|0
gives you the seconds. You will notice the ,
as I started with return
the comma allows me to apply some calculations before I output the final result.
hours=inputInMilliseconds/36e5%24|0,
to get the hours 60*60*1000
=3600000
=36e5
.....Math.floor(ms/3600000%24)
in this case we have 24 hours max.
(hours?(hours<10?'0'+hours:hours)+':':'')+
Now if you look at the first function, I made some optimizations. As hours>0
and hours
will give you the same value, I removed those 2 chars. If hours is bigger than 0, then it's true, else false, same as hours is 0 it will return 0 which is false or null. It works like a charm and it does not have to check with another value which in this case is 0.
(minutes<10?0:'')+minutes+':'+
(seconds<10?0:'')+seconds+'.'+
Also in these two lines I optimized the code a little. As we already appending to a string, I can pass 0 as number, and don't need the ''
. I also don't need to write 3 times minutes or seconds. I saved a char, but also added performance as when compiling the var s(seconds) appears 2 times vs 3.
(milliseconds<100?milliseconds<10?'00':0:'')+milliseconds
I can't apply the above number trick because 00 is always 0 if interpreted as a number.
}
Usage:
console.log(ms2timeString(89754)); // milliseconds
timeString2ms (3 updates)
function timeString2ms(a,b){// time(HH:MM:SS.mss) // optimized
return a=a.split('.'), // optimized
b=a[1]*1||0, // optimized
a=a[0].split(':'),
b+(a[2]?a[0]*3600+a[1]*60+a[2]*1:a[1]?a[0]*60+a[1]*1:a[0]*1)*1e3 // optimized
}
Description
function timeString2ms(a,b){
At the moment a
is the input as a Time String HH:MM:SS.mss
and b
is a placeholder. I don't give special names to this 2 variables as they change the role inside the function. These are just 2 temp variables. If you prefer, I can call them temp1
and temp2
.
return a=a.split('.'),
Here I split the string in 2 pieces where the first is HH:MM:SS
and the second is milliseconds
.
b=a[1]*1||0,
If the input contains milliseconds I multiply them by one to avoid the slow parseInt
function and set b
the first temp variable to the milliseconds, else I set it to 0.
a=a[0].split(':'),
Here I reuse a
which is the input string as an array where I store hours, minutes and seconds.
b+
add my milliseconds with
(a[2]?
if the array length is 3, and so I have hours, minutes and seconds
a[0]*3600+a[1]*60+a[2]*1
I multiply the hours with 3600 add them to minutes multiplied with 60 add seconds multiplied with 1 (to avoid parseInt
). In this case a
contains hours
, minutes
, seconds
, b
milliseconds
.
:a[1]?
else if no hours. In this case a
contains minutes,seconds
,b
milliseconds
a[0]*60+a[1]*1
minutes multiplied with 60 add seconds multiplied with 1 (to avoid parseInt
)
:
else. In this case a
contains only seconds
,b
milliseconds
a[0]*1
I multiplied the seconds with 1 (to avoid parseInt
)
)*1e3
and finally multiply everything with 1000
}
Usage:
console.log(timeString2ms('10:21:32.093')); // hh:mm:ss.mss
console.log(timeString2ms('21:32.093')); // mm:ss.mss
console.log(timeString2ms('21:32')); // mm:ss
console.log(timeString2ms('32.093')); // ss.mss
console.log(timeString2ms('32')); // ss
console.log(timeString2ms('32467.784')); // seconds.ms
console.log(timeString2ms(32467.784+'')); // seconds.ms
Extra
Notes
I did not use any minifier or compressor. The purpose of this code is already explained in the first lines of the post (performance and byte-saving).
Using bitwise operators and shorthand makes this function faster in the real world (if you don't think so create your own function and show me a jsperf). If you don't understand, ask.
No memoization.
Executing/initializing functions inside other functions is slower.
http://jsperf.com/multiple-functions
Accessing objects is slower than arrays or single vars.
http://jsperf.com/store-array-vs-obj
Maybe the result between every second would be more fluid, but I would see a higher performance loss when the second value changes. So in this case, I prefer some random lag that every second a fixed lag.