diff --git a/src/gui/views/gcal-week-grid.c b/src/gui/views/gcal-week-grid.c index c04f1a78..877f84ca 100644 --- a/src/gui/views/gcal-week-grid.c +++ b/src/gui/views/gcal-week-grid.c @@ -294,8 +294,8 @@ on_click_gesture_released_cb (GtkGestureClick *click_gesture, if (ltr) { - start = g_date_time_add_minutes (week_start, start_cell * 30); - end = g_date_time_add_minutes (week_start, (end_cell + 1) * 30); + start = gcal_date_time_add_floating_minutes (week_start, start_cell * 30); + end = gcal_date_time_add_floating_minutes (week_start, (end_cell + 1) * 30); } else { @@ -306,8 +306,8 @@ on_click_gesture_released_cb (GtkGestureClick *click_gesture, rtl_start_cell = start_cell + (rtl_column - column) * 48; rtl_end_cell = (rtl_column * MINUTES_PER_DAY + minute) / 30; - start = g_date_time_add_minutes (week_start, rtl_start_cell * 30); - end = g_date_time_add_minutes (week_start, (rtl_end_cell + 1) * 30); + start = gcal_date_time_add_floating_minutes (week_start, rtl_start_cell * 30); + end = gcal_date_time_add_floating_minutes (week_start, (rtl_end_cell + 1) * 30); } local_x = round ((column + 0.5) * (alloc.width / 7.0)); diff --git a/src/utils/gcal-date-time-utils.c b/src/utils/gcal-date-time-utils.c index 358ace8e..09fd1e4e 100644 --- a/src/utils/gcal-date-time-utils.c +++ b/src/utils/gcal-date-time-utils.c @@ -258,3 +258,31 @@ gcal_timezone_to_icaltimezone (GTimeZone *tz) return ical_tz; } +/** + * gcal_date_time_add_floating_minutes: + * @initial: Initial #GDateTime to add minutes + * @minutes: The number of minutes to add + * + * Calculate the datetime initial + minutes without caring about dayligh saving + * changes, so if it's 02:30 + 30 minutes, the result will be 03:00, always, + * independently of the daylight saving logic. + * + * Returns: (transfer full): A new #GDateTime with initial + minutes + */ +GDateTime* +gcal_date_time_add_floating_minutes (GDateTime *initial, + gint minutes) +{ + g_autoptr (GDateTime) result = NULL; + GTimeSpan initial_offset = 0; + GTimeSpan result_offset = 0; + GTimeSpan offset = 0; + + result = g_date_time_add_minutes (initial, minutes); + initial_offset = g_date_time_get_utc_offset (initial); + result_offset = g_date_time_get_utc_offset (result); + offset = initial_offset - result_offset; + // offset is in microseconds, convert to seconds + offset = offset / 1000000; + return g_date_time_add_seconds (result, offset); +} diff --git a/src/utils/gcal-date-time-utils.h b/src/utils/gcal-date-time-utils.h index 50d9707e..635338f8 100644 --- a/src/utils/gcal-date-time-utils.h +++ b/src/utils/gcal-date-time-utils.h @@ -53,4 +53,8 @@ GDateTime* gcal_date_time_from_icaltime (const ICalTime ICalTimezone* gcal_timezone_to_icaltimezone (GTimeZone *tz); + +GDateTime* gcal_date_time_add_floating_minutes (GDateTime *initial, + gint minutes); + G_END_DECLS diff --git a/tests/meson.build b/tests/meson.build index 62d3ba04..e5d0a5cb 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -33,6 +33,7 @@ libgcal_test_dep = declare_dependency( tests = [ 'server', + 'daylight-saving', 'discoverer', 'event', 'range', diff --git a/tests/test-daylight-saving.c b/tests/test-daylight-saving.c new file mode 100644 index 00000000..9a0352a7 --- /dev/null +++ b/tests/test-daylight-saving.c @@ -0,0 +1,125 @@ +/* test-daylight-saving.c + * + * Copyright © 2023 Daniel García Moreno + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "gcal-utils.h" + +/*********************************************************************************************************************/ + +static void +floating_minutes (void) +{ + g_autoptr (GDateTime) week_start = NULL; + g_autoptr (GDateTime) start = NULL; + g_autoptr (GDateTime) end = NULL; + g_autoptr (GTimeZone) cest = NULL; + + int start_cell = 3 * 2; // three hours (daylight time starts at 3:00 AM) + int end_cell = start_cell; + + g_test_bug ("482"); + + cest = g_time_zone_new_identifier ("Europe/Madrid"); + + // Without daylight saving change + week_start = g_date_time_new (cest, 2023, 11, 5, 0, 0, 0); + g_assert_cmpstr (g_date_time_format_iso8601 (week_start), ==, "2023-11-05T00:00:00+01"); + g_assert_false (g_date_time_is_daylight_savings (week_start)); + + start = gcal_date_time_add_floating_minutes (week_start, start_cell * 30); + end = gcal_date_time_add_floating_minutes (week_start, (end_cell + 1) * 30); + + g_assert_false (g_date_time_is_daylight_savings (start)); + g_assert_false (g_date_time_is_daylight_savings (end)); + + g_assert_cmpstr (g_date_time_format_iso8601 (start), ==, "2023-11-05T03:00:00+01"); + g_assert_cmpstr (g_date_time_format_iso8601 (end), ==, "2023-11-05T03:30:00+01"); +} + + +static void +floating_minutes_october (void) +{ + g_autoptr (GDateTime) week_start = NULL; + g_autoptr (GDateTime) start = NULL; + g_autoptr (GDateTime) end = NULL; + g_autoptr (GTimeZone) cest = NULL; + + int start_cell = 3 * 2; // three hours (daylight time starts at 3:00 AM) + int end_cell = start_cell; + + g_test_bug ("482"); + + cest = g_time_zone_new_identifier ("Europe/Madrid"); + + // With daylight saving change + week_start = g_date_time_new (cest, 2023, 10, 29, 0, 0, 0); + g_assert_cmpstr (g_date_time_format_iso8601 (week_start), ==, "2023-10-29T00:00:00+02"); + g_assert_true (g_date_time_is_daylight_savings (week_start)); + + start = gcal_date_time_add_floating_minutes (week_start, start_cell * 30); + end = gcal_date_time_add_floating_minutes (week_start, (end_cell + 1) * 30); + + g_assert_cmpstr (g_date_time_format_iso8601 (start), ==, "2023-10-29T03:00:00+01"); + g_assert_cmpstr (g_date_time_format_iso8601 (end), ==, "2023-10-29T03:30:00+01"); +} + +static void +floating_minutes_march (void) +{ + g_autoptr (GDateTime) week_start = NULL; + g_autoptr (GDateTime) start = NULL; + g_autoptr (GDateTime) end = NULL; + g_autoptr (GTimeZone) cest = NULL; + + int start_cell = 3 * 2; // three hours (daylight time starts at 3:00 AM) + int end_cell = start_cell; + + g_test_bug ("482"); + + cest = g_time_zone_new_identifier ("Europe/Madrid"); + + // With daylight saving change + week_start = g_date_time_new (cest, 2023, 3, 26, 0, 0, 0); + g_assert_cmpstr (g_date_time_format_iso8601 (week_start), ==, "2023-03-26T00:00:00+01"); + g_assert_false (g_date_time_is_daylight_savings (week_start)); + + start = gcal_date_time_add_floating_minutes (week_start, start_cell * 30); + end = gcal_date_time_add_floating_minutes (week_start, (end_cell + 1) * 30); + + g_assert_cmpstr (g_date_time_format_iso8601 (start), ==, "2023-03-26T03:00:00+02"); + g_assert_cmpstr (g_date_time_format_iso8601 (end), ==, "2023-03-26T03:30:00+02"); +} + +/*********************************************************************************************************************/ + +gint +main (gint argc, + gchar *argv[]) +{ + g_setenv ("TZ", "UTC", TRUE); + + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/"); + + g_test_add_func ("/daylight/floating-minutes", floating_minutes); + g_test_add_func ("/daylight/floating-minutes-october", floating_minutes_october); + g_test_add_func ("/daylight/floating-minutes-march", floating_minutes_march); + + return g_test_run (); +}