Javascript / Elastic COMPLEX Date Math Regex - javascript

I am trying to validate a user input date range. We can accept ISO8601 formatted dates and Date Math Expressions since I am passing it into elasticsearch.
Due to the way the code is, they enter it via an input box so I have to validate the text manually and force them into correct input (unable to transform to proper format).
Sample valid user inputs:
[2021 to 2022]
[2020-12-25T14:48Z to now]
[now-5y to now+1w]
[2020-12-25+1M to now-1m]
Sample invalid user inputs:
[2020-12-25tt4:48Z to now]
[NOW-5y to now+1w]
[now-5y too now+1w]
[2020-12-25+1M To now-1h/d]
So far I [think] I have found a way to confirm the valid ISO8601 and range delimiter but I am having trouble with the regex for the + - / options. This is a snippet of my code so far:
const ALLOWED_NOW_CONSTANTS = /\bnow([+-/][1-9][yMwdhHms])?/; // ISSUE WITH DATE MATHS HERE
const ISO_8601 = /^\d{4}(-\d\d(-\d\d(T\d\d:\d\d(:\d\d)?(\.\d+)?(([+-]\d\d:\d\d)|Z)?)?)?)?$/; // ISSUE ADDING DATE MATHS TO THIS ALSO
const validateDateExpression = (dateStr) => {
return ALLOWED_NOW_CONSTANTS.test(dateStr) || ISO_8601.test(dateStr);
}
const str = userInput.trim();
if (str.startsWith('[') && str.endsWith(']')) {
const dateExpression = trim(str, '[]').split(/(\bto\b)/i);
if (dateExpression.length !== 3) {
return '[startTime to endTime]'
}
if ( !validateDateExpression(dateExpression[0].trim()) ) {
return `${dateExpression[0]} is not a valid format`;
}
if ( !validateDateExpression(dateExpression[2].trim()) ) {
return `${dateExpression[2]} is not a valid format`;
}
return ''
}
So my issue with my regex is for the optional "rounded" date maths as well as the date maths for ISO8601. I want to be able to put in the valid date maths but the : now, now/d, now-3M/y, now-1h/d, 2017-03-22+1y, 2020-12-25T14:48:10.78Z+5w/M, 2020-12-25T14:48:10.78Z/M, etc. are cases where I can't seem to get a regex working for this.
This is because of the optional cases where if it is / then it has to be the yMwdhHms that follows instead of a digit. I can't figure out a regex to get this working (now+8h/d or now/y) .
This is also an issue for my ISO8601 date since they can add the date maths at the end and the ISO8601 date is so flexible.
Any help with this is appreciated.
If anything needs clarification on my end or if there's better solutions to this within my current restrictions let me know.

Related

Return the date format for date without moment.js

I can't figure out how to build a function that does this
INPUT: dateToDateFormat('16/07/2022') -> OUTPUT: DD/MM/YYYY
INPUT: dateToDateFormat('07/16/2022') -> OUTPUT: MM/DD/YYYY
The input dates will be already formatted by .toLocaleString function
Edit:
I think I was not precise enough with the output, it should literally output MM/DD/YYYY as a string, not the actual date value
A) First of all, your desired date string conversion seems to be easy (solution below) without using any package/overhead.
B) However, if you need to process it further as a JS Date Object, things become more difficult. In this case, I like to provide some useful code snippets at least which might help you depending on your usecase is or what you are aiming for.
A) CONVERSION
Seems you need a simple swap of day and month in your date string?
In this case, you do not need to install and import the overhead of a package such as date-fns, moment or day.js.
You can use the following function:
const date1 = "16/07/2022"
const date2 = "07/16/2022"
const dateToDateFormat = (date) => {
const obj = date.split(/\//);
return `${obj[1]}/${obj[0]}/${obj[2]}`;
};
console.log("New date1:", dateToDateFormat(date1))
console.log("New date2:", dateToDateFormat(date2))
B) STRING TO DATE
Are you using your date results to simply render it as string in the frontend? In this case, the following part might be less relevant.
However, in case of processing them by using a JS Date Object, you should be aware of the following. Unfortunately, you will not be able to convert all of your desired date results/formats, here in this case date strings "16/07/2022" & "07/16/2022", with native or common JS methods to JS Date Objects in an easy way due to my understanding. Check and run the following code snippet to see what I mean:
const newDate1 = '07/16/2022'
const newDate2 = '16/07/2022'
const dateFormat1 = new Date(newDate1);
const dateFormat2 = new Date(newDate2);
console.log("dateFormat1", dateFormat1);
console.log("dateFormat2", dateFormat2);
dateFormat2 with its leading 16 results in an 'invalid date'. You can receive more details about this topic in Mozilla's documentation. Furthermore, dateFormat1 can be converted to a valid date format but the result is not correct as the day is the 15th and not 16th. This is because JS works with arrays in this case and they are zero-based. This means that JavaScript starts counting from zero when it indexes an array (... without going into further details).
CHECK VALIDITY
In general, if you need to further process a date string, here "16/07/2022" or "07/16/2022", by converting it to a JS Date Object, you can in any case check if you succeed and a simple conversion with JS methods provides a valid Date format with the following function. At least you have kind of a control over the 'invalid date' problem:
const newDate1 = '07/16/2022'
const newDate2 = '16/07/2022'
const dateFormat1 = new Date(newDate1);
const dateFormat2 = new Date(newDate2);
function isDateValidFormat(date) {
return date instanceof Date && !isNaN(date);
}
console.log("JS Date Object?", isDateValidFormat(dateFormat1));
console.log("JS Date Object?", isDateValidFormat(dateFormat2));
Now, what is the benefit? You can use this function for further processing of your date format depending on what you need it for. As I said, it will not help us too much as we still can have valid date formats but with a falsy output (15th instead of 16th).
CONVERT TO DATE OBJECT BY KNOWING THE FORMAT
The following function converts any of your provided kinds of dates ("MM/DD/YYYY" or "DD/MM/YYYY") to a valid JS Date Object and - at the same time - a correct date. However, drawback is that it assumes to know what kind of input is used; "MM/DD/YYYY" or "DD/MM/YYYY". The dilemma is, that this information is crucial. For example, JS does not know if, for example, "07/12/2022" is "MM/DD/YYYY" or "DD/MM/YYYY". It would return a wrong result.
const newDate1 = "07/16/2022"
const newDate2 = "16/07/2022"
function convertToValidDateObject(date, inputFormat) {
const obj = date.split(/\//);
const obj0 = Number(obj[0])
const obj1 = Number(obj[1])
const obj2 = obj[2]
//
// CHECK INPUT FORMAT
if (inputFormat === "MM/DD/YYYY") {
return new Date(obj2, obj0-1, obj1+1);
} else if (inputFormat === "DD/MM/YYYY") {
return new Date(obj2, obj1-1, obj0+1);
} else {
return "ERROR! Check, if your input is valid!"
}
}
console.log("newDate1:", convertToValidDateObject(newDate1, "MM/DD/YYYY"))
console.log("newDate2:", convertToValidDateObject(newDate2, "DD/MM/YYYY"))
console.log("newDate2:", convertToValidDateObject(newDate2, "MM/YYYY"))
If the wrong format is provided as a second argument, an error is provided in the console. In practise I suggest you to use a try-catch block ( I tried here, but it does not work here in this stackoverflow editor).
I wish you good luck. Hope these information can help you.

Allow regex or other method to format mm/yyyy date format while user types on text input field

I am trying to formulate a javascript on a text input field to help user format MM/YYYY while filling out a form in real time. Must work in all browsers include IE11+. Can include just numbers so they don't have to type the '/'.
I'm trying regex, but probably date formatting is better option?
So if user types: 112002 as they are typing, it should type 11/2002.
If user types ab12000, it would ignore the ab,
and start 12/ , then ignore the 000 after that until they type valid
date input, which would be a valid 4 digit year.
So far what I have for the onChange event:
function(field, event, opts ){
var s = new String(field.value());
if(s != null && s != '')
{
if(s.length>3) // better conditional statement?
{
s = s.replace(/[^0-9]/g,''); // need more/different validation criteria here for date
if(s.length>2) // better conditional statement?
{
s = [s.slice(0,2),'/',s.slice(2,6)].join('');
}
//something here to set the value ?
}
}
}

How to format user input into the correct localized format?

I have an <input type="text"/> where the user can (try to) type in a date in whatever format/syntax (even in an invalid format).
I want to get the value of whatever the user typed in, pass it through a localized moment and then update the input with the correct format.
I'm trying to follow these guidelines in order to use a local moment
// I want to use a local instance of moment
let localLocale = moment();
// I want to set the locale to be 'fr'
localLocale.locale('fr')
// I want to set the format to be 'LL'
localLocale.format('LL')
// this is what the user typed in
let userInput = '2/3/1986'
// I want to do:
let formattedUserInput = something(userInput)
The value of formattedUserInput must be Mars 2, 1986
I'm looking for what something should be. The moment docs are so confusing there is no explanation on how to do this.
If userInput is obviously gibberish, the something() should return null or throw an error or whatever I don't mind.
I tried localLocale(userInput) but it throws a localLocale is not a function
You can use moment(String, String[]) to parse inputs in difefrent formats:
If you don't know the exact format of an input string, but know it could be one of many, you can use an array of formats.
You can use moment.ISO_8601, as shown here, to parse ISO 8601 inputs as moment(String) does.
Please note that moment(String, String[])
Starting in version 2.3.0, Moment uses some simple heuristics to determine which format to use. In order:
Prefer formats resulting in valid dates over invalid ones.
Prefer formats that parse more of the string than less and use more of the format than less, i.e. prefer stricter parsing.
Prefer formats earlier in the array than later.
One possible solution can be the following:
function something(userInput){
let m = moment(userInput, [moment.ISO_8601, 'DD/MM/YYYY', 'MM/DD/YYYY' ]);
if( !m.isValid() ){
// throw "Invalid input";
}
return m.locale('fr').format('LL');
}
['2/3/1986', 'aaa', '10-15-2017'].forEach((userInput) => {
console.log( something(userInput) );
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/locale/fr.js"></script>
The locale set is local to the moment instance you have defined. So
let localLocale = moment();
localLocale.locale('fr');
sets the local for localLocale to 'fr'. So if you want to do it locally for just this input, you'd use:
// this is what the user typed in
let userInput = '2/3/1986';
// Use a local instance of moment, using the user's input
let localLocale = moment(userInput, 'D/M/YYYY');
// Set the locale to be 'fr'
localLocale.locale('fr');
// Get the formatted string
let formattedUserInput = localLocale.format('LL');
console.log(formattedUserInput);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment-with-locales.min.js" integrity="sha256-VrmtNHAdGzjNsUNtWYG55xxE9xDTz4gF63x/prKXKH0=" crossorigin="anonymous"></script>

How to check date validation in regex using js? [duplicate]

This question already has answers here:
Javascript: how to validate dates in format MM-DD-YYYY?
(21 answers)
Closed 6 years ago.
I am trying to validate date in js ("yyyy/mm/dd") format. After googling I found other date format checked but I can't get in this format.
Any one plz can help me out.
Here is my code.
function dateChecker()
{
var date1, string, re;
re = new RegExp("\d{4}/\d{1,2}/\{1,2}");
date1 = document.getElementById("visitDate").value;
if(date1.length == 0)
{
document.getElementById("showError").innerHTML = "Plz Insert Date";
document.getElementById("showError").style.color = "red";
}
else if(date1.match(re))
{
document.getElementById("showError").innerHTML = "Ok";
document.getElementById("showError").style.color = "red";
}
else
{
document.getElementById("showError").innerHTML = "It is not a date";
document.getElementById("showError").style.color = "red";
}
}
Try this:
var date = "2017/01/13";
var regex = /^[0-9]{4}[\/][0-9]{2}[\/][0-9]{2}$/g;
console.log(regex.test(date)); // true
console.log(regex.test("13/01/2017")); //false
console.log(regex.test("2017-01-13")); // false
If you use new RegExp then you must call compile on the resulting regular expression object.
re = new RegExp("\d{4}/\d{1,2}/\d{1,2}");
re.compile();
Alternatively you could write the regex this way which does not require compile to be called.
re = /\d{4}\/\d{1,2}\/\d{1,2}/;
EDIT
Note that the above regex is not correct (ie it can approve invalid dates). I guess the brief answer is, don't use regex to validate date times. Use some datetime library like momentjs or datejs. There is too much logic. For instance, how do you handle leap years, different months having different number of possible days, etc. Its just a pain. Use a library that can parse it, if it cant be parsed, its not a date time. Trust the library.
However you could get closer with something like this
re = /^\d{4}\/(10|11|12|\d)\/((1|2)?\d|30|31)$/;
Also if you want to get comfortable with regex, download Expresso

How do I convert a phone number in NodeJS to a standard format?

I want to standardise phone numbers into a +<countrycode><areacode><number> format. Problem is the input might be:
+972-54-5123456
+972545123456
972545123456
+972 (54) 5123456
00972545123456
0545123456 // especially problematic, as I have to assume it's an Israeli number
I would like to normalize all to either 972545123456 or +972545123456 format, whatever the input is. So it will probably be:
normalizeNumber('0545123456',default_country="IL")
Use Google's libphonenumber. Here's the npm:
https://www.npmjs.com/package/google-libphonenumber
Taken from that page, a usage example:
// Require `PhoneNumberFormat`.
var PNF = require('google-libphonenumber').PhoneNumberFormat;
// Get an instance of `PhoneNumberUtil`.
var phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance();
// Parse number with country code.
var phoneNumber = phoneUtil.parse('202-456-1414', 'US');
// Print number in the international format.
console.log(phoneUtil.format(phoneNumber, PNF.INTERNATIONAL));
// => +1 202-456-1414
Pretty simple, just code it up:
function normalizeNumber(input, default_country) {
return String(input)
.replace(/[^+0-9]/g, '') // remove non-number (and +) characters
.replace(/^00/, '+') // replace leading 00 with +
.replace(/^0/, getCountryCode(default_country)) // replace leading 0 with default code
}
If you want you can split the statement up and add some checks, such as whether the final result starts with a + and/or is of some expected length.
The following modules could be used as a source for getCountryCode:
country-data
country-calling-codes

Categories