How to replace time stamp in the string and round hours? - javascript

I have function that returns array or values that represent working hours. Array can return one or two elements. In case where only one element is returned this is pretty simple but when I have two that is the problem to find the way how to replace the existing values in the string. Here is example of the original string: 0600-2200 MAY 15-SEP 30; 0600-2100 OCT 1-MAY 14. I have function that finds the time stamp in the string and returns the hours. Example of the returning array is here: [16,15]. That array has two values and I need to replace 0600-2200 with first element in the array 16 and append the word hours to that. So final output should look like this: 16 hours MAY 15-SEP 30; 15 hours OCT 1-MAY 14. Here is example of the function that converts time stamp to string:
var timeSt = "0600-2200 MAY 15-SEP 30; 0600-2100 OCT 1-MAY 14";
const calcDifference = range => {
const time = range.split`-`.map(e => (+e.substr(0, 2) * 60 + (+e.substr(2))) / 60);
return time[1] - time[0];
};
const diffs = timeSt.match(/\d{4}\-\d{4}/g).map(e => calcDifference(e));
console.log(diffs);
The solution I have tried looks like this:
var hours = "";
for(var i=0; i < diffs.length; i++){
hours += timeSt.replace(regex,diffs[i] + " hours ");
}
Here is the output that above example produced:
16 hours MAY 15-SEP 30; 16 hours OCT 1-MAY 1415 hours MAY 15-SEP 30; 15 hours OCT 1-MAY 14
Seems that entire string was appended twice. I understand why that is happening but still can't get a good way how to fix this problem. The other thing that I noticed is that some time stamp values look like this: 0000 - 2359
In that case function that converts hours will return this: [23.983333333333334]. I would like to round up that value to 24 and that is the only case where the value should be rounded up to 24 the higer int his case. I time stamp looks like this: 0500-2330 function returns [18.5] and that value should not round. It should stay as it is. If anyone knows good way on how to fix these two problems please let me know.

For the problem of replacing you can provide a callback to .replace function in string.
const roundMinutes = 15;
const timeSt = "0600-0000 MAY 15-SEP 30; 0600-2145 OCT 1-MAY 14";
const calcDifference = range => {
const time = range.split`-`.map(e => +e.substr(0, 2) * 60 + (+e.substr(2)));
let [start, end] = time;
if (end < start) {
end += 24 * 60;
}
return end - start;
};
const formatted = timeSt.replace(/\d{4}\-\d{4}/g, (range) => {
const diff = calcDifference(range);
const full = Math.round(diff / roundMinutes) * roundMinutes;
const hours = Math.floor(full / 60);
const minutes = full - hours * 60;
const time = minutes === 0 ? `${hours}` : `${hours}.${minutes}`
return `${time} hours`;
})
console.log(formatted)
To change precision you can tweak roundMinutes constant.

Related

Proper way to manipulate date string with wrong year, but correct time

I'm having to hit an API I have no access to fixing and I need to start a timer showing how long someone has been in a queue for. The date I get back is in this format 1556214336.316. The problem is the year always shows up as 1970, but the time is the correct start time. I need to calculate the difference between the time now, and the time the conversation was created at. I have tried this with little success and was wondering if there is an elegant way to only get the difference in time and not the total amount of seconds.
convertDateToTimerFormat = (time) => {
const now = new Date();
const diff = Math.round((now - parseInt(time.toString().replace('.', ''))) / 1000);
const hours = new Date(diff).getHours();
const minutes = new Date(diff).getMinutes();
const seconds = new Date(diff).getSeconds();
return `${hours}:${minutes}:${seconds}`;
}
The weird parseInt(time.toString().replace('.', ''))) seems to fix the 1970 issue, but I still can't get the data to be manipulated how I need.
I tried the momentjs library, but their diff method only appears to allow for days and hours.
Any help/guidance, would be much appreciated.
Edit with working code:
convertDateToTimerFormat = (time) => {
const now = new Date();
// eslint-disable-next-line radix
const diff = new Date(Number(now - parseInt(time.toString().replace(/\./g, ''))));
const hours = diff.getHours();
const minutes = diff.getMinutes();
const seconds = diff.getSeconds();
return `${hours}:${minutes}:${seconds}`;
}
Unix time values are the number of seconds since the Epoch and won't have a decimal like your 1556214336.316
If I take 1556214336 (without the .316) and put it in a converter I get the output 04/25/2019 # 5:45pm (UTC) which is not 1970 — it seems an accurate time (I haven't independently verified)
It seems, then, your 1556214336.316 is the seconds.milliseconds since the epoch.
Javascript uses the same epoch, but is the number of milliseconds since the epoch, not seconds, so if I'm correct about the time you're getting you should be able to just remove the decimal place and use the resulting number string. Indeed
var d = new Date(1556214336316);
console.log('Date is: ' + d.toUTCString());
produces
Date is: Thu, 25 Apr 2019 17:45:36 GMT
which exactly matches the converter's time of "5:45pm"
var d = new Date(1556214336316);
console.log('Date is: ' + d.toUTCString());
Assuming your value 1556214336.316 is a String coming back from a web API, you can remove the decimal and your conversion can be done like this (note you don't have to keep creating new Date objects):
convertDateToTimerFormat = (time) => {
const d = new Date( Number(time.replace(/\./g, '')) );
return `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`;
};
console.log( 'time: ' + convertDateToTimerFormat('1556214336.316') );
Depending on your use, you may want to use getUTCHours() etc. instead.
I don't know about elegant, but this calculates and displays the expired time in h:mm:ss format:
console.log(convertDateToTimerFormat(1556215236.316));
function convertDateToTimerFormat(time){
// Converts `time` to milliseconds to make a JS Date object, then back to seconds
const expiredSeconds = Math.floor(new Date()/1000) - Math.floor(new Date(time * 1000)/1000);
// Calculates component values
const hours = Math.floor(expiredSeconds / 3600), //3600 seconds in an hour
minutes = Math.floor(expiredSeconds % 3600 / 60),
seconds = expiredSeconds % 3600 % 60;
// Adds initial zeroes if needed
if (minutes < 10) { minutes = "0" + minutes; }
if (seconds < 10) { seconds = "0" + seconds; }
// Returns a formatted string
return `${hours}:${minutes}:${seconds}`;
}

Converting to duration from milliseconds

I am trying to convert time duration from the format of mm:ss.mss to entirely milliseconds and back.
I've already have a working function for converting from milliseconds to duration but I cannot seem to get it the other way around.
Lets say for instance that I have the duration 32:29.060, I want to convert it to milliseconds. For that I use this function:
function millisecondsToTime(ms, digits) {
digits = digits || 12;
return new Date(ms).toISOString().slice(23-digits, -1);
}
var a = millisecondsToTime(5549060, 9);
but whenever I try to convert back to time duration, I fail. I've tried parsing individually the minutes, seconds and milliseconds but it doesn't seem to work.
Here is the code that I've used for it:
var firstSplit = a.split(':')
var minutes = firstSplit[0]; //1
var secondSplit = firstSplit[1].split('.');
var seconds = secondSplit[0]; //2
var millisec = secondSplit[1]; //3
var conversion = ((+minutes) * 60 + (+seconds) * 60 + (+millisec))*1000;
I have an input bar which takes the format of mm:ss.mss and I need to convert it to milliseconds. How can I do that?
you can just return a
new Date(ms)
to get a date from ms.
And to get the same date as ms,
date.getTime() // returns ms from date object
Full example:
const ms = 5549060
const date = new Date(ms) // get a date from ms
console.log(date.getTime) // logs 5569060
If your input is a string in the format of mm:ss.mss, and you want to get a date from it, you can use moment.
const moment = require('moment')
const date = moment('22:15.143', 'mm:ss.SSS') // get date from pre specified format
You can use the string methods indexOf() and substr() to get the individual numbers out of your string and calculate the time accordingly.
I'm afraid though your millisecondsToTime() function isn't working properly.
5549060 milliseconds are roughly 92 minutes and it's returning 32:29.060
function backToTime(time) {
var index = time.indexOf(":");
var minutes = time.substr(0, index);
var seconds = time.substr(index + 1, time.indexOf(".") - (index + 1));
var milliseconds = time.substr(time.indexOf(".") + 1, time.length);
return parseInt(minutes * 60 * 1000) + parseInt(seconds * 1000) + parseInt(milliseconds);
}
console.log(backToTime("32:29.060"));
Your conversion to milliseconds is not working, this is basic math approach to both conversions:
let input = 5549060
//toDuration
let seconds = Math.floor(input / 1000);
let ms = input - seconds*1000;
let m = Math.floor(seconds / 60);
let s = seconds - m*60;
duration = m + ":" + s + "." + ms
console.log(duration)
//toMilliseconds
let holder = duration.split(":");
m = parseInt(holder[0]);
holder = holder[1].split(".");
s = parseInt(holder[0]);
ms = parseInt(holder[1]);
milliseconds = (m*60 + s)*1000 + ms
console.log(milliseconds)
If needed add check for ms length to add 0s, if you need it to have length of 3
I think your milliseconds to duration converter will be broken for durations above 60 minutes. This is because using Date the minutes field will wrap over into the minutes after 59 seconds have passed. If you want to get good support for values beyond 59 in your first field, I think maybe moving to a regex-based parser and using multiplication and addition, division and modulo to extract and reduce the fields manually might be nice. Something like this maybe:
var duration = ms => `${(ms / 60000) | 0}`.padStart(2, '0') + `:` + `${ms % 60000 / 1000 | 0}`.padStart(2, '0') + `.` + `${ms % 1000}`.padStart(3, '0')
var millisec = durat => (match => match && Number(match[1]) * 60000 + Number(match[2]) * 1000 + Number(match[3]))(/^([0-9]+)\:([0-5][0-9])\.([0-9]{3})$/.exec(durat))
You can see given the input 5549060, this function provides output 92:29.60, which is exactly 60 seconds greater than your own, and I believe to be correct. Maybe it's intentional for your usecase, but I can't imagine that being so desirable generally...

Converting human string time between different units of time

Are you aware of a npm module capable of converting human string time to days, hours, seconds or milliseconds preferably using moment.js time units?
It's a little difficult to explain, so here are a few examples:
'1hours' to minutes = 60
'2days' to seconds = 172800
'60seconds' to minutes = 1
'30minutes' to seconds = 1800
Same as above using short hand:
'1h' to minutes = 60
'2d' to seconds = 172800
'60s' to minutes = 1
'30m' to seconds = 1800
These are the string units used by moment.js
Key Shorthand
----------------------
years y
quarters Q
months M
weeks w
days d
hours h
minutes m
seconds s
milliseconds ms
Or expressed as a function:
const convertUnits = (input, format) => {
// Implementation
};
convertUnits('1hours', 'minutes') // 60
convertUnits('1h', 'm') // 60
Or is it possible to do this just using moment? Remember that I don't care about the actual date or time - I merely want the unit conversions mixed in with the human readable units.
Thanks in advance.
I ended up using below:
import * as _ from 'lodash'
import moment from 'moment'
import momentDurationFormat from 'moment-duration-format'
export const convertTime = (time, format = 's') => {
if(!_.isString(time) || !_.isString(format)){
return 0;
}
const components = time.trim().split(/(\d+)/);
const digits = parseInt(components[1]);
const unit = components[2];
return moment
.duration(digits, unit)
.format(format);
};
Try to use timestring library. It parse a human readable time string into a time based value (by default in seconds). As I see, it can all you need:
const timestring = require('timestring')
let str = '1d 3h 25m 18s'
let time = timestring(str)
console.log(time) // will log 98718
str input param can be set without spaces, for example, '1d3h25m18s'

Javascript: finding the time difference between two 'time' strings without involving date

I have two <input type="time">. By default, each input collects a time value as a string. For example, "08:30".
How can I convert this string into an object which would then enable computation? Is it possible to avoid involving the use of date in this approach?
In the end, I would like to compute the difference between two time strings and then return the result in minutes. For example, the expected return value of08:00 and 09:00 would be 60 minutes.
Just do it as if you only had pen and paper:
12:45 => 12 × 60 + 45 = 765 minutes
08:30 => 8 × 60 + 30 = 510 minutes
765 - 510 = 255
Integer division: 255 / 60 = 4 hours
Remainer: 255 - 60 × 4 = 15 minutes
Result: 04:15
You can parse from string using regular expressions:
var parts = "08:45".match(/^(\d+):(\d+)$/);
console.log(+parts[1], +parts[2], +parts[1] * 60 + +parts[2]);
... and formatting back to string should not be very difficult either.
Assuming you want to use a 24h clock:
function minutesBetween (a, b) {
return Math.abs(toMinutes(b) - toMinutes(a))
}
function toMinutes (time) {
time = /^(\d{1,2}):(\d{2})$/.exec(time)
return time[1]*60 + +time[2]
}
console.log(minutesBetween('8:30', '9:30')) //=> 60
Generally speaking I whould suggest using Date insted of using custom functions, but just for your case this is the example function:
const timeStart = "8:00:00";
const timeEnd = "8:10:00";
//Handles only time with format in hh:mm or hh:mm:ss
//For more complicated cases use Date
function diff(start, end) {
const startMinutes = getMinutes(start);
const endMinutes = getMinutes(end);
return endMinutes - startMinutes
}
function getMinutes(strTime) {
const time = strTime.split(':');
return (time[0] * 60 + time[1]*1);
}
alert(diff(timeStart, timeEnd));
Note that this function is not responsible for validation of the time difference only the computation. You should validate you input

Changing milliseconds from 3 digits to 2 digits

I am having trouble making a stopwatch that only uses 2 digits for the milliseconds part. I have the full JSFiddle here. The function I could use some help with is the formatter() method.
Right now, the method looks like this:
formatter(timeInMilliseconds) {
const padZero = (time) => {
while (time.length < 2) {
time = '0' + time;
}
return time;
}
let time = new Date(timeInMilliseconds);
let minutes = padZero(time.getMinutes().toString());
let seconds = padZero(time.getSeconds().toString());
let milliseconds = padZero((time.getMilliseconds() / 10).toFixed(0));
let output = `${minutes} : ${seconds} . ${milliseconds}`;
console.log(output);
return output;
}
For the most part, it works. The problem though is very visible if you look at the console of my JSFiddle while the timer is running. For example, if the stopwatch is currently at something like 00 : 15 . 99, it will become 00 : 15 . 100 at the next tick instead of 00 : 16 . 00.
Any help would be appreciated.
toFixed rounds rather than truncating, so 995 milliseconds and up will become 99.5 and be formatted to 100 by toFixed. You can convert it to an integer and then to a string instead to truncate it:
let milliseconds = padZero('' + (time.getMilliseconds() / 10 | 0));
It might also be a nice simplification to make padZero accept a number rather than a string:
function padZero(time) {
return time < 10 ? '0' + time : '' + time;
}
let time = new Date(timeInMilliseconds);
let minutes = padZero(time.getMinutes());
let seconds = padZero(time.getSeconds());
let milliseconds = padZero(time.getMilliseconds() / 10 | 0);
let output = `${minutes} : ${seconds} . ${milliseconds}`;
Finally, if timeInMilliseconds isn’t a timestamp in milliseconds since 1970-01-01 00:00:00 UTC and is instead a duration, it’s inappropriate to convert it to a Date. Just do some math:
const minutes = padZero(timeInMilliseconds / 60000 | 0);
const seconds = padZero((timeInMilliseconds / 1000 | 0) % 60);
const centiseconds = padZero((timeInMilliseconds / 10 | 0) % 100);
Your problem is that .toFixed() rounds instead of truncating.
(99.4).toFixed(0) == '99'
(99.5).toFixed(0) == '100'
All you need to do is replace
(time.getMilliseconds() / 10).toFixed(0)
with
Math.floor(time.getMilliseconds() / 10).toFixed(0)
and it'll work.
You can use substring()
let milliseconds = padZero((time.getMilliseconds() / 10).toFixed(0)).substr(0, 2);

Categories