src/libnetdata/parsers/duration.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Duration Converter</title>
<style>
table {
width: 50%;
border-collapse: collapse;
margin-top: 20px;
}
table, th, td {
border: 1px solid black;
}
th, td {
padding: 10px;
text-align: center;
}
.error {
color: red;
margin-top: 10px;
}
</style>
</head>
<body>
<h1>Duration Converter</h1>
<input type="text" id="durationInput" placeholder="Enter duration (e.g., 10d-12h)">
<div id="errorMessage" class="error"></div>
<table id="resultTable">
<thead>
<tr>
<th>Unit</th>
<th>Value</th>
<th>Formatted</th>
<th>Check</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
const NSEC_PER_USEC = 1000;
const USEC_PER_MS = 1000;
const NSEC_PER_SEC = 1000000000;
const NSEC_PER_MS = USEC_PER_MS * NSEC_PER_USEC;
const NSEC_PER_MIN = NSEC_PER_SEC * 60;
const NSEC_PER_HOUR = NSEC_PER_MIN * 60;
const NSEC_PER_DAY = NSEC_PER_HOUR * 24;
const NSEC_PER_WEEK = NSEC_PER_DAY * 7;
const NSEC_PER_YEAR = NSEC_PER_DAY * 365;
const NSEC_PER_MONTH = NSEC_PER_DAY * 30;
const NSEC_PER_QUARTER = NSEC_PER_MONTH * 3;
const units = [
{ unit: "ns", formatter: true, multiplier: 1 },
{ unit: "us", formatter: true, multiplier: NSEC_PER_USEC },
{ unit: "ms", formatter: true, multiplier: NSEC_PER_MS },
{ unit: "s", formatter: true, multiplier: NSEC_PER_SEC },
{ unit: "m", formatter: true, multiplier: NSEC_PER_MIN },
{ unit: "min", formatter: false, multiplier: NSEC_PER_MIN },
{ unit: "h", formatter: true, multiplier: NSEC_PER_HOUR },
{ unit: "d", formatter: true, multiplier: NSEC_PER_DAY },
{ unit: "w", formatter: false, multiplier: NSEC_PER_WEEK },
{ unit: "wk", formatter: false, multiplier: NSEC_PER_WEEK },
{ unit: "mo", formatter: true, multiplier: NSEC_PER_MONTH },
{ unit: "M", formatter: false, multiplier: NSEC_PER_MONTH },
{ unit: "q", formatter: false, multiplier: NSEC_PER_QUARTER },
{ unit: "y", formatter: true, multiplier: NSEC_PER_YEAR },
{ unit: "Y", formatter: false, multiplier: NSEC_PER_YEAR },
{ unit: "a", formatter: false, multiplier: NSEC_PER_YEAR }
];
function durationFindUnit(unit) {
if (!unit) return units[0];
return units.find(u => u.unit === unit) || null;
}
function roundToResolution(value, resolution) {
if (value > 0) return Math.floor((value + (resolution - 1) / 2) / resolution);
if (value < 0) return Math.ceil((value - (resolution - 1) / 2) / resolution);
return 0;
}
function parseDouble(str) {
str = str.trim();
const match = str.match(/^[-+]?\d*\.?\d+/);
if (match) {
const number = parseFloat(match[0]);
const remainingStr = str.slice(match[0].length).trim();
return { number, remainingStr };
}
return { number: null, remainingStr: str };
}
function durationParse(duration, unit) {
if (!duration || !unit) return false;
let s = duration.trim();
let nsec = 0;
let isNegative = false;
// Handle leading negative sign
if (s.startsWith("-")) {
isNegative = true;
s = s.slice(1).trim();
}
while (s.length > 0) {
s = s.trim();
if (s.startsWith("never") || s.startsWith("off"))
return 0;
const { number, remainingStr } = parseDouble(s);
if (number === null) return false;
s = remainingStr;
const match = s.match(/^([a-zA-Z]*)/);
let currentUnit = unit;
if (match && match[0].length > 0) {
currentUnit = match[0];
s = s.slice(match[0].length).trim();
}
const du = durationFindUnit(currentUnit);
if (!du) return false;
nsec += number * du.multiplier;
}
const unitMultiplier = durationFindUnit(unit).multiplier;
nsec = roundToResolution(nsec, unitMultiplier);
return isNegative ? -nsec : nsec;
}
function durationSnprintf(value, unit) {
if (value === 0) return "off";
const duMin = durationFindUnit(unit);
let nsec = Math.abs(value) * duMin.multiplier;
const isNegative = value < 0;
let result = isNegative ? "-" : "";
for (let i = units.length - 1; i >= 0 && nsec !== 0; i--) {
const du = units[i];
if (!du.formatter && du !== duMin) continue;
const multiplier = du.multiplier;
const rounded = (du === duMin) ? roundToResolution(nsec, multiplier) * multiplier : nsec;
let unitCount = Math.floor(rounded / multiplier);
if (unitCount !== 0) {
result += `${unitCount}${du.unit}`;
nsec -= unitCount * multiplier;
}
if (du === duMin) break;
}
return result || "off";
}
function updateTable() {
const duration = document.getElementById("durationInput").value;
const tableBody = document.getElementById("resultTable").querySelector("tbody");
const errorMessage = document.getElementById("errorMessage");
tableBody.innerHTML = "";
errorMessage.textContent = "";
units.forEach(unit => {
let value = durationParse(duration, unit.unit);
let formatted;
let check;
if(value === false) {
value = "-";
formatted = "";
check = "parsing error";
}
else {
formatted = durationSnprintf(value, unit.unit);
const parsedValue = durationParse(formatted, unit.unit);
check = (parsedValue === value) ? "ok" : `re-parsing error (${parsedValue})`;
}
const row = `<tr>
<td>${unit.unit}</td>
<td>${value}</td>
<td>${formatted}</td>
<td>${check}</td>
</tr>`;
tableBody.innerHTML += row;
});
}
document.getElementById("durationInput").addEventListener("input", updateTable);
</script>
</body>
</html>