I am trying to calculate time difference and if time difference is greater than 20 mins, I want to display an alert message and then exit.
I don't intend to loop through all the rows and want to exit the function after finding out the first host which has minutes < 20.
this.dataSource.filteredData.forEach(
async row => {
this.selection.select(row);
// const host = row.hostName.substring(0, row.hostName.indexOf('.'));
const host = 'abc';
const prevData = await this.myService.getData(host);
const timeDiff = Math.abs(new Date().getTime() - new Date(prevData[0].dateAt).getTime());
const minutes = Math.floor((timeDiff / 1000) / 60);
if (minutes < 20) {
window.alert('Please check after ' + (20 - minutes) + ' minutes.');
return false;
}
});
I am trying to exit the function using return false but it is still looping through all the rows.
Updated code without foreach:
const f = (async row => {
const host = 'abc';
const prevData = this.myService.getData(host);
const timeDiff = Math.abs(new Date().getTime() - new Date(prevData[0].dateAt).getTime());
const minutes = Math.floor((timeDiff / 1000) / 60);
return minutes;
});
(async () => {
for (let i = 0; i < this.dataSource.filteredData.length; i++) {
const mins = await f(this.dataSource.filteredData[i]);
if (mins < 200) {
window.alert('Please retry after ' + (200 - mins) + ' minutes.');
break;
}
}
})();
In the above code, this.myService.getData(host) is returning null
Here is a minimally-viable representation of how the control should be programmed:
let rows = [1, 2, 3];
let f = (async row => {
return row === "2";
});
(async () => {
for (let i = 0; i < rows.length; i++) {
let result = await f(rows[i]);
console.log(result);
if (!result) {
break;
}
}
})();
I'm trying to compare an array of times with the current local time. But comparing the numbers fails for some values. As far as I could detect, it is not always failing the same time string. Im really out of ideas what the reason could be.
function lastPassedTimeIndex() {
const d = new Date();
const nowHr = d.getHours();
const nowMin = d.getMinutes();
let lastPassedTimeIndex = 0;
const currentTimes = ['05:20', '06:57', '12:46', '15:48', '18:30', '20:02'];
for (const time of currentTimes) {
const t = time.split(':');
const printHr = parseInt(t[0], 10);
const printMin = parseInt(t[1], 10);
if ((printHr < nowHr) && (printMin < nowMin)) {
console.log(time, 'previous from now');
lastPassedTimeIndex++;
}
else if ((printHr === nowHr) && (printMin < nowMin)) {
console.log(time, 'previous from now, but hour is correct');
lastPassedTimeIndex++;
}
else if ((printHr === nowHr) && (printMin >= nowMin)) {
console.log(time, 'is upcoming or now and hour is correct');
lastPassedTimeIndex++;
}
else if ((printHr > nowHr)) {
console.log(time, 'is upcoming, hour is larger');
}
else {
console.log('I have no idea!');
}
}
console.log('LastPassedTimeIndex ', lastPassedTimeIndex);
return lastPassedTimeIndex;
}
This is my console output:
> I have no idea!
> I have no idea!
> 12:46 is upcoming, hour is larger
> 15:48 is upcoming, hour is larger
> 18:30 is upcoming, hour is larger
> 20:02 is upcoming, hour is larger
> LastPassedTimeIndex 0
I only see one error, fixed this way:
const printHr = parseInt(t[0], 10);
const printMin = parseInt(t[1], 10);
if ((printHr < nowHr) /* Take out what was here */) {
console.log(time, 'previous from now');
lastPassedTimeIndex++;
}
The minutes have to be compared only when the hours are the same, or just compare as strings:
var time = new Date().toTimeString().slice(0, 5)
var times = ['05:20', '06:57', '12:46', '15:48', '18:30', '20:02']
console.log( time, times.findIndex(t => t >= time) - 1 )
I have four screens that I want to display in the following order.
Screen1(2 seconds) -> Screen2 (2 seconds) -> Screen3 (2 Seconds)
I also have a fourth screen which should only show when the time is between 05:55-06:05 and 17:55-18:05
In order to accomplish this my code looks like this till now:
function timecondition() {
var hours = new Date();
var minutes = new Date();
var h = hours.getHours();
var m = minutes.getMinutes();
var timecondition;
if((h == 5 && m >= 55) || (h == 6 && m <= 5) || (h == 17 && m >= 55) || (h == 18 && m <= 5)) {
timecondition = true;
}
else {
timecondition = false;
}
return timecondition;
}
$(document).ready(
function() {
setInterval(function() {
setTimeout(function() {
if(timecondition()) {
$('#show').load("http://localhost:8084/test/screen4");
}
else {
$('#show').load("http://localhost:8084/test/screen1");
}
}, 2000);
setTimeout(function() {
if(timecondition()) {
$('#show').load("http://localhost:8084/test/screen4");
}
else {
$('#show').load("http://localhost:8084/test/screen2");
}
}, 4000);
setTimeout(function() {
if(timecondition()) {
$('#show').load("http://localhost:8084/test/screen4");
}
else {
$('#show').load("http://localhost:8084/test/screen3");
}
}, 6000);
}, 6000);
}
);
Unfortunately it doesnt work like I want it to be.
When I start the webapplication at 05:54 the sequence(screen1->screen2->screen3)
But once the clock hits 05:55 it won't display the fourth screen, like it was in my intention.
When I start the application within the timecondition eg. at 05:56 it shows the fourth screen, but won't leave screen4 when the timecondition is not true anymore a few minutes later.
Is it because I need dynamic functions?
Couple of potential bugs there.
1. You are taking the time twice
Every time you call new Date() you are taking a snapshot of an instant. In this case they are only milliseconds away from each other, but it's a bug nonetheless.
var hours = new Date();
var minutes = new Date();
One object should be enough:
var now = new Date();
var h = now.getHours();
var m = now.getMinutes()
2. Your time condition is wrong
var timecondition;
if((h == 5 || h == 6 || h == 17 || h == 18) && (m >= 55 || m <= 05)) {
timecondition = true;
}
else {
timecondition = false;
}
return timecondition;
There is two problems with this:
a) It is hiding the function name from within. This is just a minor bug and doesn't affect the functionality here yet.
b) You are checking for hours and minutes independently. This IS a serious bug because it doesn't comply with your business logic.
That whole code above can be smarter rewritten as:
h = h % 12
return (h == 5 && m >= 55) || (h == 6 && m <= 5)
3. You reload the page every few seconds
The second argument to setInterval and setTimeout respectively is in milliseconds. So you are issuing a load every 2 seconds.
4. You are nesting timeouts within intervals
This basically means that every six seconds you are setting a timer for the next 2, 4 and 6 seconds. This is not really a bug, but unnecessarily complex. Why not set one interval for running every two seconds?
Here's some refactored and hopefully fixed code. Didn't try it out yet, though.
function slideshow() {
var screens = [
"http://localhost:8084/test/screen1",
"http://localhost:8084/test/screen2",
"http://localhost:8084/test/screen3"
];
var specialScreen = "http://localhost:8084/test/screen4";
// Contains the index of currently shown screen or -1
// when the special screen is shown
var currentScreen = 0;
// Cache the element here so we don't need to search for it every two seconds
var show = $('#show');
function timecondition() {
var now = new Date();
var h = now.getHours();
var m = now.getMinutes();
h = h % 12;
return (h == 5 && m >= 55) || (h == 6 && m <= 5);
}
function update() {
if (timecondition()) {
if (currentScreen != -1) {
show.load(specialScreen);
currentScreen = -1;
}
return;
}
currentScreen = (currentScreen + 1) % screens.length;
show.load(screens[currentScreen]);
}
setInterval(update, 2000);
}
$(document).ready(slideshow);
If you wanted different durations for the screens, you could do it roughly like this:
function slideshow() {
var screens = [
{url: "http://localhost:8084/test/screen1", t: 2000},
{url: "http://localhost:8084/test/screen2", t: 3000},
{url: "http://localhost:8084/test/screen3", t: 10000}
];
var specialScreen = "http://localhost:8084/test/screen4";
// Contains the index of currently shown screen or -1
// when the special screen is shown
var currentScreen = 0;
// Cache the element here so we don't need to search for it every two seconds
var show = $('#show');
function timecondition() {
var now = new Date();
var h = now.getHours();
var m = now.getMinutes();
h = h % 12;
return (h == 5 && m >= 55) || (h == 6 && m <= 5);
}
var step = 1000;
var screenTimer = 0;
function update() {
if (timecondition()) {
if (currentScreen != -1) {
show.load(specialScreen);
currentScreen = -1;
}
return;
}
if ((screenTimer += step) >= screeens[currentScreen].t) {
currentScreen = (currentScreen + 1) % screens.length;
show.load(screens[currentScreen].url);
screenTimer = 0;
}
}
setInterval(update, step);
}
$(document).ready(slideshow);
I'm trying to convert ISO 8601 string to seconds in JS/Node. The best I could come up with was:
function convert_time(duration) {
var a = duration.match(/\d+/g)
var duration = 0
if(a.length == 3) {
duration = duration + parseInt(a[0]) * 3600;
duration = duration + parseInt(a[1]) * 60;
duration = duration + parseInt(a[2]);
}
if(a.length == 2) {
duration = duration + parseInt(a[0]) * 60;
duration = duration + parseInt(a[1]);
}
if(a.length == 1) {
duration = duration + parseInt(a[0]);
}
return duration
}
It works when I input strings such as "PT48S", "PT3M20S" or "PT3H2M31S", but fails miserably if the string is "PT1H11S". Does anyone have a better idea?
If you're using moment.js you can simply call...
moment.duration('PT15M33S').asMilliseconds();
= 933000 ms
EDIT 2021: While this works, and still gets upvotes, I wouldn't advise including moment.js just for this. I'd recommend using a regex answer like #redgetan's
function YTDurationToSeconds(duration) {
var match = duration.match(/PT(\d+H)?(\d+M)?(\d+S)?/);
match = match.slice(1).map(function(x) {
if (x != null) {
return x.replace(/\D/, '');
}
});
var hours = (parseInt(match[0]) || 0);
var minutes = (parseInt(match[1]) || 0);
var seconds = (parseInt(match[2]) || 0);
return hours * 3600 + minutes * 60 + seconds;
}
works for these cases:
PT1H
PT23M
PT45S
PT1H23M
PT1H45S
PT23M45S
PT1H23M45S
I suggest this little hack to prevent your problematic case:
function convert_time(duration) {
var a = duration.match(/\d+/g);
if (duration.indexOf('M') >= 0 && duration.indexOf('H') == -1 && duration.indexOf('S') == -1) {
a = [0, a[0], 0];
}
if (duration.indexOf('H') >= 0 && duration.indexOf('M') == -1) {
a = [a[0], 0, a[1]];
}
if (duration.indexOf('H') >= 0 && duration.indexOf('M') == -1 && duration.indexOf('S') == -1) {
a = [a[0], 0, 0];
}
duration = 0;
if (a.length == 3) {
duration = duration + parseInt(a[0]) * 3600;
duration = duration + parseInt(a[1]) * 60;
duration = duration + parseInt(a[2]);
}
if (a.length == 2) {
duration = duration + parseInt(a[0]) * 60;
duration = duration + parseInt(a[1]);
}
if (a.length == 1) {
duration = duration + parseInt(a[0]);
}
return duration
}
Fiddle
Here's my solution:
function parseDuration(duration) {
var matches = duration.match(/[0-9]+[HMS]/g);
var seconds = 0;
matches.forEach(function (part) {
var unit = part.charAt(part.length-1);
var amount = parseInt(part.slice(0,-1));
switch (unit) {
case 'H':
seconds += amount*60*60;
break;
case 'M':
seconds += amount*60;
break;
case 'S':
seconds += amount;
break;
default:
// noop
}
});
return seconds;
}
My solution:
function convert_time(duration) {
var total = 0;
var hours = duration.match(/(\d+)H/);
var minutes = duration.match(/(\d+)M/);
var seconds = duration.match(/(\d+)S/);
if (hours) total += parseInt(hours[1]) * 3600;
if (minutes) total += parseInt(minutes[1]) * 60;
if (seconds) total += parseInt(seconds[1]);
return total;
}
Fiddle
You can find a very simple PHP solution here - How To Convert Youtube API Time (ISO 8601 String Video Duration) to Seconds In PHP - Code
This function convert_time() takes one parameter as input - the Youtube API Time (Video Duration) which is in ISO 8601 string format and returns its duration in seconds.
function convert_time($str)
{
$n = strlen($str);
$ans = 0;
$curr = 0;
for($i=0; $i<$n; $i++)
{
if($str[$i] == 'P' || $str[$i] == 'T')
{
}
else if($str[$i] == 'H')
{
$ans = $ans + 3600*$curr;
$curr = 0;
}
else if($str[$i] == 'M')
{
$ans = $ans + 60*$curr;
$curr = 0;
}
else if($str[$i] == 'S')
{
$ans = $ans + $curr;
$curr = 0;
}
else
{
$curr = 10*$curr + $str[$i];
}
}
return($ans);
}
Testing Some Inputs:
"PT2M23S" => 143
"PT2M" => 120
"PT28S" => 28
"PT5H22M31S" => 19351
"PT3H" => 10800
"PT1H6M" => 3660
"PT1H6S" => 3606
Here's #redgetan 's solution in ES6.
I also fixed it for years, weeks and days.
https://www.digi.com/resources/documentation/digidocs/90001437-13/reference/r_iso_8601_duration_format.htm
// Copied from:
// https://stackoverflow.com/questions/22148885/converting-youtube-data-api-v3-video-duration-format-to-seconds-in-javascript-no
function parseISO8601Duration(duration) {
const match = duration.match(/P(\d+Y)?(\d+W)?(\d+D)?T(\d+H)?(\d+M)?(\d+S)?/)
// An invalid case won't crash the app.
if (!match) {
console.error(`Invalid YouTube video duration: ${duration}`)
return 0
}
const [
years,
weeks,
days,
hours,
minutes,
seconds
] = match.slice(1).map(_ => _ ? parseInt(_.replace(/\D/, '')) : 0)
return (((years * 365 + weeks * 7 + days) * 24 + hours) * 60 + minutes) * 60 + seconds
}
if (parseISO8601Duration('PT1H') !== 3600) {
throw new Error()
}
if (parseISO8601Duration('PT23M') !== 1380) {
throw new Error()
}
if (parseISO8601Duration('PT45S') !== 45) {
throw new Error()
}
if (parseISO8601Duration('PT1H23M') !== 4980) {
throw new Error()
}
if (parseISO8601Duration('PT1H45S') !== 3645) {
throw new Error()
}
if (parseISO8601Duration('PT1H23M45S') !== 5025) {
throw new Error()
}
if (parseISO8601Duration('P43W5DT5M54S') !== 26438754) {
throw new Error()
}
if (parseISO8601Duration('P1Y43W5DT5M54S') !== 57974754) {
throw new Error()
}
I've written a CoffeeScript variation (you can easily compile it at coffeescript.org when desired)
DIFFERENCE: the returning duration comes in a human readable format (e.g. 04:20, 01:05:48)
String.prototype.parseDuration = ->
m = #.match /[0-9]+[HMS]/g
res = ""
fS = fM = !1
for part in m
unit = part.slice -1
val = part.slice 0, part.length - 1
switch unit
when "H" then res += val.zeros( 2 ) + ":"
when "M"
fM = 1
res += val.zeros( 2 ) + ":"
when "S"
fS = 1
res += if fM then val.zeros 2 else "00:" + val.zeros 2
if !fS then res += "00"
res
I've also implemented this helper function to fill < 10 values with a leading zero:
String.prototype.zeros = ( x ) ->
len = #length
if !x or len >= x then return #
zeros = ""
zeros += "0" for [0..(x-len-1)]
zeros + #
3nj0y!!!
I realize eval is unpopular, but here's the easiest and fastest approach I can imagine. Enjoy.
function formatDuration(x) {
return eval(x.replace('PT','').replace('H','*3600+').replace('M','*60+').replace('S', '+').slice(0, -1));
}
I think using moment.js will be an easier solution. But if someone is looking for a custom solution, here is a simple regex one for you:
var regex = /PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/;
var regex_result = regex.exec("PT1H11S"); //Can be anything like PT2M23S / PT2M / PT28S / PT5H22M31S / PT3H/ PT1H6M /PT1H6S
var hours = parseInt(regex_result[1] || 0);
var minutes = parseInt(regex_result[2] || 0);
var seconds = parseInt(regex_result[3] || 0);
var total_seconds = hours * 60 * 60 + minutes * 60 + seconds;
I ran into issues with the above solution. I decided to write it as obtuse as possible. I also use my own "getIntValue" in place of parseInt for extra sanity.
Just thought other searching might appreciate the update.
Fiddle
function convertYouTubeTimeFormatToSeconds(timeFormat) {
if ( timeFormat === null || timeFormat.indexOf("PT") !== 0 ) {
return 0;
}
// match the digits into an array
// each set of digits into an item
var digitArray = timeFormat.match(/\d+/g);
var totalSeconds = 0;
// only 1 value in array
if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') == -1 && timeFormat.indexOf('S') == -1) {
totalSeconds += getIntValue(digitArray[0]) * 60 * 60;
}
else if (timeFormat.indexOf('H') == -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') == -1) {
totalSeconds += getIntValue(digitArray[0]) * 60;
}
else if (timeFormat.indexOf('H') == -1 && timeFormat.indexOf('M') == -1 && timeFormat.indexOf('S') > -1) {
totalSeconds += getIntValue(digitArray[0]);
}
// 2 values in array
else if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') == -1) {
totalSeconds += getIntValue(digitArray[0]) * 60 * 60;
totalSeconds += getIntValue(digitArray[1]) * 60;
}
else if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') == -1 && timeFormat.indexOf('S') > -1) {
totalSeconds += getIntValue(digitArray[0]) * 60 * 60;
totalSeconds += getIntValue(digitArray[1]);
}
else if (timeFormat.indexOf('H') == -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') > -1) {
totalSeconds += getIntValue(digitArray[0]) * 60;
totalSeconds += getIntValue(digitArray[1]);
}
// all 3 values
else if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') > -1) {
totalSeconds += getIntValue(digitArray[0]) * 60 * 60;
totalSeconds += getIntValue(digitArray[1]) * 60;
totalSeconds += getIntValue(digitArray[2]);
}
// console.log(timeFormat, totalSeconds);
return totalSeconds;
}
function getIntValue(value) {
if (value === null) {
return 0;
}
else {
var intValue = 0;
try {
intValue = parseInt(value);
if (isNaN(intValue)) {
intValue = 0;
}
} catch (ex) { }
return Math.floor(intValue);
}
}
Python
It works by parsing the input string 1 character at a time, if the character is numerical it simply adds it (string add, not mathematical add) to the current value being parsed.
If it is one of 'wdhms' the current value is assigned to the appropriate variable (week, day, hour, minute, second), and value is then reset ready to take the next value.
Finally it sum the number of seconds from the 5 parsed values.
def ytDurationToSeconds(duration): #eg P1W2DT6H21M32S
week = 0
day = 0
hour = 0
min = 0
sec = 0
duration = duration.lower()
value = ''
for c in duration:
if c.isdigit():
value += c
continue
elif c == 'p':
pass
elif c == 't':
pass
elif c == 'w':
week = int(value) * 604800
elif c == 'd':
day = int(value) * 86400
elif c == 'h':
hour = int(value) * 3600
elif c == 'm':
min = int(value) * 60
elif c == 's':
sec = int(value)
value = ''
return week + day + hour + min + sec
This is not java specific, but i would like to add JAVA snippet as that may helpful to other users
String duration = "PT1H23M45S";
Pattern pattern = Pattern.compile("PT(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?");
Matcher matcher = pattern.matcher(duration);
long sec = 0;
long min = 0;
long hour = 0;
if (matcher.find())
{
if(matcher.group(1)!=null)
hour = NumberUtils.toInt(matcher.group(1));
if(matcher.group(2)!=null)
min = NumberUtils.toInt(matcher.group(2));
if(matcher.group(3)!=null)
sec = NumberUtils.toInt(matcher.group(3));
}
long totalSec = (hour*3600)+(min*60)+sec;
System.out.println(totalSec);
Assuming the input is valid, we can use the regex exec method to iterate on the string and extract the group sequentially:
const YOUTUBE_TIME_RE = /(\d+)([HMS])/g;
const YOUTUBE_TIME_UNITS = {
'H': 3600,
'M': 60,
'S': 1
}
/**
* Returns the # of seconds in a youtube time string
*/
function parseYoutubeDate(date: string): number {
let ret = 0;
let match: RegExpExecArray;
while (match = YOUTUBE_TIME_RE.exec(date)) {
ret += (YOUTUBE_TIME_UNITS[match[2]]) * Number(match[1]);
}
return ret;
}
ES6:
const durationToSec = formatted =>
formatted
.match(/PT(?:(\d*)H)?(?:(\d*)M)?(?:(\d*)S)?/)
.slice(1)
.map(v => (!v ? 0 : v))
.reverse()
.reduce((acc, v, k) => (acc += v * 60 ** k), 0);
Kotlin version:
private val youtubeDurationPattern: Pattern =
Pattern.compile("PT(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?")
fun String.parseDuration(): Int {
val matcher: Matcher = youtubeDurationPattern.matcher(this)
if (!matcher.find()) {
throw IllegalStateException("Cannot parse $this.")
}
val hour = matcher.group(1)?.toInt() ?: 0
val min = matcher.group(2)?.toInt() ?: 0
val sec = matcher.group(3)?.toInt() ?: 0
return hour * 3600 + min * 60 + sec
}
and test:
#Test
fun testParseDuration() {
assertEquals(10 * 60, "PT10M".parseDuration())
assertEquals(10 * 60 + 30, "PT10M30S".parseDuration())
assertEquals(30, "PT30S".parseDuration())
assertEquals(2 * 3600 + 3 * 60 + 16, "PT2H3M16S".parseDuration())
}
Suppose a carpark charges a $2 minimum fee to park for up to 3 hours, then the carpark charges an additional $0.5 per hour for each hour. For example, park for 5 hours charge $2+$0.5+$0.5=$3
How should I calculate the fee use for loop?
There's no need to use a for loop:
function calculateFee(hours) {
if (isNaN(hours) || hours <= 0) return 0;
if (hours <= 3) return 2;
var additionalHours = Math.round(hours - 3);
return 2 + 0.5 * additionalHours;
}
var fee = calculateFee(5);
And if using a for loop is a requirement:
function calculateFee(hours) {
if (isNaN(hours) || hours <= 0) return 0;
var result = 2;
if (hours <= 3) return result;
var additionalHours = Math.round(hours - 3);
for (i = 0; i < additionalHours; i++) {
result += 0.5;
}
return result;
}
And finally an example using objects:
function FeeCalculator(minimalFee, initialHours, additionalHourFee) {
if (isNaN(minimalFee) || minimalFee <= 0) { throw "minimalFee is invalid"; }
if (isNaN(initialHours) || initialHours <= 0) { throw "initialHours is invalid"; }
if (isNaN(additionalHourFee) || additionalHourFee <= 0) { throw "additionalHourFee is invalid"; }
this.minimalFee = minimalFee;
this.initialHours = initialHours;
this.additionalHourFee = additionalHourFee;
}
FeeCalculator.prototype = {
calculateFee: function(hours) {
if (hours <= this.initialHours) return this.minimalFee;
var additionalHours = Math.round(hours - this.initialHours);
return this.minimalFee + this.additionalHourFee * additionalHours;
}
};
var calculator = new FeeCalculator(2, 3, 0.5);
var fee = calculator.calculateFee(5);
May be like this, sorry If it is wrong, coz I didnt test it.
fee=0
if (hour>0){
fee=2;
hour-=3;
//without for loop
//if(hour>0)fee+=0.5*hour;
//with for loop
for(var i=0;i<hour;i++){
fee+=0.5;
}
}
return fee;