check input value for specific format using Javascript - javascript

I have an input field that allows a user to enter a date.
I need this date to be in the following format: 10Jan13 (capitalization is not important)
There is a popup calender that if used will format the date correctly for the user.
I'd like to check the value of the input onblur using Javascript to be sure that the user did not either paste or type the date improperly.
I am currently checking number-only fields like this:
var numbers = /^[0-9]+$/;
if (!BIDInput.value.match(numbers))
{
checkedInput.value = "";
alert('Not a number');
}
and I'm checking letters-only fields like this:
var letters = /^[a-z]+$/
if (!nameInput.value.match(letters))
{
nameInput.value = "";
alert('Not a letter');
}
I would like to check the date format in a similar a fashion if possible. But anything that accomplishes the task will do. Can anyone point me in the right direction on how to get this done?
I know that client side validation does not replace server side validation. This is for user experience purposes only.

You're pretty much there with what you have. Basically your format is one or two digits, then one of 12 possible strings, followed by two digits. So for instance:
var shortDateRex = /^\d{1,2}(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\d{2}$/;
Breakdown:
^ Start of string.
\d{1,2} One or two digits.
(:?...) A non-capturing group. Or you could use a capture group if you like.
Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec An alternation, allowing any of those twelve choices. Naturally you can add more if you like. If you have two choices that start the same way (Jan and January, for instance), put the longer one earlier in the alternation.
\d{2} Two digits.
Side note: I'd have to recommend against two-digit dates on principle, and particularly given where in the century we currently are!
Responding to Amberlamps' comment that this doesn't validate the date: Once you've validated the format, it's trivial to then check the date itself if you like (to rule out 30Feb13, for instance):
var validateDateString = (function() {
var monthNames = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec".toLowerCase().split("|");
var dateValidateRex = /^(\d{1,2})(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)(\d{2})$/i;
var arbitraryCenturyCutoff = 30;
function validateDateString(str) {
var match;
var day, month, year;
var dt;
match = dateValidateRex.exec(str);
if (!match) {
return false;
}
day = parseInt(match[1]);
month = monthNames.indexOf(match[2].toLowerCase()); // You may need a shim on very old browsers for Array#indexOf
year = parseInt(match[3], 10);
year += year > arbitraryCenturyCutoff ? 1900 : 2000;
dt = new Date(year, month, day);
if (dt.getDate() !== day ||
dt.getMonth() !== month ||
dt.getFullYear() !== year) {
// The input was invalid; we know because the date object
// had to adjust something
return false;
}
return true;
}
return validateDateString;
})();
...or something along those lines.
Live Example | Source
Or if (like me) you hate to see a list like that list of month names repeated you can use the RegExp constructor with a string instead, but you have to remember to duplicate your backslashes:
var monthNamesString = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
var monthNames = monthNamesString.toLowerCase().split("|");
var dateValidateRex = new RegExp("^(\\d{1,2})(" + monthNamesString + ")(\\d{2})$", "i");
Live Example | Source

You would use the following regular expression to check for a string starting with 2 numbers, followed by 3 characters followed by 2 numbers
[0-9]{2}[a-zA-Z]{3}[0-9]{2}

Related

Mask the last two digits of a date in JavaScript using regex

I’m trying to demonstrate how a date would look like if it was displayed with some characters masked. Specifically, something like this:
10 August 2018 => 10 August 20**
10 August 2018 => 10 August **** (and this too if possible)
I’ve spent some time looking for working examples on here but haven’t found one for this specific example. In my own experiments I only ever end up with one asterisk (10 August 19*) instead of one per character.
It all needs to happen within a textToMask.replace(regex, '*').
I know you’d never use this in production; it’s for a visual demo.
You can use padEnd method
function maskIt(str, pad = 1) {
const slicedStr = str.slice(0,pad*-1);
const masked = slicedStr.padEnd(str.length, '*');
console.log(masked);
}
maskIt("10 August 2018",2);
maskIt("10 August 2018",4);
Here's a dirt simple mask() function, that works with any string, and doesn't involve regex:
function mask(str, amt = 1) {
if (amt > str.length) {
return '*'.repeat(str.length);
} else {
return str.substr(0, str.length-amt) + '*'.repeat(amt);
}
}
console.log(mask('10 August 2018', 2));
console.log(mask('10 August 2018', 4));
console.log(mask('test', 5));
Here is a very simple function that uses a regular expression to find a given number of digits at the end of a given date and substitutes them with an equal number of asterisks.
Example:
const mask_date = (date, n) => date.replace(new RegExp(`[\\d]{${n}}$`), "*".repeat(n));
console.log(mask_date("10 August 2018", 2));
console.log(mask_date("10 August 2018", 4));
Please note that I would really like to refactor this somehow, I just don't have the time to do it right now. Check back at some point tomorrow I may have made an edit to this to make the code flow a bit better.
I am using the second version of the String.prototype.replace function that allows you to pass a function instead of a string as the second parameter. Check the link to learn more.
This is a very rough function -- I unfortunately did not have a lot of time to write this out.
// str - string to be altered, pattern - regex pattern to look through, replacement - what to replace the found pattern with, match_length - do we match the length of replacement to the length of what it is replacing?
function mask(str, pattern, replacement="*", match_length=true){
return str.replace(pattern, function(whole, group){
//init some values;
let padLength = 0, returned = '';
// if the group is not a number, then we have a regex that has a grouping. I would recommend limiting your regex patterns to ONE group, unless you edit this.
if(typeof group != 'number'){
padLength = group.length;
returned = whole.slice(0, whole.indexOf(group)) + (replacement.repeat(match_length ? padLength : 1));
}else{
padLength = whole.length;
returned = replacement.repeat(match_length ? padLength : 1);
}
return returned;
});
}
let randomBirthdayString = 'April 3 2002';
console.log(mask(randomBirthdayString, /\d{2}(\d{2})$/) );
console.log(mask(randomBirthdayString, /\d{2}(\d{2})$/, 'x') );
console.log(mask(randomBirthdayString, /\d{2}(\d{2})$/, 'x', false) );
You can use the code below.
textToMask.replace(/..$/, '**')

Find missing quarters in time series

I am trying to see how could I fill the missing financial quarters of a time series like this in Javascript:
["2012-Q2","2012-Q4","2013-Q4","2014-Q1","2014-Q2","2014-Q3",
"2014-Q4","2015-Q1","2015-Q2","2015-Q3","2015-Q4","2016-Q1",
"2016-Q2","2016-Q3","2016-Q4","2017-Q1","2017-Q2","2017-Q3",
"2017-Q4","2018-Q1"]
I would like somehow to get a time series with the missing elements i.e. for each year I should see 4 "dates".
I don't mind ignoring the first quarter before the first element "2012-Q2" and the last 3 quarters after the last element "2018-Q1".
I know moment.js has functions like quarter() or fquarter() (via a plugin), but I am looking for something closer to the other way around. I already have the quarters (as date-strings), and I have to parse them as date objects.
I need to fill the "quarter holes" in between those input string values.
In my case I probably need to parse first those date-strings in that custom format to make them something moment could understand, but I am a bit lost. In here https://momentjs.com/docs/#/parsing/string-format/ a potential format could involve Y for years and Q for quarters, but I am not sure how to escape the literal Q inside every input date-string of that array?
Also assuming I could somehow parse all those date strings into moment objects, then I am not sure how that could help in filling the holes?
I can not find a pure javascript solution involving date types.
Another approach could be to parse those date-strings and get the year and the quarter number using substring and then manually filling the holes checking year/quarter pairs, is there anything simpler than this?
Assuming that you want a full list of quarters between the first one of your input array until the last one, you can:
parse with moment the first and the last element of your array, using moment(String, String) with 'YYYY[-Q]Q' as format parameter, see Escaping charaters section of the docs.
loop from start to end using isSameOrBefore (or other query functions) adding 1 quarter on each iteration (add(1, 'Q'))
Here a live sample:
var quarters = ["2012-Q2","2012-Q4","2013-Q4","2014-Q1","2014-Q2","2014-Q3",
"2014-Q4","2015-Q1","2015-Q2","2015-Q3","2015-Q4","2016-Q1",
"2016-Q2","2016-Q3","2016-Q4","2017-Q1","2017-Q2","2017-Q3",
"2017-Q4","2018-Q1"];
var format = 'YYYY[-Q]Q';
var start = moment(quarters[0], format);
var end = moment(quarters[quarters.length-1], format);
var results = [];
while( start.isSameOrBefore(end) ){
results.push(start.format(format));
start.add(1, 'Q');
}
console.log(results);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js"></script>
Why not just make a function that returns the quarters from a particular range?
function getQuarters(startYear, endYear){
var times = [];
for(var i = startYear; i <= endYear; i++){
times.push(i + "-Q1");
times.push(i + "-Q2");
times.push(i + "-Q3");
times.push(i + "-Q4");
}
return times;
}
Calling:
getQuarters(2017,2017);
Returns:
["2017-Q1", "2017-Q2", "2017-Q3", "2017-Q4"]
Assuming you want to get an array with the missing values, you could take a start quarter and cehck against the given data for either pushing the quartal or incremet the index of the array.
function incQ(time) {
time[1]++;
if (time[1] === 5) {
time[0]++;
time[1] = 1;
}
}
var quarters = ["2012-Q2", "2012-Q4", "2013-Q4", "2014-Q1", "2014-Q2", "2014-Q3", "2014-Q4", "2015-Q1", "2015-Q2", "2015-Q3", "2015-Q4", "2016-Q1", "2016-Q2", "2016-Q3", "2016-Q4", "2017-Q1", "2017-Q2", "2017-Q3", "2017-Q4", "2018-Q1"],
actual = quarters[0].split('-Q'),
missing = [],
i = 0;
while (i < quarters.length) {
if (actual.join('-Q') !== quarters[i]) {
missing.push(actual.join('-Q'));
} else {
i++;
}
incQ(actual);
}
console.log(missing);

Validating date input field with onsubmit

I was looking everywhere, but I couldn't find a good code for my problem.
I have some input fields, what the users are using to send online their requests.
And one of the fields is a date field, what I would like somehow to validate in that way, that to be allowed only if the entered date to be somewhere between today and 8 days before.
Example: if today is 29 November , they would be allowed to enter only date between 21st-29th November and nothing else
And to be shown an alert window already when they have entered the wrong date
They will pick up the days from minicalendar but that part is resolved, I need help only with the validating.
If somebody could post a working code, I would be very grateful.
Thank you
Use jQuery UI DatePicker, the script below:
$(function() {
var currentDate = new Date();
var maxAllowedDate = new Date(currentDate);
maxAllowedDate.setDate(currentDate.getDate() + 8);
$( "#datepicker" ).datepicker({
changeYear: true,
minDate: '0',
maxDate: '+7D',
});
$('#datepicker').change(function(){
var enteredVal = new Date(this.value);
if(enteredVal.getTime() < currentDate.getTime() || enteredVal.getTime() > maxAllowedDate.getTime()) {
alert("invalid");
} else {
alert("valid");
}
});
});
and the UI:
<div class="demo">
<p>Date: <input type="text" id="datepicker"></p>
Here is the jsFiddle demo: http://jsfiddle.net/pjkz7k0t/1/
Since you asked for javascript, I will assume you want a javascript answer, not a jQuery answer.
function isValidDate(checkDate) {
if(/\d\d\/\d\d\/\d\d\d\d/.test(checkDate)) {
// split checkDate into three pieces
var strMM = checkDate.split('/')[0];
var strDD = checkDate.split('/')[1];
var strYYYY = checkDate.split('/')[2];
// create new Date() object from split pieces
var strDateCheck = new Date(strYYYY,(strMM - 1),strDD);
// evaluate each piece of resulting date object against each corresponding piece of checkDate
if(((strDateCheck.getMonth() + 1) == strMM) && (strDateCheck.getDate() == strDD) && (strDateCheck.getFullYear() == strYYYY)) {
/* if you wish, add additional validation constraints here */
return true; // all three pieces match exactly
}
}
return false; // did not meet criteria for return true
}
This method uses explicit regex to validate the formats.
Instead of creating elaborate methods of testing each piece, I used the pieces to build a new Date() object, knowing that the result MIGHT not match checkDate, and used pieces from resulting date to test the pieces of checkDate passed into the function. If ALL THREE pieces match, the entered date is valid.
For instance:
'02/29/2014' returns false
'02/29/2012' returns true
'12/36/2014' returns false
'29/06/2014' returns false
The code is pure javascript which improves portability, and this method does not interfere or impede additional validation by any other criteria you choose to use (against year ranges, or evaluating strCheckDate against today(), or any other constraints specific to your specific application).
An added advantage is that this method does not just determine whether what is passed to the function can be used to create a valid date, but confirms that the date entered MATCHES the valid date that can be created (thereby overcoming the problem of javascript rolling "extra days" forward on date creation).
This could easily be expanded to test for various configurations of date, using the exact same logic, simply by creating a different regex test, and splitting checkDate differently.
regex for DD/MM/YYYY would be the same, but the split would look like this:
// split characters into three pieces
var strDD = checkDate.split('/')[0];
var strMM = checkDate.split('/')[1];
var strYYYY = checkDate.split('/')[2];
Or for YYYY/MM/DD you would use regex:
/\d\d\d\d\/\d\d\/\d\d/.test(checkDate)
and the split would look like this:
// split characters into three pieces
var strYYYY = checkDate.split('/')[0];
var strMM = checkDate.split('/')[1];
var strDD = checkDate.split('/')[2];
This is highly morphable (and clean) javascript code to accomplish the purpose of validating a user entered date, and can be quickly modified to expand checks for valid date within range.
function isValidDateRange(checkDate,minDate,maxDate) {
if(/\d\d\/\d\d\/\d\d\d\d/.test(checkDate)) {
// split checkDate into three pieces
var strMM = checkDate.split('/')[0];
var strDD = checkDate.split('/')[1];
var strYYYY = checkDate.split('/')[2];
// create new Date() object from split pieces
var strDateCheck = new Date(strYYYY,(strMM - 1),strDD);
// evaluate each piece of resulting date object against each corresponding piece of checkDate
if(((strDateCheck.getMonth() + 1) == strMM) && (strDateCheck.getDate() == strDD) && (strDateCheck.getFullYear() == strYYYY)) {
// if this code fires, you have a valid date entered, first logic hurdle passed
// If you pass in minDate and maxDate as any format other than a date object, you should
// create new Date(); from them before comparing.
// Example:
// var strMinMM = minDate.split('/')[0];
// var strMinDD = minDate.split('/')[1];
// var strMinYYYY = minDate.split('/')[2];
// minDate = new Date(strMinYYYY,(strMinMM - 1),strMinDD);
// var strMaxMM = maxDate.split('/')[0];
// var strMaxDD = maxDate.split('/')[1];
// var strMaxYYYY = maxDate.split('/')[2];
// maxDate = new Date(strMaxYYYY,(strMaxMM - 1),strMaxDD);
if((!strDateCheck < minDate) && (!strDateCheck > maxDate)) {
return true; // all three pieces match exactly AND date is within specified range
}
}
}
return false; // did not meet criteria for return true
}

Multiple RegExp in single function

This is not exactly a problem, but more a question of method.
I am working on a project where people are able to type shorthand dates in input field, for example if you simply type "20", the input will automatically display the full date for 20th of this month.
There are many shorthand types possible, so I had to make multiple RegExp and then check each and every one.
My question is, is there a better way to deal with this? I am no javaScript expert,but I have a feeling that this is not exacty "best practise".
Here is the function
function dateParser(date) {
var splitDate = date.split(/[.:\s]/);
var day = new RegExp(/\b\d{1,2}\b/);
var dateHour = new RegExp(/\b\d{1,2}\s\d{1,2}\b/);
var dateHourMin = new RegExp(/\b\d{1,2}\s\d{1,2}[:]\d{1,2}\b/);
var dateMonth = new RegExp(/\b\d{1,2}[\/\-\,\.]\d{1,2}\b/);
var dateMonthHour = new RegExp(/\b\d{1,2}[\/\-\,\.]\d{1,2}\s\d{1,2}\b/);
var dateMonthHourMin = new RegExp(/\b\d{1,2}[\/\-\,\.]\d{1,2}\s\d{1,2}[:]\d{1,2}\b/);
var dateMonthYear = new RegExp(/\b\d{1,2}[\/\-\,\.]\d{1,2}[\/\-\,\.]\d{1,4}\b/);
var dateMonthYearHour = new RegExp(/\b\d{1,2}[\/\-\,\.]\d{1,2}[\/\-\,\.]\d{1,4}\s\d{1,2}\b/);
var dateMonthYearHourMin = new RegExp(/\b\d{1,2}[\/\-\,\.]\d{1,2}[\/\-\,\.]\d{1,4}\s\d{1,2}[:]\d{1,2}\b/);
var month = new Date().getMonth() + 1;
var year = new Date().getFullYear();
var newDate;
if(dateMonthYearHourMin.test(date)) {
newDate = splitDate[0]+"."+splitDate[1]+"."+splitDate[2]+" "+splitDate[3]+":"+splitDate[4];
}
else if(dateMonthYearHour.test(date)) {
newDate = splitDate[0]+"."+splitDate[1]+"."+splitDate[2]+" "+splitDate[3]+":00";
}
else if(dateMonthYear.test(date)) {
newDate = splitDate[0]+"."+splitDate[1]+"."+splitDate[2]+" 12:00";
}
else if(dateMonthHourMin.test(date)) {
newDate = splitDate[0]+"."+splitDate[1]+"."+year+" "+splitDate[2]+":"+splitDate[3];
}
else if(dateMonthHour.test(date)) {
newDate = splitDate[0]+"."+splitDate[1]+"."+year+" "+splitDate[2]+":00";
}
else if(dateMonth.test(date)) {
newDate = splitDate[0]+"."+splitDate[1]+"."+year+" 12:00";
}
else if(dateHourMin.test(date)) {
newDate = splitDate[0]+"."+month+"."+year+" "+splitDate[1]+":"+splitDate[2];
}
else if(dateHour.test(date)) {
newDate = splitDate[0]+"."+month+"."+year+" "+splitDate[1]+":00";
}
else if(day.test(date)) {
newDate = splitDate[0]+"."+month+"."+year+" 12:00";
}
return newDate;
}
I believe you can consolidate some of your regular expressions and make this more compact.
The first thing I notice is that whitespace appears to be an important separator in your input strings. Specifically, the date (or day) is always separated from the hour/minute with a space. So the first thing I would do is split your input on a space:
var parts = date.split( /\s/ );
var datePart = parts[0];
var timePart = parts[1]; // could be undefined
Now we can process the date part and the time part (if it exists) separately. The components of your date part are always separated by a slash, a dash, a comma, or a period, so again we can just split it:
parts = datePart.split( /[\/\-\,\.]/ );
var day = parts[0];
var month = parts[1] // could be undefined
var year = parts[2]; // could be undefined
You can split the time similarly, since hours and minutes are always separated by a colon:
if( timePart ) {
parts = timePart.split( /:/ );
var hour = parts[0];
var minute = parts[1]; // could be undefined
}
This should make this a little more compact and easy to read and maintain. You could go even more compact with a singular regular expression with groups, but I feel that this approach would be better.
The problem you described is tackled by Natural Language Processing, Programming/Query Language Design fields.
One of the approaches for solving those kind of problems is manually written scanner using RegExp/other string scanning and working with the result the way you did. It works for simple languages, doesn't require much knowledge in language design department and usually is intuitive to modify.
If you however have feeling that input is going to grow to something more complicated I recommend replacing RegExp scanning with full-fledged parser/lexer using for example Jison "Your friendly JavaScript parser generator!" or anything else that suits you.

javascript dynamic regex vs hard typed and captures

I've been at this for hours -- I think I need sleep... but no matter how I alter the expression javascript will only capture the 1st and 3rd elements:
var number = 09416;
var mat = "([0-9]+/[0-9]+/[0-9]+\\s+[0-9]+:[0-9]+)\\s+([A-Z]+)\\s+[0-9,]+\\s+(.*?"+number+".+)";
// month / day / year hour : min AMPM byte size filename containing number in middle
var pattern = new RegExp(mat,"gi");
var arr = ['09/07/2010 07:08 PM 1,465,536 BOL09416 BOL31.exe',
'09/06/2010 12:13 PM 110,225 BOL09416_BOL030.exe',
'09/08/2010 04:46 AM 60,564 BOL09416_BOL32.exe',
'09/08/2010 01:08 PM 63,004 bol09416_bol33.exe']
for (var i=0;i<arr.length;i++){
var match = pattern.exec(arr[i]);
alert(match);
}
It is all spaces (no tabs), I've rewriten the regex to be as explainatory as possible... It correctly matches on arr[0] and arr[2], but nulls on the other two.
Tried looking for possible typo's, trying different .+,.*,.+? etc. All online matchers show that it should be working: Example
Anybody have any ideas as to what I'm missing?
====================
Update:
Going through all the awesome suggestions I am stumped even further:
var match = arr[i].match(/([0-9]+\/[0-9]+\/[0-9]+\s+[0-9]+:[0-9]+)\s+([A-Z]+)\s+[0-9,]+\s+(.*?09416.+)/g);
gives match[0] = full string match[1] = undefined. Basically no captures.
where as:
var match = /([0-9]+\/[0-9]+\/[0-9]+\s+[0-9]+:[0-9]+)\s+([A-Z]+)\s+[0-9,]+\s+(.*?09416.+)/g.exec(arr[i]);
DOES return match[0] = full string, match[1] = date, and so on.
So I guess my real question is how to include dynamically made RegExpressions, and have multiple captures? As the only difference between:
var number = "09416";
var mat = "([0-9]+/[0-9]+/[0-9]+\\s+[0-9]+:[0-9]+)\\s+([A-Z]+)\\s+[0-9,]+\\s+(.*?09416.+)";
var pattern = new RegExp(mat,'g');
and
/([0-9]+\/[0-9]+\/[0-9]+\s+[0-9]+:[0-9]+)\s+([A-Z]+)\s+[0-9,]+\s+(.*?09416.+)/g.exec(arr[i]);
is that I hard-typed the number.
var number = 09416;
// month / day / year hour : min AMPM byte size filename containing number in middle
var mat = '^(\d{2}\/\d{2}\/\d{4})\s+(\d{2}:\d{2}\s*[AP]M)\s+((\d+[\d,]?))\s+(.' + number + '.*)$';
var pattern = new RegExp(mat);
var arr = ['09/07/2010 07:08 PM 1,465,536 BOL09416 BOL31.exe',
'09/06/2010 12:13 PM 110,225 BOL09416_BOL030.exe',
'09/08/2010 04:46 AM 60,564 BOL09416_BOL32.exe',
'09/08/2010 01:08 PM 63,004 bol09416_bol33.exe']
for (var i=0;i<arr.length;i++){
var match = arr[i].match(pattern);
console.log(match);
}
Use string.match instead of regex.exec.
Edited
I've removed the global and it worked like it should be. I've also rewritten the regex but it's quite close to yours (not a big deal).
Look at the output by firebug below:
["09/08/2010 04:46 AM ...,564 BOL09416_BOL32.exe", "09/08/2010", "04:46 AM", "60,564", "564", "BOL09416_BOL32.exe"]
0 "09/08/2010 04:46 AM ...,564 BOL09416_BOL32.exe" //whole match
1 "09/08/2010" //date
2 "04:46 AM" //time
3 "60,564" //bytes
4 "564" // last digit of bytes (i can't take this off. but it's harmless)
5 "BOL09416_BOL32.exe" //name of file
I would suggest you try Regexr to build the expression.
try this
([0-9]+/[0-9]+/[0-9]+ +?[0-9]+:[0-9]+) +?([A-Z]+) +?[0-9,]+ +?(.*?09416.*)
Try this regex:
new RegExp('([0-9]+/[0-9]+/[0-9]+\\s+[0-9]+:[0-9]+)\\s+(AM|PM)\\s+([0-9,]+)\\s+([^0-9]*'+number+'.+)','gi')
Time
AM/PM
File size
File name

Categories

Resources