Skip to content

Commit

Permalink
[5.x] Carbon 3 (#11348)
Browse files Browse the repository at this point in the history
Co-authored-by: Jason Varga <[email protected]>
  • Loading branch information
duncanmcclean and jasonvarga authored Feb 21, 2025
1 parent d6ab488 commit 1a50ee8
Show file tree
Hide file tree
Showing 30 changed files with 376 additions and 35 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"league/glide": "^2.3",
"maennchen/zipstream-php": "^3.1",
"michelf/php-smartypants": "^1.8.1",
"nesbot/carbon": "^2.62.1",
"nesbot/carbon": "^2.62.1 || ^3.0",
"pixelfear/composer-dist-plugin": "^0.1.4",
"rebing/graphql-laravel": "^9.7",
"rhukster/dom-sanitizer": "^1.0.6",
Expand Down
2 changes: 1 addition & 1 deletion src/Assets/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ public function mimeType()
*/
public function lastModified()
{
return Carbon::createFromTimestamp($this->meta('last_modified'));
return Carbon::createFromTimestamp($this->meta('last_modified'), config('app.timezone'));
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/Auth/File/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public function lastModified()
? File::disk('users')->lastModified($path)
: time();

return Carbon::createFromTimestamp($timestamp);
return Carbon::createFromTimestamp($timestamp, config('app.timezone'));
}

/**
Expand Down Expand Up @@ -298,7 +298,7 @@ public function lastLogin()
{
$last_login = $this->getMeta('last_login');

return $last_login ? Carbon::createFromTimestamp($last_login) : $last_login;
return $last_login ? Carbon::createFromTimestamp($last_login, config('app.timezone')) : $last_login;
}

public function setLastLogin($carbon)
Expand Down
2 changes: 1 addition & 1 deletion src/Auth/Passwords/TokenRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public function exists(CanResetPasswordContract $user, $token)
$record = $this->getResets()->get($user->email());

return $record &&
! $this->tokenExpired(Carbon::createFromTimestamp($record['created_at']))
! $this->tokenExpired(Carbon::createFromTimestamp($record['created_at'], config('app.timezone')))
&& $this->hasher->check($token, $record['token']);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Data/ExistsAsFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public function fileLastModified()
return Carbon::now();
}

return Carbon::createFromTimestamp(File::lastModified($this->path()));
return Carbon::createFromTimestamp(File::lastModified($this->path()), config('app.timezone'));
}

public function fileExtension()
Expand Down
2 changes: 1 addition & 1 deletion src/Data/TracksLastModified.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ trait TracksLastModified
public function lastModified()
{
return $this->has('updated_at')
? Carbon::createFromTimestamp($this->get('updated_at'))
? Carbon::createFromTimestamp($this->get('updated_at'), config('app.timezone'))
: $this->fileLastModified();
}

Expand Down
2 changes: 1 addition & 1 deletion src/Entries/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ public function makeFromRevision($revision)
->slug($attrs['slug']);

if ($this->collection()->dated() && ($date = Arr::get($attrs, 'date'))) {
$entry->date(Carbon::createFromTimestamp($date));
$entry->date(Carbon::createFromTimestamp($date, config('app.timezone')));
}

return $entry;
Expand Down
2 changes: 1 addition & 1 deletion src/Forms/Submission.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public function columns()
*/
public function date()
{
return Carbon::createFromTimestamp($this->id());
return Carbon::createFromTimestamp($this->id(), config('app.timezone'));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Controllers/CP/SessionTimeoutController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function __invoke()
// remember me would have already been served a 403 error and wouldn't have got this far.
$lastActivity = session('last_activity', now()->timestamp);

return Carbon::createFromTimestamp($lastActivity)
return Carbon::createFromTimestamp($lastActivity, config('app.timezone'))
->addMinutes(config('session.lifetime'))
->diffInSeconds();
}
Expand Down
32 changes: 28 additions & 4 deletions src/Http/Middleware/Localize.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
use Carbon\Carbon;
use Closure;
use Illuminate\Support\Facades\Date;
use ReflectionClass;
use Statamic\Facades\Site;
use Statamic\Statamic;
use Statamic\Support\Arr;

class Localize
{
Expand All @@ -29,10 +31,7 @@ public function handle($request, Closure $next)
app()->setLocale($site->lang());

// Get original Carbon format so it can be restored later.
// There's no getter for it, so we'll use reflection.
$format = (new \ReflectionClass(Carbon::class))->getProperty('toStringFormat');
$format->setAccessible(true);
$originalToStringFormat = $format->getValue();
$originalToStringFormat = $this->getToStringFormat();
Date::setToStringFormat(Statamic::dateFormat());

$response = $next($request);
Expand All @@ -45,4 +44,29 @@ public function handle($request, Closure $next)

return $response;
}

/**
* This method is used to get the current toStringFormat for Carbon, in order for us
* to restore it later. There's no getter for it, so we need to use reflection.
*
* @throws \ReflectionException
*/
private function getToStringFormat(): ?string
{
$reflection = new ReflectionClass($date = Date::now());

// Carbon 2.x
if ($reflection->hasProperty('toStringFormat')) {
$format = $reflection->getProperty('toStringFormat');
$format->setAccessible(true);

return $format->getValue();
}

// Carbon 3.x
$factory = $reflection->getMethod('getFactory');
$factory->setAccessible(true);

return Arr::get($factory->invoke($date)->getSettings(), 'toStringFormat');
}
}
2 changes: 1 addition & 1 deletion src/Licensing/LicenseManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function requestRateLimited()
public function failedRequestRetrySeconds()
{
return $this->requestRateLimited()
? Carbon::createFromTimestamp($this->response('expiry'))->diffInSeconds()
? (int) Carbon::createFromTimestamp($this->response('expiry'), config('app.timezone'))->diffInSeconds(absolute: true)
: null;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Licensing/Outpost.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ private function cacheAndReturnValidationResponse($e)

private function cacheAndReturnRateLimitResponse($e)
{
$seconds = $e->getResponse()->getHeader('Retry-After')[0];
$seconds = (int) $e->getResponse()->getHeader('Retry-After')[0];

return $this->cacheResponse(now()->addSeconds($seconds), ['error' => 429]);
}
Expand Down
16 changes: 8 additions & 8 deletions src/Modifiers/CoreModifiers.php
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ public function dashify($value)
*/
public function daysAgo($value, $params)
{
return $this->carbon($value)->diffInDays(Arr::get($params, 0));
return (int) abs($this->carbon($value)->diffInDays(Arr::get($params, 0)));
}

/**
Expand Down Expand Up @@ -1055,7 +1055,7 @@ public function hexToRgb($value)
*/
public function hoursAgo($value, $params)
{
return $this->carbon($value)->diffInHours(Arr::get($params, 0));
return (int) abs($this->carbon($value)->diffInHours(Arr::get($params, 0)));
}

/**
Expand Down Expand Up @@ -1617,7 +1617,7 @@ public function md5($value)
*/
public function minutesAgo($value, $params)
{
return $this->carbon($value)->diffInMinutes(Arr::get($params, 0));
return (int) abs($this->carbon($value)->diffInMinutes(Arr::get($params, 0)));
}

/**
Expand Down Expand Up @@ -1652,7 +1652,7 @@ public function modifyDate($value, $params)
*/
public function monthsAgo($value, $params)
{
return $this->carbon($value)->diffInMonths(Arr::get($params, 0));
return (int) abs($this->carbon($value)->diffInMonths(Arr::get($params, 0)));
}

/**
Expand Down Expand Up @@ -2234,7 +2234,7 @@ public function segment($value, $params, $context)
*/
public function secondsAgo($value, $params)
{
return $this->carbon($value)->diffInSeconds(Arr::get($params, 0));
return (int) abs($this->carbon($value)->diffInSeconds(Arr::get($params, 0)));
}

/**
Expand Down Expand Up @@ -2933,7 +2933,7 @@ public function values($value)
*/
public function weeksAgo($value, $params)
{
return $this->carbon($value)->diffInWeeks(Arr::get($params, 0));
return (int) abs($this->carbon($value)->diffInWeeks(Arr::get($params, 0)));
}

/**
Expand Down Expand Up @@ -3045,7 +3045,7 @@ public function wordCount($value)
*/
public function yearsAgo($value, $params)
{
return $this->carbon($value)->diffInYears(Arr::get($params, 0));
return (int) abs($this->carbon($value)->diffInYears(Arr::get($params, 0)));
}

/**
Expand Down Expand Up @@ -3210,7 +3210,7 @@ private function getMathModifierNumber($params, $context)
private function carbon($value)
{
if (! $value instanceof Carbon) {
$value = (is_numeric($value)) ? Date::createFromTimestamp($value) : Date::parse($value);
$value = (is_numeric($value)) ? Date::createFromTimestamp($value, config('app.timezone')) : Date::parse($value);
}

return $value;
Expand Down
2 changes: 1 addition & 1 deletion src/Revisions/RevisionRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ protected function makeRevisionFromFile($key, $path)
->key($key)
->action($yaml['action'] ?? false)
->id($date = $yaml['date'])
->date(Carbon::createFromTimestamp($date))
->date(Carbon::createFromTimestamp($date, config('app.timezone')))
->user($yaml['user'] ?? false)
->message($yaml['message'] ?? false)
->attributes($yaml['attributes']);
Expand Down
2 changes: 1 addition & 1 deletion src/Stache/Stache.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ public function buildDate()
return null;
}

return Carbon::createFromTimestamp($cache['date']);
return Carbon::createFromTimestamp($cache['date'], config('app.timezone'));
}

public function disableUpdatingIndexes()
Expand Down
2 changes: 1 addition & 1 deletion src/Support/FileCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public function toArray()
'size_mb' => $kb,
'size_gb' => $kb,
'is_file' => File::isImage($path),
'last_modified' => Carbon::createFromTimestamp(File::lastModified($path)),
'last_modified' => Carbon::createFromTimestamp(File::lastModified($path), config('app.timezone')),
];
}

Expand Down
2 changes: 1 addition & 1 deletion src/Taxonomies/LocalizedTerm.php
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ protected function defaultAugmentedRelations()
public function lastModified()
{
return $this->has('updated_at')
? Carbon::createFromTimestamp($this->get('updated_at'))
? Carbon::createFromTimestamp($this->get('updated_at'), config('app.timezone'))
: $this->term->fileLastModified();
}

Expand Down
2 changes: 1 addition & 1 deletion src/Tokens/FileTokenRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private function makeFromPath(string $path): FileToken

return $this
->make($token, $yaml['handler'], $yaml['data'] ?? [])
->expireAt(Carbon::createFromTimestamp($yaml['expires_at']));
->expireAt(Carbon::createFromTimestamp($yaml['expires_at'], config('app.timezone')));
}

public static function bindings(): array
Expand Down
2 changes: 1 addition & 1 deletion tests/Assets/AssetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2017,7 +2017,7 @@ public function it_can_process_a_custom_image_format()
public function it_appends_timestamp_to_uploaded_files_filename_if_it_already_exists()
{
Event::fake();
Carbon::setTestNow(Carbon::createFromTimestamp(1549914700));
Carbon::setTestNow(Carbon::createFromTimestamp(1549914700, config('app.timezone')));
$asset = $this->container->makeAsset('path/to/asset.jpg');
Facades\AssetContainer::shouldReceive('findByHandle')->with('test_container')->andReturn($this->container);
Storage::disk('test')->put('path/to/asset.jpg', '');
Expand Down
2 changes: 1 addition & 1 deletion tests/Feature/Assets/StoreAssetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public function it_can_upload_and_overwrite()
#[Test]
public function it_can_upload_and_append_timestamp()
{
Carbon::setTestNow(Carbon::createFromTimestamp(1697379288));
Carbon::setTestNow(Carbon::createFromTimestamp(1697379288, config('app.timezone')));
Storage::disk('test')->put('path/to/test.jpg', 'contents');
Storage::disk('test')->assertExists('path/to/test.jpg');
$this->assertCount(1, Storage::disk('test')->files('path/to'));
Expand Down
4 changes: 2 additions & 2 deletions tests/Feature/Entries/EntryRevisionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public function it_restores_a_published_entrys_working_copy_to_another_revision(

$revision = tap((new Revision)
->key('collections/blog/en/123')
->date(Carbon::createFromTimestamp('1553546421'))
->date(Carbon::createFromTimestamp('1553546421', config('app.timezone')))
->attributes([
'published' => false,
'slug' => 'existing-slug',
Expand Down Expand Up @@ -345,7 +345,7 @@ public function it_restores_an_unpublished_entrys_contents_to_another_revision()

$revision = tap((new Revision)
->key('collections/blog/en/123')
->date(Carbon::createFromTimestamp('1553546421'))
->date(Carbon::createFromTimestamp('1553546421', config('app.timezone')))
->attributes([
'published' => true,
'slug' => 'existing-slug',
Expand Down
2 changes: 1 addition & 1 deletion tests/Feature/Fieldtypes/FilesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function it_uploads_a_file($container, $isImage, $expectedPath, $expected
? UploadedFile::fake()->image('test.jpg', 50, 75)
: UploadedFile::fake()->create('test.txt');

Date::setTestNow(Date::createFromTimestamp(1671484636));
Date::setTestNow(Date::createFromTimestamp(1671484636, config('app.timezone')));

$disk = Storage::fake('local');

Expand Down
14 changes: 13 additions & 1 deletion tests/Feature/GraphQL/Fieldtypes/DateFieldtypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use Illuminate\Support\Carbon;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use ReflectionClass;
use Statamic\Support\Arr;

#[Group('graphql')]
class DateFieldtypeTest extends FieldtypeTestCase
Expand All @@ -14,7 +16,17 @@ public function setUp(): void
parent::setUp();

Carbon::macro('getToStringFormat', function () {
return static::$toStringFormat;
// Carbon 2.x
if (property_exists(static::this(), 'toStringFormat')) {
return static::$toStringFormat;
}

// Carbon 3.x
$reflection = new ReflectionClass(self::this());
$factory = $reflection->getMethod('getFactory');
$factory->setAccessible(true);

return Arr::get($factory->invoke(self::this())->getSettings(), 'toStringFormat');
});
}

Expand Down
44 changes: 44 additions & 0 deletions tests/Modifiers/DaysAgoTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace Tests\Modifiers;

use Illuminate\Support\Carbon;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Modifiers\Modify;
use Tests\TestCase;

class DaysAgoTest extends TestCase
{
#[Test]
#[DataProvider('dateProvider')]
public function it_outputs_days_ago($input, $expected)
{
Carbon::setTestNow(Carbon::parse('2025-02-20 00:00'));

$this->assertSame($expected, $this->modify(Carbon::parse($input)));
}

public static function dateProvider()
{
return [
// Carbon 3 would return floats but to preserve backwards compatibility
// with Carbon 2 we will cast to integers.
'same time' => ['2025-02-20 00:00', 0],
'less than a day ago' => ['2025-02-19 11:00', 0],
'1 day ago' => ['2025-02-19 00:00', 1],
'2 days ago' => ['2025-02-18 00:00', 2],

// Future dates would return negative numbers in Carbon 3 but to preserve
// backwards compatibility with Carbon 2, we keep them positive.
'one day from now' => ['2025-02-21 00:00', 1],
'less than a day from now' => ['2025-02-20 13:00', 0],
'more than a day from now' => ['2025-02-21 13:00', 1],
];
}

public function modify($value)
{
return Modify::value($value)->daysAgo()->fetch();
}
}
Loading

0 comments on commit 1a50ee8

Please sign in to comment.