Format DD/MM/YYYY in reactjs on Input Change - javascript

I want to add "/" when user enters in a input field after dd/mm/yyyy, basically user is trying to enter DOB, and want '/' to be automatically added after entering dd then mm then yyyy.
Also User wont be able to input more than 31 in dd & 12 in mm and for the year need to check if the DOB is invalid, in case of Leap year (example 29/02/2024) and not more than 4 digits.
I tried but when user enters text after 2 characters '/' is entered but the input is somehow lost. for example when user types '22' then '2', so only '22/' is shown not the 2.
Also i did tried the logic for the leap year but somehow managing the overall state in single input field is not working.
Here is what I tried.
import { useCallback, useState } from "react";
import moment from "moment";
import "./styles.css";
export default function App() {
const [dob, setDob] = useState("");
const [day, setDay] = useState("");
const [month, setMonth] = useState("");
const [year, setYear] = useState("");
const m = moment();
const [errors, setErrors] = useState();
const onchange = (e) => {
setDob(e.target.value);
checkAndAdd();
};
const handleDate = async () => {
let dayOfDob, monthOfDob, yearOfDob;
yearOfDob = dob.substring(6, 10);
if (dob.length === 2) {
dayOfDob = dob.substring(0, 2);
if (dayOfDob > 31) {
setErrors({ ...errors, dayO: "Day is greater" });
}
setDay(dayOfDob);
} else if (dob.length === 5) {
monthOfDob = dob.substring(3, 5);
if (monthOfDob > 12) {
setErrors({ ...errors, monthO: "Month is greater" });
}
setMonth(monthOfDob);
}
if (yearOfDob.length <= 4) {
let res = await isValidDate(dayOfDob, monthOfDob, yearOfDob);
console.log("validateDate", res);
}
};
const isValidDate = async (dayValue, monthValue, yearValue) => {
if (dayValue && monthValue && yearValue) {
let dayVal = dayValue.trim();
let monthVal = monthValue.trim();
const yearVal = yearValue.trim();
let leapYear = false;
if (dayVal != "" && monthVal != "" && yearVal != "") {
dayVal = dayVal <= 9 ? "0" + Number(dayVal) : dayVal;
monthVal = monthVal <= 9 ? "0" + Number(monthVal) : monthVal;
const d = new Date(yearVal + "-" + monthVal + "-" + dayVal);
if (yearVal % 4 !== 0 && monthVal == 2 && dayVal > 28) {
leapYear = true;
}
if (monthVal == 2 && dayVal > 29) {
leapYear = true;
}
if (yearVal.length === 4) {
if (!isNaN(d.getTime()) && !leapYear) {
setErrors("");
return d;
} else {
setErrors("DOB format is invalid");
return false;
}
}
} else {
return false;
}
}
};
const checkAndAdd = useCallback(() => {
if (dob.length === 2 || dob.length === 5) {
setDob(`${dob}/`);
handleDate();
}
}, [dob]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<input type="text" value={dob} onChange={onchange} />
</div>
);
}
Please let me know if explained it correctly.
Also working sandbox is here
Thanks.

To solve your problem of the number being replaced by a "/" you can simply do something like this on your onchange() and checkAndAdd() methods :
const onchange = (e) => {
const value = e.target.value;
setDob(value);
checkAndAdd(value);
};
const checkAndAdd = useCallback((value) => {
if (dob.length === 2 || dob.length === 5) {
const last = value.charAt(value.length - 1);
setDob(`${dob}/${last}`);
handleDate();
}
}, [dob]);

So I just had to do this for a non-React legacy site using jQuery but the approach is easily adapted. Here is how to do it in jQuery (I don't have time at the moment to rewrite it but it's mostly vanilla JavaScript):
$("#dob").bind("keyup", function(event) {
var value = event.target.value.replace(/[^0-9\/]/g, "").replace(/\/\/+/g, '/').trim();
if (priorValue !== null && priorValue.length < event.target.value.length && value.length > 1) {
const parts = value.split("/");
const month = parts[0];
const day = parts[1];
const year = parts[2];
if (value.length === 2 && value.indexOf("/") === -1) {
// "10" -> "10/"
event.target.value = value + "/";
} else if (value.length === 5 && parts.length !== 3) {
// "12/34" -> "12/34/"
event.target.value = value + "/";
} else if (value.length === 4 && value.charAt(1) === "/" && !value.charAt(3) === "/") {
// "1/15" -> "1/15/"
// but not "1/1/" -> "1/1//"
event.target.value = value + "/";
} else if (parts.length > 3 && value[value.length - 1] === "/") {
// "1/15/1950/" -> "1/15/1950"
event.target.value = value.slice(0, -1);
} else if (parts.length === 3 && year.length > 4) {
// "1/15/19501" -> "1/15/1950"
// avoiding slicing month and day w/ .slice(0, 2) for now...
event.target.value = month + "/" + day + "/" + year.slice(0, 4);
} else if (value !== event.target.value) {
event.target.value = value;
}
}
priorValue = event.target.value;
});
There are a bunch of gotchas like some event types not working on Android (although synthetic events in React might solve that for you). Also, I suggest setting HTML5 input hint to numeric on the field with inputmode="numeric" which will default mobile to numeric input.
I check the length and if it's shorter than the prior length in order to handle backspace/deleting.
It's not perfect but it's working fairly well. I think there might be better methods like defaulting to a date input on desktop and doing an iOS-style date spinner but that is a fair amount of work (or you can pay for it, there are actually commercial solutions which seems excessive but there you go).

Related

JavaScript : return maximum possible `HH:MM`

Leetcode problem:
You are given a string that represents time in the format hh:mm. Some of the digits are blank (represented by ?). Fill in ? such that the time represented by this string is the maximum possible. Maximum time: 23:59, minimum time: 00:00. You can assume that input string is always valid.
You can use the replace function ability of String.replace. The second parameter passed in is the position of the ?, so use that to look up the max value for that position in an array.
const maxValues = ["2", "3", ":", "5", "9"];
const maxTime = (time) => time.replace(/\?/g, (s, p, str) => {
if (p === 1 && str[0] !== '2' && str[0] !== '?' ) {
return '9';
}
return maxValues[p];
});
console.log(maxTime("?1:1?"))
console.log(maxTime("??:??"))
console.log(maxTime("11:11"))
console.log(maxTime("1?:?1"))
This is not a very elegant solution but it addresses the problem I pointed out in my reply to James, where the send H in HH can be either 9 or 3 depending on the time. This also is just different but also valid.
maxTime = (strTime) => {
let [...str] = strTime;
if (str[0] == "?") { str[0] = "2"; }
if (str[1] == "?" && str[0] < "2") { str[1] = "9"; } else { str[1] = "3"; }
if (str[3] == "?") { str[3] = "5"; }
if (str[4] == "?") { str[4] = "9"; }
return str.join('');
}
console.log(maxTime("?2:22")); // 22:22
console.log(maxTime("2?:22")); // 23:22
console.log(maxTime("22:?2")); // 22:52
console.log(maxTime("22:2?")); // 22:29
console.log(maxTime("0?:??")); // 09:59
console.log(maxTime("1?:??")); // 19:59
console.log(maxTime("??:??")); // 23:59
or a loop
const max = "29:59";
maxTime = (strTime) => {
let [...str] = strTime;
for (x = 0; x < 5; x++) { if (strTime[x] == "?") { str[x] = max[x]; }}
if (str[0] == "2" && strTime[1] == "?") { str[1] = "3"; }
return str.join('');
}
console.log(maxTime("?2:22")); // 22:22
console.log(maxTime("2?:22")); // 23:22
console.log(maxTime("22:?2")); // 22:52
console.log(maxTime("22:2?")); // 22:29
console.log(maxTime("0?:??")); // 09:59
console.log(maxTime("1?:??")); // 19:59
console.log(maxTime("??:??")); // 23:59
maxTime = (time) => {
const timeArr = time.split(":");
let hr = timeArr[0];
let mn = String(timeArr[1]);
if (mn.includes("?")) {
const mnArr = mn.split("");
if (mnArr[0] === "?" && mnArr[1] === "?") {
mn = "59";
} else if (mnArr[0] === "?") {
mn = "5" + mnArr[1];
} else if (mnArr[1] === "?") {
const temp = mnArr[0] === "5" ? "9" : "0";
mn = mnArr[0] + temp;
}
}
if (hr.includes("?")) {
const hrArr = hr.split("");
if (hrArr[0] === "?" && hrArr[1] === "?") {
hr = "23";
} else if (hrArr[0] === "?") {
hr = "2" + hrArr[1];
hr = Number(hr) <= 24 ? "1" + hrArr[1] : hr;
} else if (hrArr[1] === "?") {
const temp = hrArr[0] === "2" ? "3" : "9";
hr = hrArr[0] + temp;
}
}
return `(${time}) => ${hr}:${mn}`;
}

Javascript refactroing series of if else statement

I have a method and attached an event listener like below.
This method looks ugly. Is there any other way to refactor?
Any suggestions or ideas would be appreciated.
document.querySelector('dateInput').addEventListener('input', func.validateCalendar, false);
const func = {
validateCalendar({target}){
const tmpArr = Array.from(target.value.replace(/[^0-9\.]/g, ''));
if( tmpArr.length === 0 ){
target.value = '';
}else if( tmpArr.length === 4){
target.value = tmpArr.join('');
}else if( tmpArr.length === 5 ){ //month first digit
const month1 = +tmpArr[4]
console.log('len:5, month1: ', month1);
if( month1 > 1 ){
tmpArr.splice(4,1, '');
}
tmpArr.splice(4,0, '-');
target.value = tmpArr.join('');
}else if( tmpArr.length === 6){ //month second digit
const month1 = +tmpArr[4];
const month2 = +tmpArr[5];
const cond1 = month1 === 0 && month2 === 0;
const cond2 = month1 === 0 && month2 > 9;
const cond3 = month1 === 1 && month2 > 2
if( cond1 || cond2 || cond3 ){
tmpArr.splice(5,1, '');
}
tmpArr.splice(4,0, '-');
target.value = tmpArr.join('');
}else if( tmpArr.length === 7 ){ //day first digit
const month = +tmpArr.slice(4,6).join('');
const day1 = +tmpArr[6];
console.log('len 7 : day1 ', day1);
const cond1 = month !== 2 && day1 > 3;
const cond2 = month === 2 && day1 > 2
if( cond1 || cond2 ){
tmpArr.splice(6,1, '');
}
tmpArr.splice(4,0, '-')
tmpArr.splice(7,0, '-');
target.value = tmpArr.join('');
}else if( tmpArr.length === 8 ){ //day second digit
const year = +tmpArr.slice(1,4).join('');
const month = +tmpArr.slice(4,6).join('');
const day = +tmpArr.slice(6,8).join('');
const monthsIn31 = [1, 3, 5, 7, 8, 10, 12];
const monthsIn30 = [4, 6, 9, 11];
const cond1 = day === 0;
const cond2 = monthsIn31.includes(month) && day > 31;
const cond3 = monthsIn30.includes(month) && day > 30;
const cond4 = month === 2;
if( cond1 || cond2 || cond3){
tmpArr.splice(7,1, '');
}
if( cond4 ){
const cond1 = moment([year]).isLeapYear() && day > 29;
const cond2 = !moment([year]).isLeapYear() && day > 28
if( cond1 || cond2 ){
tmpArr.splice(7,1, '');
}
}
console.log('len 8 : ', target.value);
tmpArr.splice(4,0, '-')
tmpArr.splice(7,0, '-');
target.value = tmpArr.join('');
}else if( tmpArr.length > 8 ){
target.value = target.value.slice(0, -1);
}
},
}
You can try switch..case if you just check a variable again and again.
Thus if you currently have:
var a = 5
if(a === 5){
console.log("Five");
} else if(a === 7) {
console.log("Seven");
} else if (a === 9) {
console.log("Nine");
}
// ... and so on
else {
console.log("Irrelevant");
}
This is equivalent switch..case for it
var a = 5
switch(a) {
case 5:
console.log("Five");
break;
case 7:
console.log("Seven");
break;
// ... and so on
default:
console.log("Irrelevant");
}
However, meanwhile the if..else if uses curly brackets {} to limit the scope of the condition, switch..case uses break to limit it. The default in the switch..case is equivalent to the else in if..else, if and only if you put the break properly.
If you forget to put break, it will execute every cases placed under the satisfied condition until it finds the break or return or the default or the closing curly bracket } of the switch..case statement.

Return the days of the week concatenated when provided a string of consecutive days of the week

I need of a function that can take a string of days like "Mon,Tues,Wed,Thurs,Fri" and return "Mon-Fri" Of course, it cannot simply return first and last concatenated with "-" but should account for "Mon,Tues,Thurs,Fri" in that case could either return the input string OR "Mon-Tues, Thurs-Fri"
Virtual cheers to whoever can help out!
function daysConcat(days) {
let key = ['Sun','Mon','Tues','Wed','Thu','Fri','Sat'];
let daysArr = days.split(',');
let consecutive = true;
let lastIndex = '';
let concatDays = daysArr.filter((day, i, arr) => {
if (day === arr[arr.length-1] && consecutive && key.indexOf(day) === lastIndex+1) {
return day;
} else if (day === arr[0]) {
lastIndex = key.indexOf(day);
return day;
} else if (key.indexOf(day) === lastIndex+1) {
lastIndex = key.indexOf(day);
} else {
consecutive = false;
}
})
if (concatDays.length > 1) {
return `${concatDays[0]} - ${concatDays[concatDays.length-1]}`
} else {
return days
}
}
let consecutiveA = 'Mon,Tues,Wed,Thu,Fri';
console.log(daysConcat(consecutiveA));
let consecutiveB = 'Mon,Tues,Wed,Thu';
console.log(daysConcat(consecutiveB));
let notConsecutiveA = 'Mon,Tues,Thu,Fri';
console.log(daysConcat(notConsecutiveA));
let notConsecutiveB = 'Sun,Mon,Tues,Thu,Fri';
console.log(daysConcat(notConsecutiveB));
Currently the above works according to my first "ask", though I would love a more elegant/straightforward approach. Also it will of course not produce "Mon-Tues, Thurs-Fri" for input string "Mon,Tues,Thurs,Fri" - any thoughts or improvements appreciated!

Convert Date from one format to another format in JavaScript

I have a string of a date in javascript in format #1. I need to convert it to format #2.
The problem starts when one format is "dd/mm/yy" and the other is "mm/dd/yy".
The formats change dynamically and I have the formats as strings, but I need a function like
Date newDate = convert(currentDate, currentFormatString, newFormatString).
How can I do it?
You should look into momentjs, which is a javascript date/time library. With that, you can easily convert between dates of different format. In your case, it would be:
string newDate = moment(currentDate, currentFormatString).format(newFormatString)
For example, moment("21/10/14", "DD/MM/YY").format("MM/DD/YY") would return "10/21/14"
You can use the above described answer of momnet.js or the function below for splitting and using it
For using moment.js make sure you enter your old and new format in upper case
function parseDate(strDate) {
var dateFormat = $('#currentDateFormat').val();
var str;
var res;
var strFormat = dateFormat.split('.');
if (strFormat.length != 3)
strFormat = dateFormat.split('/');
if (strFormat.length != 3)
strFormat = dateFormat.split('-');
str = strDate.split('.');
if (str.length != 3)
str = strDate.split('/');
if (str.length != 3)
str = strDate.split('-');
if (strFormat[0].substr(0, 1) == 'd' && strFormat[1].substr(0, 1) == 'M' &&
strFormat[2].substr(0, 1) == 'y') // for dd MM yyyy
res = new Date(str[2], str[1], str[0]);
if (strFormat[0].substr(0, 1) == 'M' && strFormat[1].substr(0, 1) == 'd' &&
strFormat[2].substr(0, 1) == 'y') // for MM dd yyyy
res = new Date(str[2], str[0], str[1]);
if (strFormat[0].substr(0, 1) == 'y' && strFormat[1].substr(0, 1) == 'M' &&
strFormat[2].substr(0, 1) == 'd')
res = new Date(str[0], str[1], str[2]);
if (strFormat[0].substr(0, 1) == 'y' && strFormat[1].substr(0, 1) == 'd' &&
strFormat[2].substr(0, 1) == 'M')
res = new Date(str[0], str[2], str[1]);
return res;
}
You can use the function below
console.log(changeDateFormat('12/1/2020','dd/MM/yyyy','MM/dd/yyyy'));
function changeDateFormat(value, inputFormat, outputFormat) {
let outputSplitter = "/";
let strOutputFormat = outputFormat.split(outputSplitter).map(i => i.toUpperCase());
if (strOutputFormat.length != 3) {
strOutputFormat = outputFormat.split('-');
outputSplitter = '-';
}
if (strOutputFormat.length != 3) throw new Error('wrong output format splitter :(');
let date = null;
if (value instanceof Date) {
date = {
["YYYY"]: value.getUTCFullYear(),
["MM"]: value.getMonth() + 1,
["DD"]: value.getDate()
}
}
if (typeof value == 'string') {
let inputSplitter = "/";
var strInputFormat = inputFormat.split(inputSplitter).map(i => i.toUpperCase());
if (strInputFormat.length != 3) {
strInputFormat = inputFormat.split('-');
inputSplitter = '-';
}
if (strInputFormat.length != 3) throw new Error('wrong input format splitter :(');
let dateElements = value.split(inputSplitter);
if (dateElements.length != 3) throw new Error('wrong value :(');
date = {
[strInputFormat[0]]: dateElements[0],
[strInputFormat[1]]: dateElements[1],
[strInputFormat[2]]: dateElements[2],
}
}
if (!date) throw new Error('unsupported value type:(');
let result = date[strOutputFormat[0]] + outputSplitter
+ date[strOutputFormat[1]] + outputSplitter
+ date[strOutputFormat[2]];
return result;
}
It seems that you should use the library of query-dateformat git hub for jquery-dateformat
or you can use the normal function like date-time-formate, use the library to config the formate template

String Time Validations (In javascript)

I have a text box which accepts time(max of 5 characters only), and a drop down which accepts am or pm value.
I need to perform some validations for the string values entered into the text box such as:
If user enters 9 => Should be changed to 0900
9:3 => 0930
09:3 => 0930
93 => alert ('Invalid Hours value)
115 => 0115
12 => 1200
Invalid entries such as !##$%^&*()<>?/~`,;'"[]_-abcdefg.. => alert ('Invalid Time Value') should be displayed.
So far, all I've achieved is replacing the : with ''.
For example, if user enters 09:00 => 0900
I need something like:
if clks is 1 digit, then rightpad clks with 2 zeroes.
if 2 digits: clks > 12 , then alert(Invalid hours value)
if 3 digits: clks < (%59) (i.e checking last 2 chars) , then leftpad with 1 zero
or clks > (%59) , then alert ('Invalid minutes value)
if 4 digits: clks>12% (checking first 2 chars), alert ('invalid hours value')
or (clks>12% and clks>59%) , then alert('invalid clock time')
or (clks<12%) and (clks<59%) , then accept the number as it is.
if 5 digits: (and all are numbers), then alert('invalid clk time')
These validations need to be done within script tag. (The backend language that I've used is jsp.)
Pls help me :(
Here is a part of the code that I have written:
<script type='text/javascript'>
function clocks(){
var clk = document.getElementById('TIME').value;
var clks = clk.replace('/:/g','');
var ampm = document.getElementById('AMPM').value;
var add = 1200;
if (clks=="")
{
alert("You must enter clock time");
}
else
{
if (ampm=='p')
{
clks=parseFloat(clks) + parseFloat(add);
}
}
}
....
</script>
Here's a way to do it in a function:
function validate_time( clks ) {
// Remove any non-digit characters
clks = clks.toString().replace(/\D/g, '');
var is_valid = false;
switch (clks.length) {
case 1:
// This will run if the length is 1 digit.
clks = clks + '00';
// Mark this as a valid time.
is_valid = true;
// stop running the rest
break;
case 2:
if ( parseInt(clks) > 12 ) {
alert("Invalid hours value");
} else {
is_valid = true;
}
break;
case 3:
// Get last two characters
var mins = clks.substr(1,2); // offset 1 character, length of 2 characters
if ( parseInt(mins) <= 59 ) {
clks = '0' + clks;
is_valid = true;
} else {
alert('Invalid minutes value');
}
break;
case 4:
var hours = clks.substr(0,2);
var mins = clks.substr(2,2);
if ( parseInt(hours) > 12 || parseInt(mins) > 59 ) {
alert('Invalid clock time');
} else {
is_valid = true;
}
break;
case 5:
alert("Invalid clock time");
break;
}
var data = { clks: clks, is_valid: is_valid };
return data;
}
To call it, you'd do:
var result = validate_time(clks);
and your result would be an object passed back... result.clks is the padded time, and result.is_valid will either be a true or false value as to whether the input time is valid or not.
function validateTimeNew(obj) {
var timeValue = obj.value;
if (timeValue == "" || timeValue.indexOf(":") < 0) {
alert("Invalid Time format.Valid Format Example 01:56:00 or 23:06:00");
return false;
}
else {
var sHrs = timeValue.split(':')[0];
var smin = timeValue.split(':')[1];
smin = smin.substring(0, 2);
var sAmnPm = timeValue.split(' ')[1] || timeValue.split(':')[2];
if (!isNaN(sAmnPm))
{
sAmnPm = sAmnPm.toUpperCase();
}
var chkAmnPm =timeValue.split(':')[1];
if (chkAmnPm.length == 4)
sAmnPm = chkAmnPm.substring(2, 4);
sAmnPm = sAmnPm.toUpperCase();
if (sHrs == "" || isNaN(sHrs) || parseInt(sHrs) > 23) {
alert("Invalid Time format Hours : "+ sHrs);
return false;
}
else if (parseInt(sHrs) == 0) {
sHrs = "00";
sAmnPm = "AM";
}
else if (sHrs < 10 )
{
sHrs = "0" + parseInt(sHrs);
if (sAmnPm != "PM")
sAmnPm = "AM";
}
else if (sHrs > 13) {
sHrs = parseInt(sHrs) - 12;
if (sHrs < 10)
sHrs = "0" + parseInt(sHrs);
sAmnPm = "PM";
}
else if (sHrs == 10 )
{
sHrs = parseInt(sHrs);
if (sAmnPm != "PM")
sAmnPm = "AM";
}
if (smin == "" || isNaN(smin) || parseInt(smin) > 59) {
alert("Invalid Time format Minutes :" + smin);
return false;
}
else if (parseInt(smin) == 0)
smin = "00";
else if (smin < 10)
smin = "0" + parseInt(smin);
if (sAmnPm == "" || sAmnPm=="undefined")
{
sAmnPm = "AM"
}
else { sAmnPm = sAmnPm.toUpperCase(); }
obj.value = sHrs + ":" + smin + ":" + sAmnPm;
}
}

Categories

Resources