How to load a site depending on hours and minutes - javascript

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);

Related

How to create a stopwatch lap time function in Javascript

I am trying to create a stopwatch app that displays lap times. Currently I have it displaying the time when the lap button is clicked. However, I want to display the difference between time a and time b, time b and time c and so forth.
Can anyone suggest a way to do this?
I have looked at different responses on various blogs and stack overflow but haven't found one I fully understood. So I didn't want to just copy code.
I have included a snippet of the code I believe to be relative to the problem.
const timer = document.getElementById("stopwatch");
const startButton = document.getElementById("startButton");
let lapNumber = 0;
let [hour, min, sec, centisecond] = [0, 0, 0, 0];
let stopTime = true;
let [lapHour, lapMin, lapSec, lapCentiSec] = [0, 0, 0, 0];
function startTimer() {
if (startButton.innerText === "Start") {
startButton.innerText = "Stop";
stopTime = false;
timerCycle();
} else if (startButton.innerText === "Stop") {
startButton.innerText = "Start";
stopTime = true;
}
}
function timerCycle() {
if (stopTime === false) {
// Set timings to numbers
centisecond = parseInt(centisecond);
sec = parseInt(sec);
min = parseInt(min);
hour = parseInt(hour);
centisecond = centisecond + 1;
if (centisecond == 100) {
sec = sec + 1;
centisecond = 0;
}
if (sec === 60) {
min = min + 1;
sec = 0;
}
if (min === 60) {
hour = hour + 1;
min = 0;
sec = 0;
}
if (centisecond < 10 || centisecond === 0) {
centisecond = "0" + centisecond;
}
if (sec < 10 || sec === 0) {
sec = "0" + sec;
}
if (min < 10 || min === 0) {
min = "0" + min;
}
if (hour < 10 || hour === 0) {
hour = "0" + hour;
}
timer.innerHTML = `${hour}:${min}:${sec}:${centisecond}`;
setTimeout("timerCycle()", 10);
}
}
function lapTime() {
lapNumber++;
const lapTimes = `${lapNumber}: ${timer.innerHTML}`;
setLocalStorage("laps", lapTimes);
}
function setLocalStorage(key, data) {
const lapTimes = JSON.parse(localStorage.getItem(key)) || [];
lapTimes.push(data);
localStorage.setItem(key, JSON.stringify(lapTimes));
displayLaps(lapTimes);
}
function displayLaps(lapTimes) {
const lapContainer = document.querySelector("#lapList");
const laps = document.createElement("li");
laps.className = "lap-time";
if (timer.innerHTML === "00:00:00:00") {
laps.innerHTML = "Please press start.";
lapContainer.appendChild(laps);
return;
} else {
const pressStart = Array.from(document.getElementsByClassName("lap-time"));
pressStart.forEach((text) => {
if (text.innerHTML === "Please press start.") {
text.remove();
}
});
}
lapTimes.forEach((lapTime) => {
laps.innerHTML = lapTime;
});
lapContainer.appendChild(laps);
}
Don't bother with all those centisecsonds etc.
Just get the number of millsecs of the current time when 'Start' is clicked
let d = new Date();
let start_ms = d.valueOf();
then do the same again when 'Stop' is clicked, and subtract the values to find the milisecs elapsed. Then convert to readable hours, mins secs for the user.

If Statement Returning True for Wrong Statement

Wrote a simple little app to check what period it is a display it with Javascript. Works great for the most part - but at certain times it will display the next period instead of the current one.
How do I test it without waiting and manually checking?
I'd obviously like to figure out why it displays the wrong period
Edit: If the time is 15:06 (3:06PM) it will display the last else statement instead of the second to last one
Code:
var now = '';
//Set Periods
//Slot 1+2 are periods - slots 3+4 are passing times inbetween periods
var periods = [
['Before School', 5.00, 7.59],
['First', 8.00, 8.49, 8.50, 8.54],
['Second', 8.55, 9.44, 9.45, 9.49],
['Third', 9.50, 10.39, 10.40, 10.44],
['Fourth', 10.45, 11.34, 11.35, 11.39],
['Fifth', 11.40, 12.29, 12.30, 12.34],
['Sixth', 12.35, 13.24, 13.25, 13.29],
['Seventh', 13.30, 14.19, 14.20, 14.24],
['Eighth', 14.25, 15.15]
];
//Display Period
function displayPeriod() {
if (now >= periods[0][1] && now <= periods[0][2]) {
document.getElementById('period').innerHTML = ('School has not started yet');
} else if (now >= periods[1][1] && now <= periods[1][2]) {
document.getElementById('period').innerHTML = ('1st');
} else if (now >= periods[1][3] && now <= periods[1][4]) {
document.getElementById('period').innerHTML = ('1st Passing');
} else if (now >= periods[2][1] && now <= periods[2][2]) {
document.getElementById('period').innerHTML = ('2nd');
} else if (now >= periods[2][3] && now <= periods[2][4]) {
document.getElementById('period').innerHTML = ('2nd Passing');
} else if (now > periods[3][1] && now <= periods[3][2]) {
document.getElementById('period').innerHTML = ('3rd');
} else if (now >= periods[3][3] && now <= periods[3][4]) {
document.getElementById('period').innerHTML = ('3rd Passing');
} else if (now >= periods[4][1] && now <= periods[4][2]) {
document.getElementById('period').innerHTML = ('4th');
} else if (now >= periods[4][3] && now <= periods[4][4]) {
document.getElementById('period').innerHTML = ('4th Passing');
} else if (now >= periods[5][1] && now <= periods[5][2]) {
document.getElementById('period').innerHTML = ('5th');
} else if (now >= periods[5][3] && now <= periods[5][4]) {
document.getElementById('period').innerHTML = ('5th Passing');
} else if (now >= periods[6][1] && now <= periods[6][2]) {
document.getElementById('period').innerHTML = ('6th');
} else if (now >= periods[6][3] && now <= periods[6][4]) {
document.getElementById('period').innerHTML = ('6th Passing');
} else if (now >= periods[7][1] && now <= periods[7][2]) {
document.getElementById('period').innerHTML = ('7th');
} else if (now >= periods[7][3] && now <= periods[7][4]) {
document.getElementById('period').innerHTML = ('7th Passing');
} else if (now >= periods[8][1] && now <= periods[8][2]) {
document.getElementById('period').innerHTML = ('8th');
} else {
document.getElementById('period').innerHTML = ('School is done for the day.');
}
}
//Check Time
function startTime() {
var today = new Date();
now = today.getHours() + '.' + today.getMinutes();
var h = today.getHours();
var m = today.getMinutes();
var s = today.getSeconds();
m = checkTime(m);
s = checkTime(s);
displayPeriod();
var t = setTimeout(startTime, 500);
}
function checkTime(i) {
if (i < 10) {
i = '0' + i
}; // add zero in front of numbers < 10
return i;
}
You can test the function by inputting the current time through startTime() function. You can comment out the setTimeout if you only want to test a specific function. You can check that the output for 15.06 is correct too.
var now = '';
//Set Periods
//Slot 1+2 are periods - slots 3+4 are passing times inbetween periods
var periods = [
['Before School', 5.00, 7.59],
['First', 8.00, 8.49, 8.50, 8.54],
['Second', 8.55, 9.44, 9.45, 9.49],
['Third', 9.50, 10.39, 10.40, 10.44],
['Fourth', 10.45, 11.34, 11.35, 11.39],
['Fifth', 11.40, 12.29, 12.30, 12.34],
['Sixth', 12.35, 13.24, 13.25, 13.29],
['Seventh', 13.30, 14.19, 14.20, 14.24],
['Eighth', 14.25, 15.15]
];
//Display Period
function displayPeriod() {
console.log(now)
if (now >= periods[0][1] && now <= periods[0][2]) {
console.log('School has not started yet');
} else if (now >= periods[1][1] && now <= periods[1][2]) {
console.log('1st');
} else if (now >= periods[1][3] && now <= periods[1][4]) {
console.log('1st Passing');
} else if (now >= periods[2][1] && now <= periods[2][2]) {
console.log('2nd');
} else if (now >= periods[2][3] && now <= periods[2][4]) {
console.log('2nd Passing');
} else if (now > periods[3][1] && now <= periods[3][2]) {
console.log('3rd');
} else if (now >= periods[3][3] && now <= periods[3][4]) {
console.log('3rd Passing');
} else if (now >= periods[4][1] && now <= periods[4][2]) {
console.log('4th');
} else if (now >= periods[4][3] && now <= periods[4][4]) {
console.log('4th Passing');
} else if (now >= periods[5][1] && now <= periods[5][2]) {
console.log('5th');
} else if (now >= periods[5][3] && now <= periods[5][4]) {
console.log('5th Passing');
} else if (now >= periods[6][1] && now <= periods[6][2]) {
console.log('6th');
} else if (now >= periods[6][3] && now <= periods[6][4]) {
console.log('6th Passing');
} else if (now >= periods[7][1] && now <= periods[7][2]) {
console.log('7th');
} else if (now >= periods[7][3] && now <= periods[7][4]) {
console.log('7th Passing');
} else if (now >= periods[8][1] && now <= periods[8][2]) {
console.log('8th');
} else {
console.log('School is done for the day.');
}
}
//Check Time
function startTime(hours, minutes, seconds) {
var today = new Date();
var h = hours != null ? hours : today.getHours(); // use current time if input is empty
var m = minutes != null ? minutes : today.getMinutes(); // use current time if input is empty
var s = seconds != null ? seconds : today.getSeconds(); // use current time if input is empty
m = checkTime(m);
s = checkTime(s);
now = h + '.' + m; // move it here to correspond with input
displayPeriod();
// var t = setTimeout(startTime, 500);
}
function checkTime(i) {
if (i < 10) {
i = '0' + i
}; // add zero in front of numbers < 10
return i;
}
startTime();
startTime(15, 6, 0);
You should check time (add zero in front of numbers < 10) before set now variable
function startTime() {
var today = new Date();
var h = checkTime(today.getHours());
var m = checkTime(today.getMinutes());
now = h + '.' + m;
displayPeriod();
var t = setTimeout(startTime, 500);
}
Where your problem is:
Your startTime function is setting now like this:
now = today.getHours() + '.' + today.getMinutes();
It's then calculating zero-padded hour and minute values, but these are not used!
So, instead of representing nine minutes past eight as "9.08", it is using "9.8".
How to test your code
You probably want to learn about unit testing, but that is too big a topic to go over here.
Some of the prinicpals of writing testable code can be applied here, though.
First, separate logic from updating UI (or DOM), by refactoring your DisplayPeriod function to return a string value instead of modifying the DOM:
function displayPeriod() {
if (now >= periods[0][1] && now <= periods[0][2]) {
return 'School has not started yet';
} else if (now >= periods[1][1] && now <= periods[1][2]) {
return '1st';
} else if (now >= periods[1][3] && now <= periods[1][4]) {
return '1st Passing';
} else if (now >= periods[2][1] && now <= periods[2][2]) {
return '2nd';
// (Snip)
} else {
return 'School is done for the day.';
}
}
This method would then be used by another method which updates the DOM.
Second, allow for injection of dependencies. E.g. You have an implicit dependency on the system clock via the Date() constructor. If you refactor StartTime to accept a date, then your test code can pass in whichever date values it needs to test different cases:
// Note that bugs in this method have not been fixed!
function startTime(today) {
now = today.getHours() + '.' + today.getMinutes();
var h = today.getHours();
var m = today.getMinutes();
var s = today.getSeconds();
m = checkTime(m);
s = checkTime(s);
// Commented out, as interfers with testing (should be moved to DOM setting method).
// var t = setTimeout(startTime, 500);
}
Third, use a test framework to run various scenarios and check expected and actual results. Here's a poor man's test case and test execution script:
function TestCase(time, expectedResult) {
startTime(time)
var result = displayPeriod();
if (result == expectedResult) {
console.log("Passed for " + time)
} else {
console.log("Failed for " + time + "(" + result + ")");
}
}
for (minute = 0; minute < 50; minute++) {
time = new Date(2018, 11, 14, 8, minute);
TestCase(time, "1st");
}

Comparison with parsed numbers fails

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 )

setInterval works just one time, then stops

I create a countdown function but it works just at the begining and then stops.
var rek_inter = setInterval(cnt(s_, d), 1000);
function cnt(deg, deg2) {
deg--;
while (deg < 0) {
deg = 59;
deg2--;
}
if (deg2 < 0) {
$('#s_').html("ok");
} else if (deg2 >= 0) {
var d_sn = fixd(deg2);
var s_sn = fixd(deg);
$('#s_').html(d_sn + ":" + s_sn);
}
}
function fixd(g) {
if (g < 10) {
return '0' + g;
}
return g;
}
I tried that, too;
var rek_inter = setInterval(function() {cnt(s_, d);}, 1000);
But result was same.
If I put the function into the interval function like that:
var rek_inter = setInterval(function () {
s_--;
while (s_ < 0) {
s_ = 59;
d--;
}
if (d < 0) {
$('#s_').html("ok");
} else if (d >= 0) {
var d_sn = fixd(d);
var s_sn = fixd(s_);
$('#s_').html(d_sn + ":" + s_sn);
}
}, 1000);
function fixd(g) {
if (g < 10) {
return '0' + g;
}
return g;
}
It works. But I need that as I wrote first at the top. What could be the problem and solution here?
"I have lot of s_, s_2, s_3.... and d, d1, d2... values and I want to use them in single setInterval, this is why I am tryng to use cnt(s_, d); if that will work I will write cnt(s_2, d2), cnt(s_3, d3).."
You could just use closures to your advantage here.
var rek_inter1 = setInterval(cnt(s_2, d2), 1000),
rek_inter2 = setInterval(cnt(s_3, d3), 1000);
function cnt(deg, deg2) {
return function () {
deg--;
while (deg < 0) {
deg = 59;
deg2--;
}
if (deg2 < 0) {
$('#s_').html("ok");
} else if (deg2 >= 0) {
var d_sn = fixd(deg2);
var s_sn = fixd(deg);
$('#s_').html(d_sn + ":" + s_sn);
}
};
}
The first attempt is the same as :
setInterval ( value, time) ;
Here value = cnt(s_, d), the result of the call of the cnt function.
This cannot work, since setInterval expects a function. Too bad it silently fails in javascript.
In the second attempt, the issue is that you modify only function var, so no change can occur : you change deg, deg2, when in fact you would like to change s_ and d.
The third attempt is right, since you both invoke a function and change the globals s_ and d.
I would rather write it this way :
var rek_inter = setInterval( iterate , 1000);
function iterate () {
s_--;
while (s_ < 0) {
s_ = 59;
d--;
}
if (d < 0) {
$('#s_').html("ok");
} else if (d >= 0) {
var d_sn = fixd(d);
var s_sn = fixd(s_);
$('#s_').html(d_sn + ":" + s_sn);
}
}
Edit :
The O.P. mentionned that he wants to handle a set of (s_, d ) parameters.
I suggest you create an array of object which contains such parameters :
var sdParameters = [];
sdParameters.push( { s : some value , d: some other value} );
sdParameters.push( { s : some value 2, d: some other value 2 } );
... // (or using a for loop to grab the ds and ss if possible)
Then after each s/d object is defined by its index, so with :
function iterate (ind) {
var s_ = --sdParameters[ind].s ;
while (s_ < 0) {
s_ = 59;
sdParameters[ind].d--;
}
var d = sdParameters[ind].d;
if (d < 0) {
$('#s_').html("ok");
} else if (d >= 0) {
var d_sn = fixd(d);
var s_sn = fixd(s_);
$('#s_').html(d_sn + ":" + s_sn);
}
}
you can have all your intervals running on one single global array with :
var rek_inter = setInterval( iterate.bind(null,0) , 1000);
var rek_inter1 = setInterval( iterate.bind(null,1) , 1000);
(obviously you can/should store the intervals in an array, you might store them within sdParameters.
One last remark : i couldn't use relevant variable names, since i couldn't guess the use. Using significant names in your code can be of great help when things gets more complex.

Converting Youtube Data API V3 video duration format to seconds in JavaScript/Node.js

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())
}

Categories

Resources