Skip to content

Commit 13b70d1

Browse files
authored
Add shiftTimezone() method to Chronos (#507)
Adds a shiftTimezone() method that changes the timezone while keeping the local time. This is different from setTimezone() which converts the time to the new timezone. For example, 10:00 AM in New York shifted to Chicago gives 10:00 AM in Chicago, while setTimezone would give 9:00 AM in Chicago.
1 parent f50c542 commit 13b70d1

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

src/Chronos.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,25 @@ public function setTimezone(DateTimeZone|string $value): static
10401040
return parent::setTimezone(static::safeCreateDateTimeZone($value));
10411041
}
10421042

1043+
/**
1044+
* Change the timezone while keeping the local time.
1045+
*
1046+
* Unlike `setTimezone()` which converts the time to the new timezone,
1047+
* this method keeps the same wall clock time but changes the timezone.
1048+
*
1049+
* For example, if you have 10:00 AM in New York and shift to Chicago,
1050+
* you'll get 10:00 AM in Chicago (not 9:00 AM as setTimezone would give).
1051+
*
1052+
* @param \DateTimeZone|string $timezone The new timezone
1053+
* @return static
1054+
*/
1055+
public function shiftTimezone(DateTimeZone|string $timezone): static
1056+
{
1057+
$timezone = static::safeCreateDateTimeZone($timezone);
1058+
1059+
return new static($this->format('Y-m-d H:i:s.u'), $timezone);
1060+
}
1061+
10431062
/**
10441063
* Return time zone set for this instance.
10451064
*

tests/TestCase/DateTime/InstanceTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,45 @@ public function testInstanceFromDateTimeKeepsMicros()
4444
$carbon = Chronos::instance($datetime);
4545
$this->assertSame($micro, $carbon->micro);
4646
}
47+
48+
public function testShiftTimezone(): void
49+
{
50+
$dt = Chronos::create(2024, 6, 15, 10, 30, 0, 0, 'America/New_York');
51+
$shifted = $dt->shiftTimezone('America/Chicago');
52+
53+
// Same wall clock time
54+
$this->assertSame(10, $shifted->hour);
55+
$this->assertSame(30, $shifted->minute);
56+
$this->assertSame(0, $shifted->second);
57+
58+
// Different timezone
59+
$this->assertSame('America/Chicago', $shifted->tzName);
60+
61+
// Different UTC time (Chicago is 1 hour behind NY in summer)
62+
$this->assertNotEquals($dt->getTimestamp(), $shifted->getTimestamp());
63+
}
64+
65+
public function testShiftTimezoneVsSetTimezone(): void
66+
{
67+
$dt = Chronos::create(2024, 6, 15, 10, 0, 0, 0, 'America/New_York');
68+
69+
// setTimezone converts - same moment, different wall clock
70+
$converted = $dt->setTimezone('America/Chicago');
71+
$this->assertSame(9, $converted->hour);
72+
$this->assertSame($dt->getTimestamp(), $converted->getTimestamp());
73+
74+
// shiftTimezone keeps wall clock - different moment
75+
$shifted = $dt->shiftTimezone('America/Chicago');
76+
$this->assertSame(10, $shifted->hour);
77+
$this->assertNotEquals($dt->getTimestamp(), $shifted->getTimestamp());
78+
}
79+
80+
public function testShiftTimezonePreservesMicroseconds(): void
81+
{
82+
$dt = Chronos::create(2024, 6, 15, 10, 30, 45, 123456, 'America/New_York');
83+
$shifted = $dt->shiftTimezone('Europe/London');
84+
85+
$this->assertSame(123456, $shifted->microsecond);
86+
$this->assertSame(45, $shifted->second);
87+
}
4788
}

0 commit comments

Comments
 (0)