I am trying to validate a string the way it is done in Jira in Javascript. I'm trying to replicate how it is validated in Jira. I am guessing I could do this with Regex but I am not sure how.
A user can type a string in the format of "1d 6h 30m" which would mean 1 day, 6 hours, 30 minutes. I do not need the weeks for my use case. I want to show an error if the user uses an invalid character (anything except 'd','h','m', or ' '). Also the string must separate the time durations by spaces and ideally I would like to force the user to enter the time durations in descending order meaning '6h 1d' would be invalid because the days should come first. Also the user does not have to enter all information so '30m' would be valid.
This is my code for getting the days, hours and minutes which seems to work. I just need help with the validation part.
let time = '12h 21d 30m'; //example
let times = time.split(' ');
let days = 0;
let hours = 0;
let min = 0;
for(let i = 0; i < times.length; i ) {
if (times[i].includes('d')){
days = times[i].split('d')[0];
}
if (times[i].includes('h')){
hours = times[i].split('h')[0];
}
if (times[i].includes('m')){
min = times[i].split('m')[0];
}
}
console.log(days);
console.log(hours);
console.log(min);
CodePudding user response:
const INPUT = "12h 21d 30s";
checkTimespanFormat(INPUT);
if (checkTimespanKeysOrder(INPUT, true))
console.log(`${INPUT} keys order is valid`);
else console.log(`${INPUT} keys order is NOT valid`);
//******************************************************************
/**
* Ensures that time keys are:
* - Preceeded by one or two digits
* - Separated by one or many spaces
*/
function checkTimespanFormat(timespanStr, maltiSpacesSeparation = false) {
// timespan items must be separated by 1 space
if (maltiSpacesSeparation) timespanStr = timespanStr.toLowerCase().split(" ");
// timespan items must be separated by one or many space
else timespanStr = timespanStr.toLowerCase().split(/ /);
// timespan items must be formatted correctly
timespanStr.forEach((item) => {
if (!/^\d{1,2}[dhms]$/.test(item)) console.log("invalid", item);
else console.log("valid", item);
});
}
/**
* Validates:
* - Keys order
* - Duplicate keys
*/
function checkTimespanKeysOrder(timespanStr) {
const ORDER = ["d", "h", "m", "s"];
let timeKeysOrder = timespanStr
.replace(/[^dhms]/g, "") // Removing non time keys characters
.split("") // toArray
.map((char) => {
return ORDER.indexOf(char); // Getting the order of time keys
});
for (i = 0; i < timeKeysOrder.length - 1; i )
if (timeKeysOrder.at(i) >= timeKeysOrder.at(i 1)) return false;
return true;
}
CodePudding user response:
Based on your comment, I have added a validation regex to be run first before running the match regex.
For validation, you want
/^(\d [d]\s )?(\d [h]\s )?(\d [m]\s )?(\d [s]\s |$)?/
For extracting values, you want
/([\d] [dhms]\s |$)/g
You can then use String.match with this regular expression, iterating through all the matches add adding time based on the time letter at the end
CodePudding user response:
Here's my take at the problem:
- match minutes
([1-5]?[\d])m
, with eligible values in range [0,59] - match hours
([1]?[\d]|2[0-3])h
, with eligible values in range [0,23] - match days
([1-9]|[1-9][\d])d
, with eligible values in range [1,99]
Then we can encapsulate regex for days in hours and regex for hours in minutes to make sure that we have formats like {dd}d {hh}h {mm}m
, {hh}h {mm}m
, {mm}m
:
"(((([1-9]|[1-9][\d])d )?([1]?[\d]|2[0-3])h )*([1-5]?[\d])m)"
Corner cases include inputs with zeros like 00m
, 00d 00m
, 01h 00d 00m
. In order to reject the former two and accept the last one, we can negate the values 00m
and 00d
when the preceeding patterns are not found.
Final regex to use:
"(?!0m)(((?!0h)(([1-9]|[1-9][\d])d )?([1]?[\d]|2[0-3])h )*([1-5]?[\d])m)"
Check for:
days
in Group 4hours
in Group 5minutes
in Group 6
Tested at https://regex101.com.
Does it solve your problem?