diff --git a/CHANGELOG.md b/CHANGELOG.md
index b4cd109ed..f3912cce5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ All notable changes to `laravel-livewire-tables` will be documented in this file
## UNRELEASED
### New Features
- Add capability to use as a Full Page Component
+- Add DateColumn
### Tweaks
- Internal - modify GitHub workflows to improve caching, but use unique caches per workflow matrix
diff --git a/database/sqlite.database b/database/sqlite.database
index 957f99638..953fb91ee 100644
Binary files a/database/sqlite.database and b/database/sqlite.database differ
diff --git a/docs/columns/other-column-types.md b/docs/columns/other-column-types.md
index c5298c48f..6378dd7f6 100644
--- a/docs/columns/other-column-types.md
+++ b/docs/columns/other-column-types.md
@@ -82,6 +82,31 @@ BooleanColumn::make('Active')
->yesNo()
```
+## Date Columns
+
+Date columns provide an easy way to display dates in a given format, without having to use repetitive format() methods or partial views.
+
+You may pass either a DateTime object, in which you can define an "outputFormat"
+```php
+DateColumn::make('Updated At', 'updated_at')
+ ->outputFormat('Y-m-d H:i:s),
+```
+
+Or you may pass a string, in which case you can define an "inputFormat" in addition to the outputFormat:
+```php
+DateColumn::make('Last Charged', 'last_charged_at')
+ ->inputFormat('Y-m-d H:i:s')
+ ->outputFormat('Y-m-d'),
+```
+
+You may also set an "emptyValue" to use when there is no value from the database:
+```php
+DateColumn::make('Last Charged', 'last_charged_at')
+ ->inputFormat('Y-m-d H:i:s')
+ ->outputFormat('Y-m-d')
+ ->emptyValue('Not Found'),
+```
+
## Image Columns
Image columns provide a way to display images in your table without having to use `format()` or partial views:
diff --git a/resources/views/includes/columns/date.blade.php b/resources/views/includes/columns/date.blade.php
new file mode 100644
index 000000000..096f40658
--- /dev/null
+++ b/resources/views/includes/columns/date.blade.php
@@ -0,0 +1,3 @@
+
+ {{ $value }}
+
diff --git a/src/Views/Columns/DateColumn.php b/src/Views/Columns/DateColumn.php
new file mode 100644
index 000000000..badbbcf67
--- /dev/null
+++ b/src/Views/Columns/DateColumn.php
@@ -0,0 +1,52 @@
+getValue($row);
+ if (! ($dateTime instanceof \DateTime)) {
+ try {
+ // Check if format matches what is expected
+ if (! \Carbon\Carbon::hasFormatWithModifiers($dateTime, $this->getInputFormat())) {
+ throw new \Exception('DateColumn Received Invalid Format');
+ }
+
+ // Create DateTime Object
+ $dateTime = \DateTime::createFromFormat($this->getInputFormat(), $dateTime);
+ } catch (\Exception $exception) {
+ report($exception);
+
+ // Return Null
+ return $this->getEmptyValue();
+ }
+ }
+
+ // Return
+ return $dateTime->format($this->getOutputFormat());
+
+ }
+}
diff --git a/src/Views/Traits/Configuration/DateColumnConfiguration.php b/src/Views/Traits/Configuration/DateColumnConfiguration.php
new file mode 100644
index 000000000..9846fbd9a
--- /dev/null
+++ b/src/Views/Traits/Configuration/DateColumnConfiguration.php
@@ -0,0 +1,36 @@
+outputFormat = $outputFormat;
+
+ return $this;
+ }
+
+ /**
+ * Define the inputFormat to use for the Column
+ */
+ public function inputFormat(string $inputFormat): self
+ {
+ $this->inputFormat = $inputFormat;
+
+ return $this;
+ }
+
+ /**
+ * Define the Empty Value to use for the Column
+ */
+ public function emptyValue(string $emptyValue): self
+ {
+ $this->emptyValue = $emptyValue;
+
+ return $this;
+ }
+}
diff --git a/src/Views/Traits/Helpers/DateColumnHelpers.php b/src/Views/Traits/Helpers/DateColumnHelpers.php
new file mode 100644
index 000000000..1a43ed375
--- /dev/null
+++ b/src/Views/Traits/Helpers/DateColumnHelpers.php
@@ -0,0 +1,30 @@
+outputFormat ?? 'Y-m-d';
+ }
+
+ /**
+ * Retrieve the inputFormat to use for the Column
+ */
+ public function getInputFormat(): ?string
+ {
+ return $this->inputFormat ?? null;
+ }
+
+ /**
+ * Retrieve the Empty Value to use for the Column
+ */
+ public function getEmptyValue(): string
+ {
+ return $this->emptyValue;
+ }
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
index 2623b0438..33cfd5315 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -52,11 +52,11 @@ protected function setUp(): void
]);
Pet::insert([
- ['id' => 1, 'name' => 'Cartman', 'age' => 22, 'species_id' => 1, 'breed_id' => 4],
- ['id' => 2, 'name' => 'Tux', 'age' => 8, 'species_id' => 1, 'breed_id' => 4],
- ['id' => 3, 'name' => 'May', 'age' => 2, 'species_id' => 2, 'breed_id' => 102],
- ['id' => 4, 'name' => 'Ben', 'age' => 5, 'species_id' => 3, 'breed_id' => 200],
- ['id' => 5, 'name' => 'Chico', 'age' => 7, 'species_id' => 3, 'breed_id' => 202],
+ ['id' => 1, 'name' => 'Cartman', 'age' => 22, 'species_id' => 1, 'breed_id' => 4, 'last_visit' => '2023-01-04'],
+ ['id' => 2, 'name' => 'Tux', 'age' => 8, 'species_id' => 1, 'breed_id' => 4, 'last_visit' => '2023-02-04'],
+ ['id' => 3, 'name' => 'May', 'age' => 2, 'species_id' => 2, 'breed_id' => 102, 'last_visit' => null],
+ ['id' => 4, 'name' => 'Ben', 'age' => 5, 'species_id' => 3, 'breed_id' => 200, 'last_visit' => '2023-04-04'],
+ ['id' => 5, 'name' => 'Chico', 'age' => 7, 'species_id' => 3, 'breed_id' => 202, 'last_visit' => '2023-05-04'],
]);
Veterinary::insert([
diff --git a/tests/Views/Columns/DateColumnTest.php b/tests/Views/Columns/DateColumnTest.php
new file mode 100644
index 000000000..c582cc9b6
--- /dev/null
+++ b/tests/Views/Columns/DateColumnTest.php
@@ -0,0 +1,103 @@
+assertSame('Last Visit', $column->getTitle());
+ }
+
+ /** @test */
+ public function can_infer_field_name_from_title_if_no_from(): void
+ {
+ $column = DateColumn::make('My Title');
+
+ $this->assertSame('my_title', $column->getField());
+ }
+
+ /** @test */
+ public function can_set_base_field_from_from(): void
+ {
+ $column = DateColumn::make('Name', 'last_visit');
+
+ $this->assertSame('last_visit', $column->getField());
+ }
+
+ /** @test */
+ public function can_set_relation_field_from_from(): void
+ {
+ $column = DateColumn::make('Name', 'last_visit');
+
+ $this->assertSame('last_visit', $column->getField());
+ }
+
+ /** @test */
+ public function can_get_column_formatted_contents(): void
+ {
+ $column = DateColumn::make('Name', 'last_visit')->inputFormat('Y-m-d')->outputFormat('Y-m-d');
+
+ $rows = $this->basicTable->getRows();
+
+ $this->assertSame($rows->last()->last_visit, $column->getContents($rows->last()));
+ $this->assertSame($rows->last()->last_visit, '2023-05-04');
+ }
+
+ /** @test */
+ public function can_get_column_reformatted_contents(): void
+ {
+ $column = DateColumn::make('Name', 'last_visit')->inputFormat('Y-m-d')->outputFormat('d-m-Y');
+
+ $rows = $this->basicTable->getRows();
+
+ $this->assertSame('04-05-2023', $column->getContents($rows->last()));
+ }
+
+ /** @test */
+ public function can_not_get_column_reformatted_contents_with_bad_values(): void
+ {
+ $column = DateColumn::make('Name', 'last_visit')->inputFormat('d-m-Y')->outputFormat('d-m-Y');
+
+ $firstRow = $this->basicTable->getRows()->first();
+
+ $firstRow->last_visit = '44-12-2023';
+
+ $this->assertSame('', $column->getContents($firstRow));
+
+ $firstRow->last_visit = '04-01-2023';
+
+ $this->assertSame('04-01-2023', $column->getContents($firstRow));
+
+ $this->assertSame('04-01-2023', $column->emptyValue('Unknown')->getContents($firstRow));
+
+ $firstRow->last_visit = '44-12-2023';
+
+ $this->assertSame('Unknown', $column->emptyValue('Unknown')->getContents($firstRow));
+
+ }
+
+ /** @test */
+ public function can_set_column_empty_value(): void
+ {
+ $column = DateColumn::make('Name', 'last_visit')->inputFormat('d-m-Y')->outputFormat('d-m-Y');
+ $this->assertSame('', $column->getEmptyValue());
+
+ $column->emptyValue('Not Found');
+ $this->assertSame('Not Found', $column->getEmptyValue());
+
+ $thirdRow = $this->basicTable->getRows()->slice(3, 1)->first();
+
+ $this->assertSame('Not Found', $column->getContents($thirdRow));
+
+ $column->emptyValue('');
+ $this->assertSame('', $column->getContents($thirdRow));
+
+ }
+}