Skip to content

Instantly share code, notes, and snippets.

@rwaldron
Created February 4, 2020 17:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rwaldron/6e8f91b7a92a14ef0f1e12e7c8ded356 to your computer and use it in GitHub Desktop.
Save rwaldron/6e8f91b7a92a14ef0f1e12e7c8ded356 to your computer and use it in GitHub Desktop.
const tsToSeconds = (timestamp, framerate) => {
/*
Parse a timestamp into seconds
Acceptable formats are:
- HH:MM:SS.MMM
- MM:SS.MMM
- SS.MMM
- HH:MM:SS;FF
- MM:SS;FF
- SS;FF
Hours and minutes are optional. Default to 0
Seconds MUST be specified
Seconds can be followed by milliseconds OR by the frame number
*/
const RADIX = 10;
const validformat = /^([0-9]+:){0,2}[0-9]+([.;][0-9]+)?$/;
let first;
let last;
let lastIndex;
let pairs;
if (typeof timestamp === "number") {
return timestamp;
}
if (typeof timestamp === "string" &&
!validformat.test(timestamp)) {
throw new Error(`
Invalid time format.
'${timestamp}' must be formatted as any one of:
- HH:MM:SS.MMM
- MM:SS.MMM
- SS.MMM
- HH:MM:SS;FF
- MM:SS;FF
- SS;FF
`.trim());
}
pairs = timestamp.split(":");
lastIndex = pairs.length - 1;
last = pairs[lastIndex];
// Fix last element:
if (last.includes(";")) {
if (!framerate || typeof framerate !== "number") {
throw new Error("Missing or invalid 'framerate' argument.");
}
let [ss, ff] = last.split(";");
let SS = parseInt(ss, RADIX);
let FF = parseInt(ff, RADIX);
let frameAsTime = 0;
if (FF > framerate) {
throw new Error(`
Frame number is greater than framerate:
- Frame: ${FF}
- Framerate: ${framerate}
`.trim());
}
pairs[lastIndex] = SS + (FF / framerate);
}
first = pairs[0];
return [
/* intentionally empty */,
parseFloat(first, RADIX),
(parseInt(first, RADIX) * 60) +
parseFloat(pairs[1], RADIX),
(parseInt(first, RADIX) * 3600) +
(parseInt(pairs[1], RADIX) * 60) +
parseFloat(pairs[2], RADIX)
][pairs.length || 1];
};
// ------------- TESTING ---------------
//
const tolerance = 0.0001;
const framerate = 24;
const tests = [
[0.333, 0.333],
[5.333, 5.333],
[6, 6.000],
[6.004, 6.004],
[9.11111111, 9.11111111],
// *SS.MMM
["4.003", 4.003],
["01.234", 1.234],
["6.78", 6.780],
["8.090", 8.090],
// SS;FF
["10;4", 10.1666],
["10;17", 10.7083],
["12;1", 12.0416],
["13;2", 13.0833],
["20;11", 20.4583],
["23;17", 23.7083],
["27;7", 27.2916],
["27;22", 27.9166],
// HH:MM:SS;FF
["12:04;12", 724.5],
["22:59;23", 1379.9583],
["1:48:27;9", 6507.375],
["3:23:15;1", 12195.0416],
["12:56;7", 776.2916],
["12:56;7", 776.2916],
["2:02:42;8", 7362.3333],
];
const fuzzyEquivalent = (a, b, tolerance = 0.0001) => {
return (a < (b + tolerance)) && (a > (b - tolerance));
};
const evaluate = (a, b, t) => fuzzyEquivalent(a, b, t) ? "PASSED" : "FAILED";
const message = (input, expect, output) =>
`Conversion of '${input}' should be ${expect} ±${tolerance}. (Received: ${output})`;
for (let [input, expect] of tests) {
const output = tsToSeconds(input, framerate);
const result = evaluate(output, expect, tolerance);
console.log(result, message(input, expect, output));
}
const trythese = [
["Frame number is greater than framerate", () => tsToSeconds("10;25", framerate)],
["Invalid time format", () => tsToSeconds("")],
["Missing or invalid 'framerate' argument.", () => tsToSeconds("00:00;1")],
];
for (let [expect, tryable] of trythese) {
try {
tryable();
} catch (error) {
let result = 'FAILED';
if (error.message.includes(expect)) {
result = 'PASSED';
}
console.log(`${result} ${error}`);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment