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
10 changes: 2 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"friendsofphp/php-cs-fixer": "^3.86",
"graham-campbell/analyzer": "^5.0",
"guzzlehttp/guzzle": "^7.7",
"jamesmills/laravel-timezone": "dev-master",
"jenssegers/agent": "^2.6",
"larastan/larastan": "^3.0",
"laravel/fortify": "^1.25",
Expand All @@ -48,7 +47,8 @@
"spatie/laravel-sluggable": "^3.6",
"spatie/pest-plugin-snapshots": "^2.2",
"spatie/regex": "^3.1",
"spatie/yaml-front-matter": "^2.0"
"spatie/yaml-front-matter": "^2.0",
"torann/geoip": "^3.0"
},
"autoload": {
"psr-4": {
Expand All @@ -71,12 +71,6 @@
]
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/peoplelikeushomeexchange/laravel-timezone.git"
}
],
"scripts": {
"format": [
"vendor/bin/php-cs-fixer fix"
Expand Down
68 changes: 2 additions & 66 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/Blog/Components/Kiosk/CreateArticle.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

use ARKEcosystem\Foundation\Blog\Enums\Category;
use ARKEcosystem\Foundation\Blog\Models\Article;
use ARKEcosystem\Foundation\Support\Timezone;
use ARKEcosystem\Foundation\UserInterface\Components\Concerns\HandleToast;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
use JamesMills\LaravelTimezone\Facades\Timezone;
use Livewire\Component;
use Livewire\WithFileUploads;

Expand Down Expand Up @@ -48,7 +48,7 @@ public function save() : void
]);

if ($this->state['published_at'] !== '' && $this->state['published_at'] !== null) {
$this->state['published_at'] = Timezone::convertFromLocal($this->state['published_at']);
$this->state['published_at'] = Timezone::fromLocal($this->state['published_at']);
} else {
$this->state['published_at'] = null;
}
Expand Down
7 changes: 3 additions & 4 deletions src/Blog/Components/Kiosk/UpdateArticle.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use ARKEcosystem\Foundation\Blog\Enums\Category;
use ARKEcosystem\Foundation\Blog\Models\Article;
use ARKEcosystem\Foundation\Blog\Models\User;
use ARKEcosystem\Foundation\Support\Timezone;
use ARKEcosystem\Foundation\UserInterface\Components\Concerns\HandleToast;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Database\Eloquent\Collection;
Expand All @@ -15,7 +16,6 @@
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
use JamesMills\LaravelTimezone\Facades\Timezone;
use Livewire\Component;
use Livewire\WithFileUploads;

Expand Down Expand Up @@ -53,8 +53,7 @@ public function mount(Article $article) : void
$this->users = User::orderBy('name')->withTrashed()->get();

if ($article->published_at !== null) {
// @phpstan-ignore-next-line
$this->state['published_at'] = Timezone::convertToLocal($article->published_at, 'Y-m-d\TH:i');
$this->state['published_at'] = Timezone::toLocal($article->published_at)->format('Y-m-d\TH:i');
}
}

Expand All @@ -70,7 +69,7 @@ public function save() : void
]);

if ($this->state['published_at'] !== '' && $this->state['published_at'] !== null) {
$this->state['published_at'] = Timezone::convertFromLocal($this->state['published_at']);
$this->state['published_at'] = Timezone::fromLocal($this->state['published_at']);
} else {
$this->state['published_at'] = null;
}
Expand Down
21 changes: 21 additions & 0 deletions src/Blog/Listeners/UpdateUserTimezone.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace ARKEcosystem\Foundation\Blog\Listeners;

use Illuminate\Auth\Events\Login;

final class UpdateUserTimezone
{
public function handle(Login $event): void
{
$timezone = geoip()->getLocation(request()->ip())->timezone;

if ($timezone === null || $event->user->timezone === $timezone) {
return;
}

$event->user->forceFill(['timezone' => $timezone])->save();
}
}
10 changes: 10 additions & 0 deletions src/Providers/BlogServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
use ARKEcosystem\Foundation\Blog\Controllers\Contracts\ArticleController as ArticleControllerContract;
use ARKEcosystem\Foundation\Blog\Controllers\KioskController;
use ARKEcosystem\Foundation\Blog\Controllers\UserController;
use ARKEcosystem\Foundation\Blog\Listeners\UpdateUserTimezone;
use Illuminate\Auth\Events\Login;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Illuminate\View\Compilers\BladeCompiler;
Expand All @@ -35,6 +38,8 @@ public function boot(): void
$this->registerLivewireComponents();

$this->registerRoutes();

$this->registerListeners();
}

protected function registerContracts(): void
Expand Down Expand Up @@ -105,4 +110,9 @@ private function registerRoutes(): void
});
});
}

private function registerListeners(): void
{
Event::listen(Login::class, UpdateUserTimezone::class);
}
}
17 changes: 17 additions & 0 deletions src/Support/Timezone.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
use Carbon\Carbon;
use Carbon\CarbonTimeZone;
use DateTimeZone;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;

final class Timezone
{
Expand All @@ -28,4 +30,19 @@ public static function formattedList(): array
];
})->sortBy('offset')->toArray());
}

public static function toLocal(Carbon $date): Carbon
{
return $date->copy()->setTimezone(static::current());
}

public static function fromLocal(string $date): Carbon
{
return Carbon::parse($date, static::current())->setTimezone('UTC');
}

private static function current(): string
{
return Auth::user()?->timezone ?? Config::get('app.timezone');
}
}
2 changes: 0 additions & 2 deletions tests/Blog/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use ARKEcosystem\Foundation\Providers\UserInterfaceServiceProvider;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\View;
use JamesMills\LaravelTimezone\LaravelTimezoneServiceProvider;
use Livewire\LivewireServiceProvider;
use Spatie\MediaLibrary\MediaLibraryServiceProvider;
use Spatie\ResponseCache\Middlewares\DoNotCacheResponse;
Expand Down Expand Up @@ -57,7 +56,6 @@ protected function getPackageProviders($app)
LivewireServiceProvider::class,
MediaLibraryServiceProvider::class,
ResponseCacheServiceProvider::class,
LaravelTimezoneServiceProvider::class,
];
}

Expand Down
43 changes: 43 additions & 0 deletions tests/Blog/Unit/Listeners/UpdateUserTimezoneTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

use ARKEcosystem\Foundation\Blog\Listeners\UpdateUserTimezone;
use ARKEcosystem\Foundation\Blog\Models\User;
use Illuminate\Auth\Events\Login;
use Illuminate\Support\Facades\Event;
use Torann\GeoIP\GeoIP;
use Torann\GeoIP\Location;

function fakeGeoIpTimezone(?string $timezone): void
{
$geoip = Mockery::mock(GeoIP::class);
$geoip->shouldReceive('getLocation')->andReturn(new Location(['timezone' => $timezone]));

app()->instance('geoip', $geoip);
}

it('is registered for the login event', function () {
expect(Event::hasListeners(Login::class))->toBeTrue();
expect(Event::getRawListeners()[Login::class])->toContain(UpdateUserTimezone::class);
});

it('updates the user timezone on login', function () {
fakeGeoIpTimezone('Europe/Ljubljana');

$user = User::factory()->create(['timezone' => 'UTC']);

(new UpdateUserTimezone())->handle(new Login('web', $user, false));

expect($user->fresh()->timezone)->toBe('Europe/Ljubljana');
});

it('does not touch the user when the timezone could not be resolved', function () {
fakeGeoIpTimezone(null);

$user = User::factory()->create(['timezone' => 'America/Mexico_City']);

(new UpdateUserTimezone())->handle(new Login('web', $user, false));

expect($user->fresh()->timezone)->toBe('America/Mexico_City');
});
31 changes: 31 additions & 0 deletions tests/Blog/Unit/TimezoneTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

use ARKEcosystem\Foundation\Blog\Models\User;
use ARKEcosystem\Foundation\Support\Timezone;
use Carbon\Carbon;

it('converts a date to the user timezone', function () {
$this->actingAs(User::factory()->create(['timezone' => 'America/Mexico_City']));

$date = Carbon::parse('2026-01-01 12:00:00', 'UTC');

expect(Timezone::toLocal($date)->format('Y-m-d H:i'))->toBe('2026-01-01 06:00');
expect($date->timezoneName)->toBe('UTC');
});

it('converts a local date to utc', function () {
$this->actingAs(User::factory()->create(['timezone' => 'America/Mexico_City']));

$date = Timezone::fromLocal('2026-01-01T06:00');

expect($date->timezoneName)->toBe('UTC');
expect($date->format('Y-m-d H:i'))->toBe('2026-01-01 12:00');
});

it('falls back to the app timezone for guests', function () {
config(['app.timezone' => 'UTC']);

expect(Timezone::fromLocal('2026-01-01T06:00')->format('Y-m-d H:i'))->toBe('2026-01-01 06:00');
});
Loading