-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #913 from DFE-Digital/bugs/fix-bulk-edit-date-vali…
…dation Fix - Bulk edit date validation logic
- Loading branch information
Showing
2 changed files
with
69 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 57 additions & 36 deletions
93
...s/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/DateValidationCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,75 @@ | ||
using System.Globalization; | ||
|
||
namespace Dfe.ManageFreeSchoolProjects.API.UseCases.BulkEdit.Validations | ||
namespace Dfe.ManageFreeSchoolProjects.API.UseCases.BulkEdit.Validations | ||
{ | ||
public class DateValidationCommand : IValidationCommand<BulkEditDto> | ||
{ | ||
public ValidationResult Execute(ValidationCommandParameters<BulkEditDto> parameters) | ||
{ | ||
var actualOpeningDateString = parameters.Value; | ||
var dateParts = CleanAndSplitDate(parameters.Value); | ||
|
||
var dateParts = actualOpeningDateString.Split('/'); | ||
if (dateParts.Length != 3) | ||
return new ValidationResult | ||
{ IsValid = false, ErrorMessage = "Enter a valid date. For example, 27/03/2021" }; | ||
if (!IsValidDateFormat(dateParts)) | ||
return CreateValidationResult(false, "Enter a valid date. For example, 27/03/2021"); | ||
|
||
var dateObject = new { Day = dateParts[0], Month = dateParts[1], Year = dateParts[2] }; | ||
|
||
var missingParts = new List<string>(); | ||
var (day, month, year) = (dateParts[0], dateParts[1], dateParts[2]); | ||
|
||
if (string.IsNullOrEmpty(dateObject.Day)) missingParts.Add("day"); | ||
if (string.IsNullOrEmpty(dateObject.Month)) missingParts.Add("month"); | ||
if (string.IsNullOrEmpty(dateObject.Year)) missingParts.Add("year"); | ||
var missingPartsMessage = CheckForMissingDateParts(day, month, year); | ||
if (missingPartsMessage != null) | ||
return CreateValidationResult(false, missingPartsMessage); | ||
|
||
if (missingParts.Count == 3) | ||
return new ValidationResult | ||
{ IsValid = false, ErrorMessage = "Date must include a day, month, and year" }; | ||
if (!IsValidMonth(month, out var monthNumber)) | ||
return CreateValidationResult(false, "Month must be between 1 and 12"); | ||
|
||
if (missingParts.Count > 0) | ||
return new ValidationResult | ||
{ IsValid = false, ErrorMessage = $"Date must include a {string.Join(" and ", missingParts)}" }; | ||
if (!IsValidYear(year, out var yearNumber)) | ||
return CreateValidationResult(false, "Year must be between 2000 and 2050"); | ||
|
||
if (!int.TryParse(dateObject.Day, out var day) || day < 1 || day > 31) | ||
return new ValidationResult | ||
{ IsValid = false, ErrorMessage = "Day must be a valid number between 1 and 31" }; | ||
if (!IsValidDay(day, yearNumber, monthNumber, out _)) | ||
return CreateValidationResult(false, | ||
$"Day must be between 1 and {DateTime.DaysInMonth(yearNumber, monthNumber)}"); | ||
|
||
if (!int.TryParse(dateObject.Month, out var month) || month < 1 || month > 12) | ||
return new ValidationResult | ||
{ IsValid = false, ErrorMessage = "Month must be a valid number between 1 and 12" }; | ||
return CreateValidationResult(true, null); | ||
} | ||
|
||
if (!int.TryParse(dateObject.Year, out var year) || year < 2000 || year > 2050) | ||
return new ValidationResult { IsValid = false, ErrorMessage = "Year must be between 2000 and 2050" }; | ||
private static string[] CleanAndSplitDate(string date) | ||
{ | ||
if (!date.Contains('/')) | ||
return [date]; | ||
|
||
var daysInCurrentMonth = DateTime.DaysInMonth(year, month); | ||
if (day > daysInCurrentMonth) | ||
return new ValidationResult | ||
{ | ||
IsValid = false, | ||
ErrorMessage = $"Day must be between 1 and {daysInCurrentMonth} for the given month." | ||
}; | ||
var dateParts = date.Split('/'); | ||
if (dateParts[2].EndsWith("00:00:00")) | ||
dateParts[2] = dateParts[2][..^" 00:00:00".Length]; | ||
|
||
return dateParts; | ||
} | ||
|
||
return new ValidationResult { IsValid = true }; | ||
private static bool IsValidDateFormat(string[] dateParts) => dateParts.Length == 3; | ||
|
||
private static string CheckForMissingDateParts(string day, string month, string year) | ||
{ | ||
var missingParts = new List<string>(); | ||
if (string.IsNullOrEmpty(day)) missingParts.Add("day"); | ||
if (string.IsNullOrEmpty(month)) missingParts.Add("month"); | ||
if (string.IsNullOrEmpty(year)) missingParts.Add("year"); | ||
|
||
if (missingParts.Count == 3) | ||
return "Date must include a day, month, and year"; | ||
if (missingParts.Count > 0) | ||
return $"Date must include a {string.Join(" and ", missingParts)}"; | ||
return null; | ||
} | ||
|
||
private static bool IsValidMonth(string month, out int monthNumber) => | ||
int.TryParse(month, out monthNumber) && monthNumber is >= 1 and <= 12; | ||
|
||
private static bool IsValidYear(string year, out int yearNumber) => | ||
int.TryParse(year, out yearNumber) && yearNumber is >= 2000 and <= 2050; | ||
|
||
private static bool IsValidDay(string day, int year, int month, out int dayNumber) | ||
{ | ||
var isValid = int.TryParse(day, out dayNumber) && dayNumber >= 1 && | ||
dayNumber <= DateTime.DaysInMonth(year, month); | ||
return isValid; | ||
} | ||
|
||
private static ValidationResult CreateValidationResult(bool isValid, string message) => | ||
new() { IsValid = isValid, ErrorMessage = message }; | ||
} | ||
} |