I have a requirement to Validate a Decimal Field on an enterprise application to Hours and Minute Format.
so for example 7.30 is 7 hrs 30 minutes
9.55 is 9 hours 55minutes
10.80 .....this should not be accepted.
23.59 is the maximum.
I tried the sample code.
function ValidateTotalHours() {
var totalhours = Xrm.Page.getAttribute("new_totalhours").getValue();
if (!/^([0-23]).([0-5][0-9])$/.test(totalhours)) {
Xrm.Utility.alertDialog("Total Hours Format is invalid");
}
}
I tried validating with https://regex101.com/ before proceeding but it seems my Regex expression is incorrect.
Any ideas on the correct implementation.
Your pattern is incorrect:
[0-23] equals [0123]
. needs to be escaped. \. otherwise it will match any character except line break
What you need is: ^([0-1]?[0-9]|2[0-3])\.([0-5][0-9])$
const pattern = /^([0-1]?[0-9]|2[0-3])\.([0-5][0-9])$/;
const times = ['24.00', '23.60', '22.59', '04.05', '4.05', '23.02', '15.25', '24.59'];
times.forEach(time => {
console.log(`${time}: ${pattern.test(time)}`);
});
The [0-23] in the RegEx:
/^([0-23]).([0-5][0-9])$/
is actually specifying:
[0123]
You want something like this:
/^(2[0-3]|[01]?[0-9])\.[0-5][0-9]$/
Related
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);
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;
}
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
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 4
hours in Group 5
minutes in Group 6
Tested at https://regex101.com.
Does it solve your problem?
I'm currently coding a discord bot using javascript and I want to test if a message contains an hour. I managed to do it this way
var content = msg.content.toLowerCase(); // Get the content on the message
const nbs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var hours;
nbs.forEach(number => {
if(content.includes(`${number}am`)) hours = `${number}AM`;
else if(content.includes(`${number}pm`)) hours = `${number}PM`;
})
But the problem I have is in this example I can't detect if the hour contains minutes like 10:15am for example. I thought about doing another array containing integers from 1 to 59 and doing another forEach inside the one I already have but I'm afraid it would slow the code down quite a bit.
Is there a way I could test if the message contains an int between 2 values so I could have something like this ?
content.includes(`${hr}:${min}am`)
You can use regular expressions:
function getTime(message) {
const result = message.match(/(\d{1,2}):(\d{2})\s*(am|pm)/i);
if (result) {
return {
hours: Number(result[1]),
minutes: Number(result[2]),
format: result[3],
}
} else {
return null;
}
}
console.log(getTime("hello 2:13AM"));
console.log(getTime("hello 19:55 pm"));
console.log(getTime("hello"));
Use Regular Expressions to get the time.
I would recommend using something like this:
/([0-2][0-9])\:([0-6][0-9])(?:\s*)?(am|pm)?/
I'll now go through all parts of this regular expression:
([0-2][0-9]) - Using the first capturing group you will be able to get values from 00 to 24 (well 29 to be honest) but it will do the trick.
\: - will capture your seperator in this case a double colon
([0-6][0-9]) - will capture a number between 00 to 69
(?:\s*)?(am|pm)? - last but not least capture am or pm. This is completely optional. The regex will also capture times without am or pm if someone writes in a 24 hour format
let content = 'some content 10:00 AM ...';
content = content.toLowerCase();
console.log(content);
const time_regex = /([0-2][0-9])\:([0-6][0-9])(?:\s*)?(am|pm)?/;
if ((m = time_regex.exec(content)) !== null) {
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
document.body.insertAdjacentHTML('beforeBegin', `<p>Found match, group ${groupIndex}: ${match}</p>`);
});
}
If you want to be even more specific and make sure that times like 26:00 or 13:65 are not possible to match you can adjust the regex like this
([0-2][0-9])(?<!25|26|27|28|29)\:([0-6][0-9])(?<!61|62|63|64|65|66|67|68|69)(?:\s*)?(am|pm)?
As you can see after the check for the values 00 to 29 and 00 to 69 I have added negative lookbehinds with all values we want to exclude. This makes sure that the first capturing group can only count up to 24 and the second can only count up to 60.
In the column where the hours/minutes are stored for some of the business facilities time stamp(s) are presented in this format 0000-0000. The first two digits represent hours and the other two minutes. Here is example of some business hours:
0700-2300 M-F 0700-1700 S&S
0600-2200
0600-2300 Mon-Fri 0700-2200 Sat&Sun
Local 1 0000-2359 Local 2 0630-2230
0700-2100
0600-2345
The original solution that I had was to convert the values in JavaScript and that it was pretty simple. The problem I have is when there is more than one set of time hours/minutes in the string. In the example above that is the case where hours/minutes are different during the weekend or for the different locations. The JS code example is here:
var time = calcDifference("0600-2345");
function calcDifference(time) {
var arr = time.split('-').map(function(str) {
var hours = parseInt(str.substr(0, 2), 10),
minutes = parseInt(str.substr(2, 4), 10);
var result = (hours * 60 + minutes) / 60;
return result.toFixed(2);
});
return arr[1] - arr[0];
}
console.log(time);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
The code above works just fine if I pass the argument with one time stamp. I'm wondering how to handle situation where I have two time stamps? What would be a good solution to search if string has more than one hours/minutes values and then convert each of them and return to the user.
Assuming the HHMM-HHMM format is consistent in the input, and you don't care about discarding the remaining information in the string, regex is probably the simplest approach (and much safer than your current approach of splitting on hyphens, which might easily occur in other parts of the string you don't care about.)
Note that you won't be able to distinguish between "weekend" and "weekday" times, because that information isn't in a consistent format in your input. (This looks like human input, which pretty much guarantees that your HHMM-HHMM format also won't be strictly consistent; consider allowing for optional whitespace around the hyphen for example, and logging strings which show no match so you can check them manually.)
var testinputs = [
"0700-2300 M-F 0700-1700 S&S",
"0600-2200",
"0600-2300 Mon-Fri 0700-2200 Sat&Sun",
"Local 1 0000-2359 Local 2 0630-2230",
"0700-2100",
"0600-2345"
]
var reg = /(\d\d)(\d\d)\-(\d\d)(\d\d)/g; // \d means any digit 0-9; \- matches a literal "-", parens capture the group for easier access later
for (input of testinputs) {
console.log("Input: ", input)
var matches;
while ((matches = reg.exec(input)) !== null) { // loop through every matching instance in the string
// matches[0] is the full HHMM-HHMM string; the remainder is
// the HH and MM for each parenthetical in the regexp:
console.log(matches)
}
}
There are plenty of ways to do this ( based on your point of view ), but this is my favourite one. you can manipulate the text then pass numbers individually.
var date = '0700-2300 M-F 0700-1700 S&S'.match( /[-\d]+/gi ).filter( e => ~e.search( /\d+/gi ) )
now you have an array of multiple timestamps saved on your database and you pass them individually to your function.
date.forEach( each => calcDifference( each ) );
You can use a regex like /\d{4}\-\d{4}/g to extract all of the digits from the string and map them to their time differences or replace text in the original.
const calcDifference = range => {
const time = range.split`-`
.map(e => (+e.substr(0, 2) * 60 + (+e.substr(2))) / 60)
return time[1] - time[0];
};
const diffs = `0700-2300 M-F 0700-1700 S&S
0600-2200
0600-2300 Mon-Fri 0700-2200 Sat&Sun
Local 1 0000-2359 Local 2 0630-2230
0700-2100
0600-2345`.replace(/\d{4}\-\d{4}/g, calcDifference);
console.log(diffs);
Valid values:
50,
50.00,
(50),
(2),
(50.00),
(10.XX),
some invalid examples:
(50,
((50,
50)),
60),
50.),
I'm assuming the format is ##.##, with two digits max, and an optional decimal with maybe 1 or two more numbers, and the whole shebang maybe surrounded by parens to indicate negative. I'm also assuming 10.XX isn't really valid, but replacing the X's with numbers would be.
I'd recommend separate negative and positive regexes so that your code is clear. E.g.:
good = ["50", "50.00", "(50)", "(2)", "(50.00)", "(10.19)"];
bad = ["(50", "((50", "50))", "60)", "50.)"];
posRegex = /^\d\d?(\.\d{0,2})?$/;
negRegex = /^\(\d\d?(\.\d{0,2})?\)$/;
function matches(val) {
var m = posRegex.test(val) || negRegex.test(val);
console.log(val + " : " + m);
}
console.log("These should be true");
good.forEach(matches);
console.log("These should be false");
bad.forEach(matches);
You can use the following:
^\(\d+(\.\d{2})?\)$|^\d+(\.\d{2})?$
See DEMO
I have input field for year and I need a regex for validation it.
I have such code: ^([12]\d)?(\d\d)$.
But I want allow to validate only years in certain range (1990-2010, for example). How can I do it?
Edit. range must be 1950-2050
Try this:
1990 - 2010:
/^(199\d|200\d|2010)$/
1950 - 2050:
/^(19[5-9]\d|20[0-4]\d|2050)$/
Other examples:
1945 - 2013:
/^(194[5-9]|19[5-9]\d|200\d|201[0-3])$/
1812 - 3048:
/^(181[2-9]|18[2-9]\d|19\d\d|2\d{3}|30[0-3]\d|304[0-8])$/
Basically, you need to split your range into easy "regexable" chunks:
1812-3048: 1812-1819 + 1820-1899 + 1900-1999 + 2000-2999 + 3000-3039 + 3040-3048
regex: 181[2-9] 18[2-9]\d 19\d\d 2\d{3} 30[0-3]\d 304[0-8]
RegExp does not seem to be the right tool here. If you have the year values already isolated surely a simple comparison would work :
if (+yr >= 1990 && +yr <= 2010)
The +yr converts the string to a number
For a range from 1950 to 2050 you may use the following regex:
^19[5-9]\d|20[0-4]\d|2050$
Online demo
Regex:
/^(19[5-9]\d|20[0-4]\d|2050)$/
Easier...
var year = parseInt(textField.value, 10);
if( year >= 1950 && year <= 2050 ) {
...
}
(199[0-9]|200[0-9]|2010)
This will work in your 'example case'.
Helpful website: http://utilitymill.com/utility/Regex_For_Range
Regex for Current Year(Dynamic) from 1995
var current_year = new Date().getFullYear().toString().split("");
var reg=new RegExp("^(199[5-9]|200[0-9]|[0-"+current_year[0]+"][0-"+current_year[1]+"][0-"+current_year[2]+"][0-"+current_year[3]+"])$");
reg.test("1995");
if you want to check for age between for example 18 or 70 I did it like this.
my solution was
function yearRange(year) {
let now = new Date().getFullYear();
let age = now - Number(year);
if (age < 18 || age > 70) return false;
return true;
}
Here is a regex if you want to find a year in a film name for example.
Years b/n 1900 - 2029 and some symbols are allowed wrapping the year .-_+[(
(?<=(?:\s|\.|_|\-|\+|\(|\[))(?:19[2-9]|20[0-2])\d(?=\s|$|\.|_|\-|\+|\)|\])
check it out here
https://regex101.com/r/eQ9zK7/82
Note you can not start with year, because there is at least interval in front.
In the example first few lines are matching, because we have multiple lines in a single line they wont match.
1917.2019.1080p...
even if 1917 was in range it will mark only 2019