C# Validating User Input like a Credit Card number - c#

This is for an assignment. I need to create a program for a sandwich shop. Part of it is to validate a user's payment info. The guidelines for this assignment are
Credit card number must be 16 digits; the first 4 digits must be one of these:
1298, 1267, 4512, 4567, 8901, 8933
Expiration date must be 2 digits for the month, 4 digits for the year. The month must be 01 - 12. The year must begin with "20". The date must be greater than the current actual month and year, and less than 6 years from the current actual month and year.
I have a text box for each number, txtCardNumber, txtSecuritycode, and txtExpiration. What I have works, but I think it's kind of messy. What would be a better way? And would it be possible to have only one Validate() method instead of a separate one for each?
public bool IsValidCard()
{
if (txtCardNumber.Text.StartsWith("1298") ||
txtCardNumber.Text.StartsWith("1267") ||
txtCardNumber.Text.StartsWith("4512") ||
txtCardNumber.Text.StartsWith("4567") ||
txtCardNumber.Text.StartsWith("8901") ||
txtCardNumber.Text.StartsWith("8933"))
{
if (Regex.Replace(txtCardNumber.Text, #"\s+", "").Length == 16)
{
return true;
}
}
return false;
}//end IsValidCard()
public bool IsValidSecurityCode()
{
bool isValid = Regex.Match(txtSecurityCode.Text, #"^\d{3}$").Success;
return isValid;
}//end IsValidSecurityCode()
public bool IsValidExpiration()
{
string[] date = Regex.Split(txtExpiration.Text, "/");
string[] currentDate = Regex.Split(DateTime.Now.ToString("MM/yyyy"), "/");
int compareYears = string.Compare(date[1], currentDate[1]);
int compareMonths = string.Compare(date[0], currentDate[0]);
//if expiration date is in MM/YYYY format
if (Regex.Match(txtExpiration.Text, #"^\d{2}/\d{4}$").Success)
{
//if month is "01-12" and year starts with "20"
if (Regex.Match(date[0], #"^[0][1-9]|[1][0-2]$").Success)
{
//if expiration date is after current date
if ((compareYears == 1) || (compareYears == 0 && (compareMonths == 1)))
{
return true;
}
}
}
return false;
}//end IsValidExpiration
Note: I haven't worked on validating the six year requirement.

You can use the following method, which will validate all the credit card info as per your requirements.
<1> Credit card number must be 16 digits; the first 4 digits must be one of these: 1298, 1267, 4512, 4567, 8901, 8933
<2> Security code must be 3 digit numeric (assumed)
<3> Expiration date must be 2 digits for the month, <4> 4 digits for the year. <5> The month must be 01 - 12. <6> The year must begin with "20". <7> The date must be greater than the current actual month and year, and <8> less than 6 years from the current actual month and year.
public static bool IsCreditCardInfoValid(string cardNo, string expiryDate, string cvv)
{
var cardCheck = new Regex(#"^(1298|1267|4512|4567|8901|8933)([\-\s]?[0-9]{4}){3}$");
var monthCheck = new Regex(#"^(0[1-9]|1[0-2])$");
var yearCheck = new Regex(#"^20[0-9]{2}$");
var cvvCheck = new Regex(#"^\d{3}$");
if (!cardCheck.IsMatch(cardNo)) // <1>check card number is valid
return false;
if (!cvvCheck.IsMatch(cvv)) // <2>check cvv is valid as "999"
return false;
var dateParts = expiryDate.Split('/'); //expiry date in from MM/yyyy
if (!monthCheck.IsMatch(dateParts[0]) || !yearCheck.IsMatch(dateParts[1])) // <3 - 6>
return false; // ^ check date format is valid as "MM/yyyy"
var year = int.Parse(dateParts[1]);
var month = int.Parse(dateParts[0]);
var lastDateOfExpiryMonth = DateTime.DaysInMonth(year, month); //get actual expiry date
var cardExpiry = new DateTime(year, month, lastDateOfExpiryMonth, 23, 59, 59);
//check expiry greater than today & within next 6 years <7, 8>>
return (cardExpiry > DateTime.Now && cardExpiry < DateTime.Now.AddYears(6));
}
Though this works fine for all the conditions, please check properly before using in production.

Related

How to validate DateTime for a leap year

I'm working with C# and I'm trying to find whether the given date and month is valid for a leap year. This is my code:
static void Main(string[] args)
{
Console.WriteLine("The following program is to find whether the Date and Month is Valid for an LEAP YEAR");
Console.WriteLine("Enter the Date");
int date = Convert.ToInt16(Console.ReadLine()); //User values for date and month
Console.WriteLine("Enter the Month");
int month = Convert.ToInt16(Console.ReadLine());
{
if (month == 2 && date < 30) //Determination of month and date of leap year using If-Else
Console.WriteLine("Your input is valid");
else if ((month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) && date < 32)
Console.WriteLine("Your inpput valid1");
else if (( month == 4 || month == 6 || month == 9 || month == 11 ) && date < 31)
Console.WriteLine("Your inpput valid2");
else
Console.WriteLine("Your input INvalid");
Console.ReadKey();
}
}
My question is, can I use DateTime for this program or is this a better way? Any suggestions are welcome.
I would suggest taking the input as a string and then using the DateTime.TryParse method. DateTime.TryParse accepts a string and an out DateTime (out keyword), and returns true if the string input was both correctly parsed and is a valid DateTime, and false otherwise.
From the documentation:
If s is the string representation of a leap day in a leap year in the current calendar, the method parses s successfully. If s is the string representation of a leap day in a non-leap year in the current culture's current calendar, the parse operation fails and the method returns false.
Usage example:
Console.WriteLine("Please enter a date.");
string dateString = Console.ReadLine();
DateTime dateValue;
if (DateTime.TryParse(dateString, out dateValue))
{
// Hooray, your input was recognized as having a valid date format,
// and is a valid date! dateValue now contains the parsed date
// as a DateTime.
Console.WriteLine("You have entered a valid date!");
}
else
{
// Aww, the date was invalid.
Console.WriteLine("The provided date could not be parsed.");
}
You could use DateTime.DaysInMonth with a year that is a known leap year like 2016.
if (month >= 1 && month <= 12 && date >= 1 && date <= DateTime.DaysInMonth(2016, month))
Console.WriteLine("Your input is valid");
else
Console.WriteLine("Your input is invalid");
Use a known leap year for the year part e.g. 2000 and append the month and day and year to form a string like mm-dd-2000 where mm and dd are the user entered values. Then use the DateTime.TryParse method which returns true if the date is valid.
If you're working from separate parts, then just:
try
{
new DateTime(year, month, day);
}
catch (ArgumentOutOfRangeException)
{
// it's not valid
}
Though if you prefer not to rely on exceptions, then go with juharr's answer, using DateTime.DaysInMonth.

How to compare a given date from today

I want to compare a given date to today and here is the condition: If provided date is greater than or equal to 6 months earlier from today, return true else return false
Code:
string strDate = tbDate.Text; //2015-03-29
if (DateTime.Now.AddMonths(-6) == DateTime.Parse(strDate)) //if given date is equal to exactly 6 months past from today (change == to > if date has to be less 6 months)
{
lblResult.Text = "true"; //this doesn't work with the entered date above.
}
else //otherwise give me the date which will be 6 months from a given date.
{
DateTime dt2 = Convert.ToDateTime(strDate);
lblResult.Text = "6 Months from given date is: " + dt2.AddMonths(6); //this works fine
}
If 6 months or greater than 6 months is what I would like for one
condition
If less than 6 months is another condition.
Your first problem is that you're using DateTime.Now instead of DateTime.Today - so subtracting 6 months will give you another DateTime with a particular time of day, which is very unlikely to be exactly the date/time you've parsed. For the rest of this post, I'm assuming that the value you parse is really a date, so you end up with a DateTime with a time-of-day of midnight. (Of course, in my very biased view, it would be better to use a library which supports "date" as a first class concept...)
The next problem is that you are assuming that subtracting 6 months from today and comparing it with a fixed date is equivalent to adding 6 months to the fixed date and comparing it with today. They're not the same operation - calendar arithmetic just doesn't work like that. You should work out which way you want it to work, and be consistent. For example:
DateTime start = DateTime.Parse(tbDate.Text);
DateTime end = start.AddMonths(6);
DateTime today = DateTime.Today;
if (end >= today)
{
// Today is 6 months or more from the start date
}
else
{
// ...
}
Or alternatively - and not equivalently:
DateTime target = DateTime.Parse(tbDate.Text);
DateTime today = DateTime.Today;
DateTime sixMonthsAgo = today.AddMonths(-6);
if (sixMonthsAgo >= target)
{
// Six months ago today was the target date or later
}
else
{
// ...
}
Note that you should only evaluate DateTime.Today (or DateTime.Now etc) once per set of calculations - otherwise you could find it changes between evaluations.
Try with this
DateTime s = Convert.ToDateTime(tbDate.Text);
s = s.Date;
if (DateTime.Today.AddMonths(-6) == s) //if given date is equal to exactly 6 months past from today (change == to > if date has to be less 6 months)
{
lblResult.Text = "true"; //this doesn't work with the entered date above.
}
replace == with >= or <= according to your needs

C# Linq to Entities get records where date range contains dates in the current month

I have Holiday EF Entity:
public class Holiday {
public int HolidayId {
get;
set;
}
public DateTime StartDate {
get;
set;
}
public DateTime EndDate {
get;
set;
}
public bool IsActive {
get;
set;
}
}
I need to get all records that has at least one day within the current month.
For example:
StartDate = "2014-06-29"
EndDate = "2014-07-03"
If current month is July then I should get that record, because after that I will have to create a List<int> of all the days affecting current month. So in the last sample I will have to create a List<int> of:
1,2 and 3 of July.
For example:
StartDate = "2014-07-23"
EndDate = "2014-08-01"
I should get that record also.
I was doing:
var holidays = Holidays.Where(h=> h.IsActive == true && h.StartDate < currentDate && h.EndDate > currentDate).ToList();
But, the query is not doing what I need.
Any clue?
What about this:
.Where(h => h.IsActive == true && (h.StartDate.Month == currentDate.Month || h.EndDate.Month == currentDate.Month)).ToList();
UPDATED:
This won't work when I have a date range where it's month is not equal to currentDate.Month. Like:
StartDate = "2014-06-29"
EndDate = "2014-08-03"
The solution that worked is:
Maybe is not the cleanest.
var holidays = Uow.HolidayRepository.GetAllReadOnly().Where(h => h.IsActive == true && (h.StartDate.Month == currentDate.Month || h.EndDate.Month == currentDate.Month) || (currentDate.Month > h.StartDate.Month && currentDate.Month < h.EndDate.Month)).ToList();
Neither test is sufficient: if the current date is today in then the first test will miss any date ranges that don't include the current date so if today is the first of the month any date range starting on the second although valid won't be caught. In addition single day ranges - unless you are including times won't be caught because the start and end dates could be equal to the current date but can never be greater than and less than - depends how you spec your date range.
The second test fails firstly because every year has months 1- 12 so not only will you be picking records for this year but for all years and secondly you miss any periods that start and end outside of the month.
Creating a list of days to compare doesn't sound efficient minimum number of runs 28 max 31.
So you need to test for ranges that either start in the current month or end in the current month as per Daniels code but in addition you need to test where the start date is before the current month and the end date is after the current month I think that should cover all cases?
I think this will do,please try it out:
int month = DateTime.Now.Month;
var result = Holidays.AsEnumerable().Where(h => Enumerable.Range(h.StartDate.Month, (h.EndDate.Month - h.StartDate.Month) + 1).Contains(month));

Create valid datetime object with validation [duplicate]

This question already has answers here:
Wanted: DateTime.TryNew(year, month, day) or DateTime.IsValidDate(year, month, day)
(2 answers)
Closed 4 years ago.
I am creating date using following code
try
{
newdatetime = new DateTime(2012, 2, 30);
break;
}
catch (ArgumentOutOfRangeException)
{
// Try 29 Feb if not 28.
}
The catch block is to catch the invalid date like 30 Feb. Is there any way to verify if the date is valid by speciying the parameters like (year, month, day)?
Well, with months you know the valid range so you can constrain that manually. Years are obviously not constrained in the normal sense, but are instead limited by the amount that DateTime can actually hold (0001 to 9999).
With days, there is the DaysInMonth(int year, int month) method that can tell you the maximum days for the provided month. This also gives you the leap year.
With this information, you can create your own method to check the range based on the provided integers.
Something like:
public static bool AreValidDateValues(int year, int month, int day)
{
if (month < 1 || month > 12)
return false;
if (year < DateTime.MinValue.Year || year > DateTime.MaxValue.Year)
return false;
var days = DateTime.DaysInMonth(year, month);
if (day < 1 || day > days)
return false;
return true;
}
Or if you can't be bothered with that, convert the raw values into a string representation of a date and put that into DateTime.TryParse, which will give a true/false for the provided string - just be careful with culture-sensitive parsing.
You can use DateTime.TryParse to perform the check :
DateTime d;
var isValid = DateTime.TryParse(String.Format("{0}/{1}/{2}", 2, 31, 2012), out d);
Console.WriteLine(isValid);

Convert a two digit year to a four digit year

This is a question of best practices. I have a utility that takes in a two digit year as a string and I need to convert it to a four digit year as a string. right now I do
//DOB's format is "MMM (D)D YY" that first digit of the day is not there for numbers 1-9
string tmpYear = rowIn.DOB.Substring(rowIn.DOB.Length - 3, 2); //-3 because it is 0 indexed
if (Convert.ToInt16(tmpYear) > 50)
tmpYear = String.Format("19{0}", tmpYear);
else
tmpYear = String.Format("20{0}", tmpYear);
I am sure I am doing it horribly wrong, any pointers?
The .NET framework has a method that does exactly what you want:
int fourDigitYear = CultureInfo.CurrentCulture.Calendar.ToFourDigitYear(twoDigitYear)
That way you will correctly adhere to current regional settings as defined in Control Panel (or group policy):
Given that there are people alive now born before 1950, but none born after 2010, your use of 50 as the flipping point seems broken.
For date of birth, can you not set the flip point to the 'year of now' (i.e. 10) in your app? Even then you'll have problems with those born before 1911...
There's no perfect way to do this - you're creating information out of thin air.
I've assumed DOB = date-of-birth. For other data (say, maturity of a financial instrument) the choice might be different, but just as imperfect.
You can also use the DateTime.TryParse method to convert your date. It uses the current culture settings to define the pivot year (in my case it is 2029)
DateTime resultDate;
Console.WriteLine("CultureInfo.CurrentCulture.Calendar.TwoDigitYearMax : {0}", System.Globalization.CultureInfo.CurrentCulture.Calendar.TwoDigitYearMax);
DateTime.TryParse("01/01/28", out resultDate);
Console.WriteLine("Generated date with year=28 - {0}",resultDate);
DateTime.TryParse("01/02/29",out resultDate);
Console.WriteLine("Generated date with year=29 - {0}", resultDate);
DateTime.TryParse("01/03/30", out resultDate);
Console.WriteLine("Generated date with year=30 - {0}", resultDate);
The output is:
CultureInfo.CurrentCulture.Calendar.TwoDigitYearMax : 2029
Generated date with year=28 - 01/01/2028 00:00:00
Generated date with year=29 - 01/02/2029 00:00:00
Generated date with year=30 - 01/03/1930 00:00:00
If you want to change the behavior you can create a culture with the year you want to use as pivot. This thread shows an example
DateTime.TryParse century control C#
But as martin stated, if you want to manage a time period that spans more than 100 year, there is no way to do it with only 2 digits.
I think Java has a good implementation of this:
http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html#year
People rarely specify years far into the future using a two-digit code. The Java implementation handles this by assuming a range of 80 years behind and 20 years ahead of the current year. So right now, 30 would be 2030, while 31 would be 1931. Additionally, this implementation is flexible, modifying its ranges as time goes on, so that you don't have to change the code every decade or so.
I just tested, and Excel also uses these same rules for 2-digit year conversion. 1/1/29 turns into 1/1/2029. 1/1/30 turns into 1/1/1930.
The implementation of
System.Globalization.CultureInfo.CurrentCulture.Calendar.ToFourDigitYear
is
public virtual int ToFourDigitYear(int year)
{
if (year < 0)
throw new ArgumentOutOfRangeException("year", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (year < 100)
return (this.TwoDigitYearMax / 100 - (year > this.TwoDigitYearMax % 100 ? 1 : 0)) * 100 + year;
else
return year;
}
Hope this helps!
It might be smarter to check tmpYear > currentYear%100. If it is, then it's 19XX, otherwise 20XX.
This solution we use for Expiration Dates, the user enters MM and YY into separate fields. This results in dates being the 31st or 30th and 28th or 29th also for February.
/// <summary>
/// Creates datetime for current century and sets days to end of month
/// </summary>
/// <param name="MM"></param>
/// <param name="YY"></param>
/// <returns></returns>
public static DateTime GetEndOfMonth(string MM, string YY)
{
// YY -> YYYY #RipVanWinkle
// Gets Current century and adds YY to it.
// Minus 5 to allow dates that may be expired to be entered.
// eg. today is 2017, 12 = 2012 and 11 = 2111
int currentYear = DateTime.Now.Year;
string thisYear = currentYear.ToString().Substring(0, 2) + YY;
int month = Int32.Parse(MM);
int year = Int32.Parse(thisYear);
if ((currentYear - 5) > year)
year += 100;
return new DateTime(year, month, DateTime.DaysInMonth(year, month));
}
This Method can convert the credit card last two year digits to four year
private static int ToFourDigitYear(int year)
{
string stringYear = year.ToString("00");
if (stringYear.Length == 2)
{
int currentYear = DateTime.Now.Year;
string firstTwoDigitsOfCurrentYear = currentYear.ToString().Substring(0, 2);
year = Convert.ToInt32(firstTwoDigitsOfCurrentYear + stringYear);
if (year < currentYear)
year = year + 100;
}
return year;
}
Out of curiosity, from where do you get this data? From a form? In that case; I would simply ask the user to fill in (or somehow select) the year with four digits or get the users age and month/day of birth, and use that data to figure out what year they were born. That way, you wouldn't have to worry about this problem at all :)
Edit: Use DateTime for working with this kind of data.
Try this simple code
//Invoke TextBoxDateFormat method with date as parameter.
Method
public void TextBoxDateFormat(string str1)
{
// Takes the current date format if MM/DD/YY or MM/DD/YYYY
DateTime dt = Convert.ToDateTime(str1);
//Converts the requested date into MM/DD/YYYY and assign it to textbox field
TextBox = String.Format("{0:MM/dd/yyyy}", dt.ToShortDateString());
//include your validation code if required
}
Had a similar issue, and came up with this... HTH!
value = this.GetDate()
if (value.Length >= 6)//ensure that the date is mmddyy
{
int year = 0;
if (int.TryParse(value.Substring(4, 2), out year))
{
int pastMillenium = int.Parse(DateTime.Now.ToString("yyyy").Substring(0, 2)) - 1;
if (year > int.Parse(DateTime.Now.ToString("yy")))//if its a future year it's most likely 19XX
{
value = string.Format("{0}{1}{2}", value.Substring(0, 4), pastMillenium, year.ToString().PadLeft(2, '0'));
}
else
{
value = string.Format("{0}{1}{2}", value.Substring(0, 4), pastMillenium + 1, year.ToString().PadLeft(2, '0'));
}
}
else
{
value = string.Empty;
}
}
else
{
value = string.Empty;
}
My answer will not match your question but for credit cards I just add 2 digits of current year
private int UpconvertTwoDigitYearToFour(int yearTwoOrFour)
{
try
{
if (yearTwoOrFour.ToString().Length <= 2)
{
DateTime yearOnly = DateTime.ParseExact(yearTwoOrFour.ToString("D2"), "yy", null);
return yearOnly.Year;
}
}
catch
{
}
return yearTwoOrFour;
}
If you calculate for a person he will probably not be more than 100 years...
Eg: 751212
var nr = "751212";
var century = DateTime.Now.AddYears(-100).Year.ToString().Substring(0, 2);
var days = (DateTime.Now - DateTime.Parse(century + nr)).Days;
decimal years = days / 365.25m;
if(years>=99)
century = DateTime.Now.Year.ToString().Substring(0, 2);
var fullnr = century+nr;
To change a 2-digit year to 4-digit current or earlier -
year = year + (DateTime.Today.Year - DateTime.Today.Year%100);
if (year > DateTime.Today.Year)
year = year - 100;
My two cents,
Given an age range=[18, 100+], two digits year=90, I can do
current year - twoDigitsYear = 2018 - 90 = 1928, I got 19, 28
hence 19 is the first two digits of year of born, and 28 is the age, which is
year=1990, age=28
But it won't work when age 0 and 100 both included in the range, same to some of the other answers here.
Based on above solutions, here is mine, i used in android while using java
it takes current year in two digit format then checks for if input
year length is equal to 2, if yes then it get current year and from
this year it splits first two digits of century, then it adds this
century with year user input. to make it 4 digit year.
public static int getConvertedYearFromTwoToFourDigits(String year) {
if (year.length() == 2) {
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
String firstTwoDigitsOfCurrentYear = String.valueOf(currentYear).substring(0, 2);
year = firstTwoDigitsOfCurrentYear + year;
}
return Integer.parseInt(year);
}
int fYear = Convert.ToInt32(txtYear.Value.ToString().Substring(2, 2));
My answer will not match your question but for credit cards I just add 2 digits of current year
private int UpconvertTwoDigitYearToFour(int yearTwoOrFour)
{
try
{
if (yearTwoOrFour.ToString().Length <= 2)
{
DateTime yearOnly = DateTime.ParseExact(yearTwoOrFour.ToString("D2"), "yy", null);
return yearOnly.Year;
}
}
catch
{
}
return yearTwoOrFour;
}

Categories