Skip to content

Commit cd329a2

Browse files
committed
Added begin of casting
1 parent 04d26bb commit cd329a2

File tree

5 files changed

+223
-7
lines changed

5 files changed

+223
-7
lines changed

README.md

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Laravel Timezones
22

3-
## Usage
3+
## Getting started
44

55
All database dates should be stored using the `app.timezone` config setting. We highly suggest keeping it as `UTC` since it's a global standard for dates storage.
66

@@ -12,4 +12,46 @@ Useful when the app's timezone should be set by ther user's settings.
1212

1313
### 2. Using a Service Provider
1414

15-
Useful when the app's timezone should be set by the application itself.
15+
Useful when the app's timezone should be set by the application itself.
16+
17+
## Usage
18+
19+
Once everything's setup, the easiest way to manipulate dates configured with the app's current timezone is to use the `TimezonedDatetime` or `ImmutableTimezonedDatetime` cast types on your models:
20+
21+
```php
22+
/**
23+
* The attributes that should be cast.
24+
*
25+
* @var array
26+
*/
27+
protected $casts = [
28+
'published_at' => \Whitecube\LaravelTimezones\Casts\TimezonedDatetime::class,
29+
'birthday' => \Whitecube\LaravelTimezones\Casts\ImmutableTimezonedDatetime::class . ':Y-m-d',
30+
];
31+
```
32+
33+
In other scenarios, use the `Timezone`'s static methods directly for conversion:
34+
35+
```php
36+
use Carbon\Carbon;
37+
use Whitecube\LaravelTimezones\Timezone;
38+
39+
// Get the current date configured with the current timezone:
40+
$now = Timezone::now();
41+
42+
// Create a date using the current timezone:
43+
$date = Timezone::date('2023-01-01 00:00:00');
44+
// Alternatively, set the timezone manually on a Carbon instance:
45+
$date = new Carbon('2023-01-01 00:00:00', Timezone::current());
46+
47+
48+
// Convert a date to the current timezone:
49+
$date = Timezone::date(new Carbon('2023-01-01 00:00:00', 'UTC'));
50+
// Alternatively, set the application timezone yourself:
51+
$date = (new Carbon('2023-01-01 00:00:00', 'UTC'))->setTimezone(Timezone::current());
52+
53+
// Convert a date to the storage timezone:
54+
$date = Timezone::store(new Carbon('2023-01-01 00:00:00', 'Europe/Brussels'));
55+
// Alternatively, set the storage timezone yourself:
56+
$date = (new Carbon('2023-01-01 00:00:00', 'Europe/Brussels'))->setTimezone(Timezone::storage());
57+
```
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Whitecube\LaravelTimezones\Casts;
4+
5+
class ImmutableTimezonedDatetime extends TimezonedDatetime
6+
{
7+
/**
8+
* Cast the given value.
9+
*
10+
* @param \Illuminate\Database\Eloquent\Model $model
11+
* @param string $key
12+
* @param mixed $value
13+
* @param array $attributes
14+
* @return array
15+
*/
16+
public function get($model, $key, $value, $attributes)
17+
{
18+
return parent::get($model, $key, $value, $attributes)->toImmutable();
19+
}
20+
}

src/Casts/TimezonedDatetime.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Whitecube\LaravelTimezones\Casts;
4+
5+
use Whitecube\LaravelTimezones\Timezone;
6+
use Illuminate\Support\Facades\Date;
7+
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
8+
9+
class TimezonedDatetime implements CastsAttributes
10+
{
11+
/**
12+
* A developer-specific format to use for string parsing
13+
*
14+
* @var null|string
15+
*/
16+
protected ?string $format;
17+
18+
/**
19+
* Create a new casting instance.
20+
*
21+
* @param null|string $format
22+
* @return void
23+
*/
24+
public function __construct(?string $format = null)
25+
{
26+
$this->format = $format;
27+
}
28+
29+
/**
30+
* Cast the given value.
31+
*
32+
* @param \Illuminate\Database\Eloquent\Model $model
33+
* @param string $key
34+
* @param mixed $value
35+
* @param array $attributes
36+
* @return \Carbon\CarbonInterface
37+
*/
38+
public function get($model, $key, $value, $attributes)
39+
{
40+
return Timezone::date($value, fn($raw, $tz) => $this->asDateTime($raw, $tz, $model));
41+
}
42+
43+
/**
44+
* Prepare the given value for storage.
45+
*
46+
* @param \Illuminate\Database\Eloquent\Model $model
47+
* @param string $key
48+
* @param mixed $value
49+
* @param array $attributes
50+
* @return string
51+
*/
52+
public function set($model, $key, $value, $attributes)
53+
{
54+
return Timezone::store($value, fn($raw, $tz) => $this->asDateTime($raw, $tz, $model))
55+
->format($this->format ?? $model->getDateFormat());
56+
}
57+
58+
/**
59+
* Create a new date value from raw material
60+
*
61+
* @param mixed $value
62+
* @param Carbon\CarbonTimeZone $timezone
63+
* @param \Illuminate\Database\Eloquent\Model $model
64+
* @return \Carbon\CarbonInterface
65+
*/
66+
public function asDateTime($value, $timezone, $model)
67+
{
68+
return Date::createFromFormat(
69+
$this->format ?? $model->getDateFormat(),
70+
$value,
71+
$timezone,
72+
);
73+
}
74+
}

src/Timezone.php

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Carbon\CarbonTimeZone;
66
use Carbon\CarbonInterface;
7+
use Illuminate\Support\Facades\Date;
78

89
class Timezone
910
{
@@ -20,6 +21,17 @@ class Timezone
2021
*/
2122
protected CarbonTimeZone $storage;
2223

24+
public function __construct(string $default)
25+
{
26+
$this->setStorage($default);
27+
$this->setCurrent($default);
28+
}
29+
30+
public static function instance(): static
31+
{
32+
return app()->make(self::class);
33+
}
34+
2335
public static function set($timezone = null)
2436
{
2537
static::instance()->setCurrent($timezone);
@@ -35,15 +47,27 @@ public static function storage(): CarbonTimeZone
3547
return static::instance()->getStorage();
3648
}
3749

38-
public function instance(): static
50+
public static function now(): CarbonInterface
3951
{
40-
return app()->make(self::class);
52+
return static::instance()->convertToCurrent(now());
4153
}
4254

43-
public function __construct(string $default)
55+
public static function date($value, callable $maker = null): CarbonInterface
4456
{
45-
$this->setStorage($default);
46-
$this->setCurrent($default);
57+
$instance = static::instance();
58+
59+
return $instance->convertToCurrent(
60+
$instance->makeDateWithStorage($value, $maker)
61+
);
62+
}
63+
64+
public static function store($value, callable $maker = null): CarbonInterface
65+
{
66+
$instance = static::instance();
67+
68+
return $instance->convertToStorage(
69+
$instance->makeDateWithCurrent($value, $maker)
70+
);
4771
}
4872

4973
public function setCurrent($timezone)
@@ -76,6 +100,27 @@ public function convertToStorage(CarbonInterface $date): CarbonInterface
76100
return $date->copy()->setTimezone($this->getStorage());
77101
}
78102

103+
public function makeDateWithCurrent($value, callable $maker = null): CarbonInterface
104+
{
105+
return is_a($value, CarbonInterface::class)
106+
? $this->convertToCurrent($value)
107+
: $this->makeDate($value, $this->getCurrent(), $maker);
108+
}
109+
110+
public function makeDateWithStorage($value, callable $maker = null): CarbonInterface
111+
{
112+
return is_a($value, CarbonInterface::class)
113+
? $this->convertToStorage($value)
114+
: $this->makeDate($value, $this->getStorage(), $maker);
115+
}
116+
117+
protected function makeDate($value, CarbonTimeZone $timezone, callable $maker = null): CarbonInterface
118+
{
119+
return ($maker)
120+
? call_user_func($maker, $value, $timezone)
121+
: Date::create($value, $timezone);
122+
}
123+
79124
protected function makeTimezone($value): CarbonTimeZone
80125
{
81126
if(! is_a($value, CarbonTimeZone::class)) {

tests/TimezoneSingletonTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
<?php
22

33
use Carbon\Carbon;
4+
use Carbon\CarbonImmutable;
45
use Carbon\CarbonTimeZone;
6+
use Carbon\CarbonInterface;
57
use Whitecube\LaravelTimezones\Timezone;
68

79
it('can create a default Timezone instance and access its current & storage settings', function() {
@@ -32,3 +34,36 @@
3234
expect($instance->convertToCurrent($date)->getTimezone()->getName() ?? null)->toBe('Europe/Brussels');
3335
expect($date->getTimezone()->getName() ?? null)->toBe(date_default_timezone_get());
3436
});
37+
38+
it('can create date with current timezone', function() {
39+
$instance = new Timezone('UTC');
40+
$instance->setCurrent('Europe/Brussels');
41+
42+
$string = $instance->makeDateWithCurrent('1993-03-16 03:00:00');
43+
$date = $instance->makeDateWithCurrent(new Carbon('1993-03-16 03:00:00', 'UTC'));
44+
$custom = $instance->makeDateWithCurrent('1993-03-16 03:00:00', fn($value, $tz) => new CarbonImmutable($value, $tz));
45+
46+
expect($string)->toBeInstanceOf(CarbonInterface::class);
47+
expect($string->getTimezone()->getName() ?? null)->toBe('Europe/Brussels');
48+
expect($date)->toBeInstanceOf(CarbonInterface::class);
49+
expect($date->getTimezone()->getName() ?? null)->toBe('Europe/Brussels');
50+
expect($custom)->toBeInstanceOf(CarbonImmutable::class);
51+
expect($custom->getTimezone()->getName() ?? null)->toBe('Europe/Brussels');
52+
});
53+
54+
it('can create date with storage timezone', function() {
55+
$instance = new Timezone('UTC');
56+
$instance->setCurrent('Europe/Brussels');
57+
58+
$string = $instance->makeDateWithStorage('1993-03-16 03:00:00');
59+
$date = $instance->makeDateWithStorage(new Carbon('1993-03-16 03:00:00', 'Europe/Brussels'));
60+
$custom = $instance->makeDateWithStorage('1993-03-16 03:00:00', fn($value, $tz) => new CarbonImmutable($value, $tz));
61+
62+
expect($string)->toBeInstanceOf(CarbonInterface::class);
63+
expect($string->getTimezone()->getName() ?? null)->toBe('UTC');
64+
expect($date)->toBeInstanceOf(CarbonInterface::class);
65+
expect($date->getTimezone()->getName() ?? null)->toBe('UTC');
66+
expect($custom)->toBeInstanceOf(CarbonImmutable::class);
67+
expect($custom->getTimezone()->getName() ?? null)->toBe('UTC');
68+
});
69+

0 commit comments

Comments
 (0)