Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ composer require brunoconte3/dev-utils
Or add to your `composer.json`:

```json
"brunoconte3/dev-utils": "2.11.0"
"brunoconte3/dev-utils": "2.12.0"
```

## Data Validation Example
Expand Down Expand Up @@ -138,6 +138,8 @@ maxWidth and requiredFile, you can set the minimum and maximum size (bytes) of t
- companyIdentification: `Validates if the CNPJ is valid, passing CNPJ with or without mask`
- dateAmerican: `Validates if the American date is valid`
- dateBrazil: `Validates if the Brazilian date is valid`
- dateIso8601: `Validates dates in ISO 8601 format, e.g.: 2025-11-20T10:30:00Z`
- dateUTCWithoutTimezone: `Validates dates in UTC format without the letter Z, e.g.: 2025-11-20T10:30:00`
- dateNotFuture: `Validates if the date not greater than date current (accepts Brazilian or American format)`
- ddd: `Validates ddd informed in YYY or YY format, by UF or in general` `Ex: ddd:pr, ddd do Paraná/Brazil, or just ddd`
- email: `Check if it's a valid email`
Expand Down Expand Up @@ -444,6 +446,8 @@ use DevUtils\ValidateDate;
ValidateDate::validateDateBrazil('29/04/2021'); //Return boolean [Format dd/mm/yyyy]
ValidateDate::validateDateAmerican('2021-04-29'); //Return boolean [Format yyyy-mm-dd]
ValidateDate::validateTimeStamp('2021-04-29 11:17:12'); //Return boolean [Format yyyy-mm-dd hh:mm:ss]
ValidateDate::validateDateIso8601('2025-11-20T10:30:00Z'); //Return boolean [Format ISO 8601: 2025-11-20T10:30:00Z]
ValidateDate::validateDateUTCWithoutTimezone('2025-11-20T10:30:00'); //Return boolean [Format UTC without Z: 2025-11-20T10:30:00]

use DevUtils\ValidateHour;
ValidateHour::validateHour('08:50'); //Return boolean [Format YY:YY]
Expand Down
2 changes: 0 additions & 2 deletions src/Arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

namespace DevUtils;

use DevUtils\Format;

class Arrays
{
public static function searchKey(array $array, string $key): ?int
Expand Down
1 change: 0 additions & 1 deletion src/DependencyInjection/FormatAux.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace DevUtils\DependencyInjection;

use DevUtils\Format;
use InvalidArgumentException;

abstract class FormatAux
Expand Down
5 changes: 4 additions & 1 deletion src/DependencyInjection/TraitRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ private function methodsNoRuleValue(): array
'validateIntegerTyped', 'validateNumeric', 'validateNumMonth', 'validateFileName',
'validateFileUploadMandatory', 'validateDateBrazil', 'validateDateAmerican', 'validateHour',
'validateTimestamp', 'validateWeekend', 'validateArray', 'validateFieldMandatory', 'validateBoolean',
'validateFloating', 'validateJson', 'validateDateNotFuture',
'validateFloating', 'validateJson', 'validateDateNotFuture', 'validateDateIso8601',
'validateDateUTCWithoutTimezone',
];
}

Expand Down Expand Up @@ -44,6 +45,8 @@ private static function functionsValidationAtoL(): array
'ip' => 'validateIp',
'json' => 'validateJson',
'lower' => 'validateLower',
'dateIso8601' => 'validateDateIso8601',
'dateUTCWithoutTimezone' => 'validateDateUTCWithoutTimezone',
];
}

Expand Down
26 changes: 21 additions & 5 deletions src/DependencyInjection/TraitRuleDate.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

namespace DevUtils\DependencyInjection;

use DevUtils\{
Format,
ValidateDate,
ValidateHour,
};
use DevUtils\Format;
use DevUtils\ValidateDate;
use DevUtils\ValidateHour;

trait TraitRuleDate
{
Expand Down Expand Up @@ -67,4 +65,22 @@ protected function validateDateNotFuture(string $field = '', string $value = '',
"O campo $field não pode ser uma data maior que a atual";
}
}

protected function validateDateIso8601(string $field = '', string $value = '', ?string $message = ''): void
{
if (!ValidateDate::validateDateIso8601($value)) {
$this->errors[$field] = !empty($message) ? $message : "O campo $field não é uma data válida!";
}
}


protected function validateDateUTCWithoutTimezone(
string $field = '',
string $value = '',
?string $message = ''
): void {
if (!ValidateDate::validateDateUTCWithoutTimezone($value)) {
$this->errors[$field] = !empty($message) ? $message : "O campo $field não é uma data válida!";
}
}
}
12 changes: 5 additions & 7 deletions src/DependencyInjection/TraitRuleString.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

namespace DevUtils\DependencyInjection;

use DevUtils\{
Format,
ValidateCnpj,
ValidateCpf,
ValidatePhone,
ValidateString,
};
use DevUtils\DependencyInjection\data\DataDdds;
use DevUtils\Format;
use DevUtils\resource\Common;
use DevUtils\ValidateCnpj;
use DevUtils\ValidateCpf;
use DevUtils\ValidatePhone;
use DevUtils\ValidateString;

trait TraitRuleString
{
Expand Down
11 changes: 2 additions & 9 deletions src/Format.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,8 @@

namespace DevUtils;

use DevUtils\{
ValidateDate,
ValidatePhone,
ValidateFile,
};
use DevUtils\DependencyInjection\{
FormatAux,
StrfTime,
};
use DevUtils\DependencyInjection\FormatAux;
use DevUtils\DependencyInjection\StrfTime;
use Exception;
use InvalidArgumentException;

Expand Down
81 changes: 81 additions & 0 deletions src/ValidateDate.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,85 @@ public static function validateDateNotFuture(string $dateAmerican): bool
}
return true;
}

public static function validateDateUTCWithoutTimezone(string $date): bool
{
$format = 'Y-m-d\TH:i:s';
$d = DateTime::createFromFormat($format, $date);
return $d && $d->format($format) === $date;
}

public static function validateDateIso8601(string $input): bool
{
if (empty($input)) {
return false;
}
return self::isCalendarDateTime($input) || self::isDuration($input) || self::isWeekDate($input)
|| self::isOrdinalDate($input) || self::isInterval($input);
}

private static function isCalendarDateTime(string $input): bool
{
$pattern = '/^\d{4}((-?\d{2})(-?\d{2})?)?(T\d{2}(:?\d{2}(:?\d{2}(\.\d+)?)?)?(Z|[+-]\d{2}(:?\d{2})?)?)?$/';
if (!preg_match($pattern, $input)) {
return false;
}
try {
$date = new \DateTime($input);
if (str_contains($input, '-')) {
$parts = explode('T', $input)[0];
if (count(explode('-', $parts)) === 3 && $date->format('Y-m-d') !== $parts) {
return false;
}
}
return true;
} catch (\Exception) {
return false;
}
}

private static function isWeekDate(string $input): bool
{
$pattern = '/^(\d{4})-?W(0[1-9]|[1-4][0-9]|5[0-3])(-?([1-7]))?$/';
if (!preg_match($pattern, $input, $matches)) {
return false;
}
$year = (int)$matches[1];
$week = (int)$matches[2];
if ($week === 53) {
$date = new \DateTime();
$date->setISODate($year, 53);
return $date->format('W') === '53';
}
return true;
}

private static function isOrdinalDate(string $input): bool
{
if (!preg_match('/^(\d{4})-?(\d{3})$/', $input, $matches)) {
return false;
}
$year = (int)$matches[1];
$dayOfYear = (int)$matches[2];
$isLeap = ($year % 4 === 0 && ($year % 100 !== 0 || $year % 400 === 0));
return $dayOfYear >= 1 && $dayOfYear <= ($isLeap ? 366 : 365);
}

private static function isDuration(string $input): bool
{
$pattern = '/^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/';
return (bool) preg_match($pattern, $input);
}

private static function isInterval(string $input): bool
{
if (!str_contains($input, '/')) {
return false;
}
$parts = explode('/', $input);
if (count($parts) !== 2) {
return false;
}
return (self::validateDateIso8601($parts[0]) && self::validateDateIso8601($parts[1]));
}
}
2 changes: 0 additions & 2 deletions src/ValidatePhone.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace DevUtils;

use DevUtils\Format;

class ValidatePhone
{
public static function validate(string $phone): bool
Expand Down
26 changes: 26 additions & 0 deletions tests/UnitRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -759,4 +759,30 @@ public function testMinHeight(): void
self::assertFalse(extension_loaded('gd'));
}
}

public function testDateUTCWithoutTimezone(): void
{
$array = [
'dateError' => '2024-13-01T12:00:00',
'dateValid' => '2024-12-01T12:00:00',
];
$rules = [
'dateError' => 'dateUTCWithoutTimezone',
'dateValid' => 'dateUTCWithoutTimezone',
];
self::assertErrorCount(1, $array, $rules);
}

public function testDateIso8601(): void
{
$array = [
'dateError' => '2024-13-01T12:00:00+00:00',
'dateValid' => '2024-12-01T12:00:00+00:00',
];
$rules = [
'dateError' => 'dateIso8601',
'dateValid' => 'dateIso8601',
];
self::assertErrorCount(1, $array, $rules);
}
}
69 changes: 69 additions & 0 deletions tests/UnitValidateDateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,73 @@ public function testValidateDateNotFuture(): void
self::assertEquals(true, ValidateDate::validateDateNotFuture(Format::dateAmerican('28/12/2022')));
self::assertEquals(false, ValidateDate::validateDateNotFuture($newDateFuture));
}

public function testValidateDateUTCWithoutTimezone(): void
{
self::assertTrue(ValidateDate::validateDateUTCWithoutTimezone('2025-11-20T10:30:00'));
self::assertTrue(ValidateDate::validateDateUTCWithoutTimezone('1999-01-01T00:00:00'));
self::assertFalse(ValidateDate::validateDateUTCWithoutTimezone('2025-11-20T10:30'));
self::assertFalse(ValidateDate::validateDateUTCWithoutTimezone('2025-11-20 10:30:00'));
self::assertFalse(ValidateDate::validateDateUTCWithoutTimezone('2025-11-20T10:30:00Z'));
self::assertFalse(ValidateDate::validateDateUTCWithoutTimezone('2025-11-20T10:30:00+00:00'));
}

private static function biuldDataTestDateIso8601Z(): array
{
return [
'2025-11-20T10:30:00Z',
'2025-11-20T10:30:00+00:00',
'2025-11-20T10:30:00-03:00',
'2025-11-20T10:30:00+03:30',
'2025-11-20T10:30:00.123Z',
'2025-11-20T10:30:00.123+00:00',
'2025-11-20T10:30:00.123456789Z',
'2025-11-20T10:30:00.1234567Z',
'20251120T103000Z',
'2025-11-20T10:30Z',
];
}

private static function biuldDataDateIsoTest(): array
{
return [
'2025',
'2025-11',
'2025-11-20',
'20251120',
'2025-11-20T10:30',
'2025-11-20T10:30+03:00',
'2025-11-20T10:30:00',
'2025-11-20T10:30:00+00:00',
'2025-11-20T10:30:00-03:00',
'2025-11-20T10:30:00+03:30',
'2025-11-20T10:30:00.123',
'2025-11-20T10:30:00.123+00:00',
'20251120T103000',
'20251120T103000+00:00',
'2025-W48',
'2025-W48-1',
'2025-324',
...self::biuldDataTestDateIso8601Z(),
];
}

public function testValidateDateIso8601AllFormats(): void
{
$valid = self::biuldDataDateIsoTest();
foreach ($valid as $v) {
self::assertTrue(ValidateDate::validateDateIso8601($v), "Falhou para: $v");
}
$invalid = [
'2025-13-40',
'2025-11-20T25:61:00Z',
'2025-11-20T10:30:00+99:99',
'2025-11-20 10:30:00Z',
'2025-W99',
'2025-400',
];
foreach ($invalid as $v) {
self::assertFalse(ValidateDate::validateDateIso8601($v), "Aceitou inválido: $v");
}
}
}
Loading