Reputation: 673
I am writing code to check whether the credit card has expired or not.
Here is what I have
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yyyy"
let enteredDate = dateFormatter.date(from: expiryDate.text!) /* line 3 - set to first day of month */
let now = Date()
if (enteredDate! < now) {
//expired
// does not work if current month and year
// is the same as the expiration date,
// because expiration day is set to the first day of the month on line 3
} else {
// valid
print("valid - now: \(now) entered: \(enteredDate)")
}
Any ideas on how I can change the initialized date to be the last day of the month instead of the first day?
Upvotes: 0
Views: 6073
Reputation: 11
Here is the full answer for Expiry date calculation from textfield formatted as MM/YY. Use this in textfieldshouldchangecharactersinrange method
var cleanString = string.replacingOccurrences(of: "/", with: "")
if cleanString.rangeOfCharacter(from: unsupportedCharacterSet) != nil {
return ""
}
let dateString: String
if cleanString.count == 0 {
return string
} else if cleanString.count > 4 {
// trim the string down to 4
let reqIndex = cleanString.index(cleanString.startIndex, offsetBy: 4)
cleanString = String(cleanString[..<reqIndex])
}
if cleanString.hasPrefix("0") == false && cleanString.hasPrefix("1") == false {
dateString = "0" + cleanString
} else {
dateString = cleanString
}
let currentYear = Calendar.current.component(.year, from: Date()) % 100 // This will give you current year (i.e. if 2019 then it will be 19)
let currentMonth = Calendar.current.component(.month, from: Date()) // This will give you current month (i.e if June then it will be 6)
var newText = ""
for (index, character) in dateString.enumerated() {
print("index: \(index)")
if index == 1 {
let enterdMonth = Int(dateString.prefix(2)) ?? 0 // get first two digit from entered string as month
print("enterdMonth at 1:\(enterdMonth)")
if (1 ... 12).contains(enterdMonth){
if enterdMonth < 10 {
newText = "0" + "\(enterdMonth)" + "/"
}else {
newText = "\(enterdMonth)" + "/"
}
}else{
}
}else if index == 2 {
if (2 ... 99).contains(Int(String(character))!) { // Entered year should be greater than 2019.
newText.append(character)
}else{
}
}else if index == 3 {
print("index---: \(index)")
let enterdYr = Int(dateString.suffix(2)) ?? 0 // get last two digit from entered string as year
let enterdMonth = Int(dateString.prefix(2)) ?? 0 // get first two digit from entered string as month
print("enterdYr: \(enterdYr)")
print("currentYear: \(currentYear)")
if (2 ... 99).contains(enterdYr) { // Entered year should be greater than 2019
if enterdYr >= currentYear {
if (1 ... 12).contains(enterdMonth) {
if enterdMonth < 10 {
newText = "0" + "\(enterdMonth)" + "/" + "\(enterdYr)"
}else {
newText = "\(enterdMonth)" + "/" + "\(enterdYr)"
}
return newText
}
}
}
}
else {
newText.append(character)
}
}
return newText
}
Upvotes: 0
Reputation: 15758
Instead of comparing the dates, compare month of the dates using compare(_:to:toGranularity:)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yyyy"
if let enteredDate = dateFormatter.date(from: "05/2019") {
let result = Calendar.current.compare(Date(), to: enteredDate, toGranularity: .month)
if result == .orderedSame {
print("valid")
} else if result == .orderedAscending {
print("valid")
} else if result == .orderedDescending {
print("expired")
}
}
Upvotes: 1
Reputation: 413
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yyyy"
let enteredDate = dateFormatter.date(from: expiryDate.text!)
//Also convert the current date in the entered date format and then checks that the date is valid if enteredDate month or year is greater than current date.
let currentDate = dateFormatter.date(from: dateFormatter.string(from: Date()))
if enteredDate.compare(now) != ComparisonResult.orderedAscending {
print("Valid")
} else {
print("Not Valid")
}
As this only compares the month and year so it will resolve your issue of first or last date of month.
Upvotes: 0
Reputation: 318824
enteredDate
will be midnight local time on the first of the month of the expiry date. Since you want that whole month to be valid, add 1 month to that value and then compare Date()
to that updated value.
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yyyy"
let enteredDate = dateFormatter.date(from: expiryDate.text!)!
let endOfMonth = Calendar.current.date(byAdding: .month, value: 1, to: enteredDate)!
let now = Date()
if (endOfMonth < now) {
print("Expired - \(enteredDate) - \(endOfMonth)")
} else {
// valid
print("valid - now: \(now) entered: \(enteredDate)")
}
Please note that I left proper handling of optionals as an exercise for the reader.
Upvotes: 6
Reputation: 1679
rmaddy's answer is perfect. Here is how I thought of using Calendar
to handle the validation. Perhaps, I wrote it in more complex way.
enum ExpiryValidation {
case valid, invalidInput, expired
}
func validateCreditCardExpiry(_ input: String) -> ExpiryValidation {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yyyy"
guard let enteredDate = dateFormatter.date(from: input) else {
return .invalidInput
}
let calendar = Calendar.current
let components = Set([Calendar.Component.month, Calendar.Component.year])
let currentDateComponents = calendar.dateComponents(components, from: Date())
let enteredDateComponents = calendar.dateComponents(components, from: enteredDate)
guard let eMonth = enteredDateComponents.month, let eYear = enteredDateComponents.year, let cMonth = currentDateComponents.month, let cYear = currentDateComponents.year, eMonth >= cMonth, eYear >= cYear else {
return .expired
}
return .valid
}
let invalidInput = validateCreditCardExpiry("hello")
let validInput = validateCreditCardExpiry("09/2020")
let expiredInput = validateCreditCardExpiry("04/2010")
Upvotes: 0
Reputation: 1695
if enteredDate.compare(now) == ComparisonResult.orderedDescending
{
print("valid")
}
else{
print("not valid")
}
Upvotes: -2