|
const units = [ |
|
{ |
|
names: ['day', 'dy', 'd'], |
|
seconds: 86400 |
|
}, { |
|
names: ['hour', 'hr', 'h'], |
|
seconds: 3600 |
|
}, { |
|
names: ['minute', 'min', 'm'], |
|
seconds: 60 |
|
}, { |
|
names: ['second', 'sec', 's'], |
|
seconds: 1 |
|
} |
|
]; |
|
|
|
function pluralize(count, noun) { |
|
return count === 1 ? noun : `${noun}s`; |
|
} |
|
|
|
class Duration { |
|
constructor(number, name) { |
|
this.count = parseFloat(number); |
|
this.unit = units.find(u => u.names.includes(name)); |
|
this.seconds = Math.floor(this.count * this.unit.seconds); |
|
} |
|
|
|
static detect(seconds) { |
|
let unit = units.find(unit => Math.floor(seconds) % unit.seconds === 0); |
|
|
|
return new Duration(seconds / unit.seconds, unit.names[0]); |
|
} |
|
|
|
toString() { |
|
return `${this.count} ${pluralize(this.count, this.unit.names[0])}`; |
|
} |
|
} |
|
|
|
function tokenizer() { |
|
let numbers = '[\\d.]+'; |
|
let names = [].concat(...units.map(m => m.names)).join('|'); |
|
|
|
return new RegExp(`(${numbers})(${names})s?`, 'g'); |
|
} |
|
|
|
function parse(input) { |
|
let re = tokenizer(); |
|
let durations = []; |
|
let result; |
|
|
|
input = input.replace(/\s/g, ''); |
|
|
|
while (result = re.exec(input)) { |
|
let [, number, name] = result; |
|
|
|
durations.push(new Duration(number, name)); |
|
} |
|
|
|
return durations; |
|
} |
|
|
|
let good = ['1 day', '1h 2 min', '1d12h', ' 3.5 dy ', '48h']; |
|
let bad = ['', '1 day 5', 'potato 1d tomato', '555', 'day']; |
|
|
|
for (let inputs of [good, bad]) { |
|
console.log(inputs.map(input => { |
|
let durations = parse(input); |
|
let seconds = durations.map(d => d.seconds).reduce((a, b) => a + b, 0); |
|
let total = Duration.detect(seconds); |
|
|
|
return { |
|
input, |
|
output: durations.join(' '), |
|
seconds, |
|
total: total.toString() |
|
}; |
|
})); |
|
} |