From e27c44a821d52aa50e1e22036d44053b4bdaadfc Mon Sep 17 00:00:00 2001 From: paul678 Date: Sun, 29 Mar 2026 19:47:02 +0300 Subject: [PATCH 1/4] Properly handle UTC comparisons inside _calculateDifference Thus avoids daylight saving issues on isToday isTomorrow and wasYesterday --- lib/src/extensions.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index bc7a7ef..f656bf8 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -139,8 +139,8 @@ extension DateTimeTimeExtension on DateTime { bool isAtSameMicrosecondAs(DateTime other) => isAtSameMillisecondAs(other) && microsecond == other.microsecond; static int _calculateDifference(DateTime date) { - final now = clock.now(); - return DateTime(date.year, date.month, date.day).difference(DateTime(now.year, now.month, now.day)).inDays; + final now = date.isUtc ? clock.now().toUtc() : clock.now(); + return date.date.difference(now.date).inDays; } /// Returns a range of dates to [to], exclusive start, inclusive end From c89a99c0dce79db43856d59249c9ff7d95092e77 Mon Sep 17 00:00:00 2001 From: paul678 Date: Sun, 29 Mar 2026 23:23:30 +0300 Subject: [PATCH 2/4] MR: Added tests --- pubspec.yaml | 1 + test/time_test.dart | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/pubspec.yaml b/pubspec.yaml index 5a02585..6e1d5a7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,3 +17,4 @@ dev_dependencies: lints: ^6.0.0 test: ^1.28.0 coverage: ^1.15.0 + timezone: ^0.11.0 diff --git a/test/time_test.dart b/test/time_test.dart index 282d512..25a49f5 100644 --- a/test/time_test.dart +++ b/test/time_test.dart @@ -1,9 +1,12 @@ import 'package:clock/clock.dart'; import 'package:test/test.dart'; import 'package:time/time.dart'; +import 'package:timezone/timezone.dart' as tz; +import 'package:timezone/data/latest.dart' as tz; void main() { final date = DateTime(2000, 1, 1); + tz.initializeTimeZones(); group('TimeExtension', () { group('Integers', () { @@ -126,6 +129,30 @@ void main() { expect(reconstructed, equals(original)); }); + test('handles utc isToday correctly across DST switch', () { + final location = tz.getLocation('Europe/Bucharest'); + tz.setLocalLocation(location); + final now = tz.TZDateTime.local(2026, 3, 29, 16); + + withClock(Clock.fixed(now), () { + final tomorrowUtc = DateTime.utc(2026, 3, 30); + + /// Older implementation of _calculateDifference from time: 2.1.6 + int oldCalculateDifference(DateTime date) { + final now = clock.now(); + return DateTime(date.year, date.month, date.day).difference(DateTime(now.year, now.month, now.day)).inDays; + } + + expect( + oldCalculateDifference(tomorrowUtc), + 0, + reason: 'Old implementation will mark UTC tomorrow as todaytimezones', + ); + + expect(tomorrowUtc.isToday, false, reason: 'New implementation handles UTC correctly'); + }); + }); + test('can handle isToday', () { final today = date; withClock(Clock.fixed(today), () { From bc17d0a7551d2813a7b88c06a22e5822073946a3 Mon Sep 17 00:00:00 2001 From: paul678 Date: Mon, 30 Mar 2026 00:19:57 +0300 Subject: [PATCH 3/4] MR: update tests --- test/time_test.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/time_test.dart b/test/time_test.dart index 25a49f5..3dc68d9 100644 --- a/test/time_test.dart +++ b/test/time_test.dart @@ -135,6 +135,7 @@ void main() { final now = tz.TZDateTime.local(2026, 3, 29, 16); withClock(Clock.fixed(now), () { + final todayUtc = DateTime.utc(2026, 3, 29); final tomorrowUtc = DateTime.utc(2026, 3, 30); /// Older implementation of _calculateDifference from time: 2.1.6 @@ -143,12 +144,18 @@ void main() { return DateTime(date.year, date.month, date.day).difference(DateTime(now.year, now.month, now.day)).inDays; } + expect( + oldCalculateDifference(todayUtc), + 0, + reason: 'Old implementation will mark UTC today as today', + ); expect( oldCalculateDifference(tomorrowUtc), 0, - reason: 'Old implementation will mark UTC tomorrow as todaytimezones', + reason: 'Old implementation will mark UTC tomorrow as today', ); + expect(todayUtc.isToday, true, reason: 'New implementation handles UTC correctly'); expect(tomorrowUtc.isToday, false, reason: 'New implementation handles UTC correctly'); }); }); From d0eeef4312b67e0f0de321deb6302bc1395d9616 Mon Sep 17 00:00:00 2001 From: paul678 Date: Wed, 8 Apr 2026 10:27:08 +0300 Subject: [PATCH 4/4] Fix tests --- .github/workflows/build.yml | 7 +++- dart_test.yaml | 2 ++ test/time_test.dart | 66 ++++++++++++++++++++----------------- 3 files changed, 44 insertions(+), 31 deletions(-) create mode 100644 dart_test.yaml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7fae870..4548020 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,8 +28,13 @@ jobs: - name: Analyze run: dart analyze lib + - name: Run Bucharest timezone regression + env: + TZ: Europe/Bucharest + run: dart test --tags bucharest_tz + - name: Run tests - run: dart test --coverage coverage + run: dart test --exclude-tags bucharest_tz --coverage coverage - name: Coverage run: dart run coverage:format_coverage -l -i ./coverage/test/time_test.dart.vm.json -o ./coverage/lcov.info diff --git a/dart_test.yaml b/dart_test.yaml new file mode 100644 index 0000000..c1212c7 --- /dev/null +++ b/dart_test.yaml @@ -0,0 +1,2 @@ +tags: + bucharest_tz: diff --git a/test/time_test.dart b/test/time_test.dart index 3dc68d9..88ededb 100644 --- a/test/time_test.dart +++ b/test/time_test.dart @@ -129,36 +129,42 @@ void main() { expect(reconstructed, equals(original)); }); - test('handles utc isToday correctly across DST switch', () { - final location = tz.getLocation('Europe/Bucharest'); - tz.setLocalLocation(location); - final now = tz.TZDateTime.local(2026, 3, 29, 16); - - withClock(Clock.fixed(now), () { - final todayUtc = DateTime.utc(2026, 3, 29); - final tomorrowUtc = DateTime.utc(2026, 3, 30); - - /// Older implementation of _calculateDifference from time: 2.1.6 - int oldCalculateDifference(DateTime date) { - final now = clock.now(); - return DateTime(date.year, date.month, date.day).difference(DateTime(now.year, now.month, now.day)).inDays; - } - - expect( - oldCalculateDifference(todayUtc), - 0, - reason: 'Old implementation will mark UTC today as today', - ); - expect( - oldCalculateDifference(tomorrowUtc), - 0, - reason: 'Old implementation will mark UTC tomorrow as today', - ); - - expect(todayUtc.isToday, true, reason: 'New implementation handles UTC correctly'); - expect(tomorrowUtc.isToday, false, reason: 'New implementation handles UTC correctly'); - }); - }); + test( + 'handles utc isToday correctly across DST switch', + () { + final location = tz.getLocation('Europe/Bucharest'); + tz.setLocalLocation(location); + final now = tz.TZDateTime.local(2026, 3, 29, 16); + + withClock(Clock.fixed(now), () { + final todayUtc = DateTime.utc(2026, 3, 29); + final tomorrowUtc = DateTime.utc(2026, 3, 30); + + // Older implementation of _calculateDifference from time: 2.1.6. + int oldCalculateDifference(DateTime date) { + final now = clock.now(); + return DateTime(date.year, date.month, date.day).difference( + DateTime(now.year, now.month, now.day), + ).inDays; + } + + expect( + oldCalculateDifference(todayUtc), + 0, + reason: 'Old implementation will mark UTC today as today.', + ); + expect( + oldCalculateDifference(tomorrowUtc), + 0, + reason: 'Old implementation will mark UTC tomorrow as today.', + ); + + expect(todayUtc.isToday, true, reason: 'New implementation handles UTC correctly.'); + expect(tomorrowUtc.isToday, false, reason: 'New implementation handles UTC correctly.'); + }); + }, + tags: ['bucharest_tz'], + ); test('can handle isToday', () { final today = date;