/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) version 3. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * * * Authors: * JP Rosevear * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "e-meeting-utils.h" #include "e-meeting-attendee.h" #define E_MEETING_ATTENDEE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_MEETING_ATTENDEE, EMeetingAttendeePrivate)) struct _EMeetingAttendeePrivate { gchar *address; gchar *member; gchar *fburi; icalparameter_cutype cutype; icalparameter_role role; gboolean rsvp; gchar *delto; gchar *delfrom; icalparameter_partstat status; gchar *sentby; gchar *cn; gchar *language; EMeetingAttendeeEditLevel edit_level; gboolean has_calendar_info; GArray *busy_periods; gboolean busy_periods_sorted; EMeetingTime busy_periods_start; EMeetingTime busy_periods_end; gboolean start_busy_range_set; gboolean end_busy_range_set; gint longest_period_in_days; }; enum { CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static void e_meeting_attendee_finalize (GObject *obj); G_DEFINE_TYPE (EMeetingAttendee, e_meeting_attendee, G_TYPE_OBJECT) static gchar * string_test (gchar *string) { return string != NULL ? string : g_strdup (""); } static gboolean string_is_set (gchar *string) { if (string != NULL && *string != '\0') return TRUE; return FALSE; } static void busy_periods_array_clear_func (gpointer data) { EMeetingFreeBusyPeriod *period = (EMeetingFreeBusyPeriod *) data; /* We're expected to clear the data segment, * but not deallocate the segment itself. The * XFB data possibly attached to the * EMeetingFreeBusyPeriod requires special * care when removing elements from the GArray */ e_meeting_xfb_data_clear (&(period->xfb)); } static void notify_changed (EMeetingAttendee *ia) { g_signal_emit_by_name (ia, "changed"); } static void e_meeting_attendee_finalize (GObject *object) { EMeetingAttendeePrivate *priv; priv = E_MEETING_ATTENDEE_GET_PRIVATE (object); g_free (priv->address); g_free (priv->member); g_free (priv->fburi); g_free (priv->delto); g_free (priv->delfrom); g_free (priv->sentby); g_free (priv->cn); g_free (priv->language); g_array_free (priv->busy_periods, TRUE); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_meeting_attendee_parent_class)->finalize (object); } static void e_meeting_attendee_class_init (EMeetingAttendeeClass *class) { GObjectClass *object_class; g_type_class_add_private (class, sizeof (EMeetingAttendeePrivate)); object_class = G_OBJECT_CLASS (class); object_class->finalize = e_meeting_attendee_finalize; signals[CHANGED] = g_signal_new ( "changed", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (EMeetingAttendeeClass, changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void e_meeting_attendee_init (EMeetingAttendee *ia) { ia->priv = E_MEETING_ATTENDEE_GET_PRIVATE (ia); ia->priv->address = string_test (NULL); ia->priv->member = string_test (NULL); ia->priv->cutype = ICAL_CUTYPE_NONE; ia->priv->role = ICAL_ROLE_NONE; ia->priv->rsvp = FALSE; ia->priv->delto = string_test (NULL); ia->priv->delfrom = string_test (NULL); ia->priv->status = ICAL_PARTSTAT_NONE; ia->priv->sentby = string_test (NULL); ia->priv->cn = string_test (NULL); ia->priv->language = string_test (NULL); ia->priv->edit_level = E_MEETING_ATTENDEE_EDIT_FULL; ia->priv->has_calendar_info = FALSE; ia->priv->busy_periods = g_array_new (FALSE, FALSE, sizeof (EMeetingFreeBusyPeriod)); g_array_set_clear_func (ia->priv->busy_periods, busy_periods_array_clear_func); ia->priv->busy_periods_sorted = FALSE; g_date_clear (&ia->priv->busy_periods_start.date, 1); ia->priv->busy_periods_start.hour = 0; ia->priv->busy_periods_start.minute = 0; g_date_clear (&ia->priv->busy_periods_end.date, 1); ia->priv->busy_periods_end.hour = 0; ia->priv->busy_periods_end.minute = 0; ia->priv->start_busy_range_set = FALSE; ia->priv->end_busy_range_set = FALSE; ia->priv->longest_period_in_days = 0; } GObject * e_meeting_attendee_new (void) { return g_object_new (E_TYPE_MEETING_ATTENDEE, NULL); } GObject * e_meeting_attendee_new_from_e_cal_component_attendee (ECalComponentAttendee *ca) { EMeetingAttendee *ia; ia = E_MEETING_ATTENDEE (g_object_new (E_TYPE_MEETING_ATTENDEE, NULL)); e_meeting_attendee_set_address (ia, g_strdup (ca->value)); e_meeting_attendee_set_member (ia, g_strdup (ca->member)); e_meeting_attendee_set_cutype (ia, ca->cutype); e_meeting_attendee_set_role (ia, ca->role); e_meeting_attendee_set_status (ia, ca->status); e_meeting_attendee_set_rsvp (ia, ca->rsvp); e_meeting_attendee_set_delto (ia, g_strdup (ca->delto)); e_meeting_attendee_set_delfrom (ia, g_strdup (ca->delfrom)); e_meeting_attendee_set_sentby (ia, g_strdup (ca->sentby)); e_meeting_attendee_set_cn (ia, g_strdup (ca->cn)); e_meeting_attendee_set_language (ia, g_strdup (ca->language)); return G_OBJECT (ia); } ECalComponentAttendee * e_meeting_attendee_as_e_cal_component_attendee (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; ECalComponentAttendee *ca; priv = ia->priv; ca = g_new0 (ECalComponentAttendee, 1); ca->value = priv->address; ca->member = string_is_set (priv->member) ? priv->member : NULL; ca->cutype= priv->cutype; ca->role = priv->role; ca->status = priv->status; ca->rsvp = priv->rsvp; ca->delto = string_is_set (priv->delto) ? priv->delto : NULL; ca->delfrom = string_is_set (priv->delfrom) ? priv->delfrom : NULL; ca->sentby = string_is_set (priv->sentby) ? priv->sentby : NULL; ca->cn = string_is_set (priv->cn) ? priv->cn : NULL; ca->language = string_is_set (priv->language) ? priv->language : NULL; return ca; } const gchar * e_meeting_attendee_get_fburi (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->fburi; } void e_meeting_attendee_set_fburi (EMeetingAttendee *ia, gchar *fburi) { EMeetingAttendeePrivate *priv; priv = ia->priv; if (priv->fburi != NULL) g_free (priv->fburi); priv->fburi = string_test (fburi); notify_changed (ia); } const gchar * e_meeting_attendee_get_address (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->address; } void e_meeting_attendee_set_address (EMeetingAttendee *ia, gchar *address) { EMeetingAttendeePrivate *priv; priv = ia->priv; if (priv->address != NULL) g_free (priv->address); priv->address = string_test (address); notify_changed (ia); } gboolean e_meeting_attendee_is_set_address (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return string_is_set (priv->address); } const gchar * e_meeting_attendee_get_member (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->member; } void e_meeting_attendee_set_member (EMeetingAttendee *ia, gchar *member) { EMeetingAttendeePrivate *priv; priv = ia->priv; if (priv->member != NULL) g_free (priv->member); priv->member = string_test (member); notify_changed (ia); } gboolean e_meeting_attendee_is_set_member (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return string_is_set (priv->member); } icalparameter_cutype e_meeting_attendee_get_cutype (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->cutype; } void e_meeting_attendee_set_cutype (EMeetingAttendee *ia, icalparameter_cutype cutype) { EMeetingAttendeePrivate *priv; priv = ia->priv; priv->cutype = cutype; notify_changed (ia); } icalparameter_role e_meeting_attendee_get_role (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->role; } void e_meeting_attendee_set_role (EMeetingAttendee *ia, icalparameter_role role) { EMeetingAttendeePrivate *priv; priv = ia->priv; priv->role = role; notify_changed (ia); } gboolean e_meeting_attendee_get_rsvp (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->rsvp; } void e_meeting_attendee_set_rsvp (EMeetingAttendee *ia, gboolean rsvp) { EMeetingAttendeePrivate *priv; priv = ia->priv; priv->rsvp = rsvp; notify_changed (ia); } const gchar * e_meeting_attendee_get_delto (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->delto; } void e_meeting_attendee_set_delto (EMeetingAttendee *ia, gchar *delto) { EMeetingAttendeePrivate *priv; priv = ia->priv; if (priv->delto != NULL) g_free (priv->delto); priv->delto = string_test (delto); notify_changed (ia); } gboolean e_meeting_attendee_is_set_delto (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return string_is_set (priv->delto); } const gchar * e_meeting_attendee_get_delfrom (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->delfrom; } void e_meeting_attendee_set_delfrom (EMeetingAttendee *ia, gchar *delfrom) { EMeetingAttendeePrivate *priv; priv = ia->priv; if (priv->delfrom != NULL) g_free (priv->delfrom); priv->delfrom = string_test (delfrom); notify_changed (ia); } gboolean e_meeting_attendee_is_set_delfrom (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return string_is_set (priv->delfrom); } icalparameter_partstat e_meeting_attendee_get_status (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->status; } void e_meeting_attendee_set_status (EMeetingAttendee *ia, icalparameter_partstat status) { EMeetingAttendeePrivate *priv; priv = ia->priv; priv->status = status; notify_changed (ia); } const gchar * e_meeting_attendee_get_sentby (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->sentby; } void e_meeting_attendee_set_sentby (EMeetingAttendee *ia, gchar *sentby) { EMeetingAttendeePrivate *priv; priv = ia->priv; if (priv->sentby != NULL) g_free (priv->sentby); priv->sentby = string_test (sentby); notify_changed (ia); } gboolean e_meeting_attendee_is_set_sentby (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return string_is_set (priv->sentby); } const gchar * e_meeting_attendee_get_cn (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->cn; } void e_meeting_attendee_set_cn (EMeetingAttendee *ia, gchar *cn) { EMeetingAttendeePrivate *priv; priv = ia->priv; if (priv->cn != NULL) g_free (priv->cn); priv->cn = string_test (cn); notify_changed (ia); } gboolean e_meeting_attendee_is_set_cn (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return string_is_set (priv->cn); } const gchar * e_meeting_attendee_get_language (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->language; } void e_meeting_attendee_set_language (EMeetingAttendee *ia, gchar *language) { EMeetingAttendeePrivate *priv; priv = ia->priv; if (priv->language != NULL) g_free (priv->language); priv->language = string_test (language); notify_changed (ia); } gboolean e_meeting_attendee_is_set_language (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return string_is_set (priv->language); } EMeetingAttendeeType e_meeting_attendee_get_atype (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; if (priv->cutype == ICAL_CUTYPE_ROOM || priv->cutype == ICAL_CUTYPE_RESOURCE) return E_MEETING_ATTENDEE_RESOURCE; if (priv->role == ICAL_ROLE_CHAIR || priv->role == ICAL_ROLE_REQPARTICIPANT) return E_MEETING_ATTENDEE_REQUIRED_PERSON; return E_MEETING_ATTENDEE_OPTIONAL_PERSON; } EMeetingAttendeeEditLevel e_meeting_attendee_get_edit_level (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; g_return_val_if_fail (ia != NULL, E_MEETING_ATTENDEE_EDIT_NONE); g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), E_MEETING_ATTENDEE_EDIT_NONE); priv = ia->priv; return priv->edit_level; } void e_meeting_attendee_set_edit_level (EMeetingAttendee *ia, EMeetingAttendeeEditLevel level) { EMeetingAttendeePrivate *priv; g_return_if_fail (ia != NULL); g_return_if_fail (E_IS_MEETING_ATTENDEE (ia)); priv = ia->priv; priv->edit_level = level; } static gint compare_times (EMeetingTime *time1, EMeetingTime *time2) { gint day_comparison; day_comparison = g_date_compare ( &time1->date, &time2->date); if (day_comparison != 0) return day_comparison; if (time1->hour < time2->hour) return -1; if (time1->hour > time2->hour) return 1; if (time1->minute < time2->minute) return -1; if (time1->minute > time2->minute) return 1; /* The start times are exactly the same. */ return 0; } static gint compare_period_starts (gconstpointer arg1, gconstpointer arg2) { EMeetingFreeBusyPeriod *period1, *period2; period1 = (EMeetingFreeBusyPeriod *) arg1; period2 = (EMeetingFreeBusyPeriod *) arg2; return compare_times (&period1->start, &period2->start); } static void ensure_periods_sorted (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; if (priv->busy_periods_sorted) return; qsort ( priv->busy_periods->data, priv->busy_periods->len, sizeof (EMeetingFreeBusyPeriod), compare_period_starts); priv->busy_periods_sorted = TRUE; } gboolean e_meeting_attendee_get_has_calendar_info (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->has_calendar_info; } void e_meeting_attendee_set_has_calendar_info (EMeetingAttendee *ia, gboolean has_calendar_info) { EMeetingAttendeePrivate *priv; priv = ia->priv; priv->has_calendar_info = has_calendar_info; } const GArray * e_meeting_attendee_get_busy_periods (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; ensure_periods_sorted (ia); return priv->busy_periods; } gint e_meeting_attendee_find_first_busy_period (EMeetingAttendee *ia, GDate *date) { EMeetingAttendeePrivate *priv; EMeetingFreeBusyPeriod *period; gint lower, upper, middle = 0, cmp = 0; GDate tmp_date; priv = ia->priv; /* Make sure the busy periods have been sorted. */ ensure_periods_sorted (ia); /* Calculate the first day which could have a busy period which * continues onto our given date. */ tmp_date = *date; g_date_subtract_days (&tmp_date, priv->longest_period_in_days); /* We want the first busy period which starts on tmp_date. */ lower = 0; upper = priv->busy_periods->len; if (upper == 0) return -1; while (lower < upper) { middle = (lower + upper) >> 1; period = &g_array_index (priv->busy_periods, EMeetingFreeBusyPeriod, middle); cmp = g_date_compare (&tmp_date, &period->start.date); if (cmp == 0) break; else if (cmp < 0) upper = middle; else lower = middle + 1; } /* There may be several busy periods on the same day so we step * backwards to the first one. */ if (cmp == 0) { while (middle > 0) { period = &g_array_index (priv->busy_periods, EMeetingFreeBusyPeriod, middle - 1); if (g_date_compare (&tmp_date, &period->start.date) != 0) break; middle--; } } else if (cmp > 0) { /* This means we couldn't find a period on the given day, and * the last one we looked at was before it, so if there are * any more periods after this one we return it. */ middle++; if (priv->busy_periods->len <= middle) return -1; } return middle; } gboolean e_meeting_attendee_add_busy_period (EMeetingAttendee *ia, gint start_year, gint start_month, gint start_day, gint start_hour, gint start_minute, gint end_year, gint end_month, gint end_day, gint end_hour, gint end_minute, EMeetingFreeBusyType busy_type, const gchar *summary, const gchar *location) { EMeetingAttendeePrivate *priv; EMeetingFreeBusyPeriod period; gint period_in_days; g_return_val_if_fail (ia != NULL, FALSE); g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE); g_return_val_if_fail (busy_type < E_MEETING_FREE_BUSY_LAST, FALSE); /* summary may be NULL (optional XFB data) */ /* location may be NULL (optional XFB data) */ priv = ia->priv; /* Check the dates are valid. */ if (!g_date_valid_dmy (start_day, start_month, start_year)) return FALSE; if (!g_date_valid_dmy (end_day, end_month, end_year)) return FALSE; if (start_hour < 0 || start_hour > 23) return FALSE; if (end_hour < 0 || end_hour > 23) return FALSE; if (start_minute < 0 || start_minute > 59) return FALSE; if (end_minute < 0 || end_minute > 59) return FALSE; g_date_clear (&period.start.date, 1); g_date_clear (&period.end.date, 1); g_date_set_dmy (&period.start.date, start_day, start_month, start_year); g_date_set_dmy (&period.end.date, end_day, end_month, end_year); period.start.hour = start_hour; period.start.minute = start_minute; period.end.hour = end_hour; period.end.minute = end_minute; period.busy_type = busy_type; /* Check that the start time is before or equal to the end time. */ if (compare_times (&period.start, &period.end) > 0) return FALSE; /* If the busy_type is FREE, then there is no need to render it in UI */ if (busy_type == E_MEETING_FREE_BUSY_FREE) goto done; /* If the busy range is not set elsewhere, track it as best we can */ if (!priv->start_busy_range_set) { if (!g_date_valid (&priv->busy_periods_start.date)) { priv->busy_periods_start.date = period.start.date; priv->busy_periods_start.hour = period.start.hour; priv->busy_periods_start.minute = period.start.minute; } else { gint compare; compare = g_date_compare ( &period.start.date, &priv->busy_periods_start.date); switch (compare) { case -1: priv->busy_periods_start.date = period.start.date; priv->busy_periods_start.hour = period.start.hour; priv->busy_periods_start.minute = period.start.minute; break; case 0: if (period.start.hour < priv->busy_periods_start.hour || (period.start.hour == priv->busy_periods_start.hour && period.start.minute < priv->busy_periods_start.minute)) { priv->busy_periods_start.date = period.start.date; priv->busy_periods_start.hour = period.start.hour; priv->busy_periods_start.minute = period.start.minute; break; } break; } } } if (!priv->end_busy_range_set) { if (!g_date_valid (&priv->busy_periods_end.date)) { priv->busy_periods_end.date = period.end.date; priv->busy_periods_end.hour = period.end.hour; priv->busy_periods_end.minute = period.end.minute; } else { gint compare; compare = g_date_compare ( &period.end.date, &priv->busy_periods_end.date); switch (compare) { case 0: if (period.end.hour > priv->busy_periods_end.hour || (period.end.hour == priv->busy_periods_end.hour && period.end.minute > priv->busy_periods_end.minute)) { priv->busy_periods_end.date = period.end.date; priv->busy_periods_end.hour = period.end.hour; priv->busy_periods_end.minute = period.end.minute; break; } break; case 1: priv->busy_periods_end.date = period.end.date; priv->busy_periods_end.hour = period.end.hour; priv->busy_periods_end.minute = period.end.minute; break; } } } /* Setting of extended free/busy (XFB) data, if we have any. */ e_meeting_xfb_data_init (&(period.xfb)); e_meeting_xfb_data_set (&(period.xfb), summary, location); g_array_append_val (priv->busy_periods, period); period_in_days = g_date_get_julian (&period.end.date) - g_date_get_julian (&period.start.date) + 1; priv->longest_period_in_days = MAX (priv->longest_period_in_days, period_in_days); done: priv->has_calendar_info = TRUE; priv->busy_periods_sorted = FALSE; return TRUE; } EMeetingTime e_meeting_attendee_get_start_busy_range (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->busy_periods_start; } EMeetingTime e_meeting_attendee_get_end_busy_range (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; priv = ia->priv; return priv->busy_periods_end; } gboolean e_meeting_attendee_set_start_busy_range (EMeetingAttendee *ia, gint start_year, gint start_month, gint start_day, gint start_hour, gint start_minute) { EMeetingAttendeePrivate *priv; g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE); priv = ia->priv; /* Check the dates are valid. */ if (!g_date_valid_dmy (start_day, start_month, start_year)) return FALSE; if (start_hour < 0 || start_hour > 23) return FALSE; if (start_minute < 0 || start_minute > 59) return FALSE; g_date_clear (&priv->busy_periods_start.date, 1); g_date_set_dmy ( &priv->busy_periods_start.date, start_day, start_month, start_year); priv->busy_periods_start.hour = start_hour; priv->busy_periods_start.minute = start_minute; priv->start_busy_range_set = TRUE; return TRUE; } gboolean e_meeting_attendee_set_end_busy_range (EMeetingAttendee *ia, gint end_year, gint end_month, gint end_day, gint end_hour, gint end_minute) { EMeetingAttendeePrivate *priv; g_return_val_if_fail (E_IS_MEETING_ATTENDEE (ia), FALSE); priv = ia->priv; /* Check the dates are valid. */ if (!g_date_valid_dmy (end_day, end_month, end_year)) return FALSE; if (end_hour < 0 || end_hour > 23) return FALSE; if (end_minute < 0 || end_minute > 59) return FALSE; g_date_clear (&priv->busy_periods_end.date, 1); g_date_set_dmy ( &priv->busy_periods_end.date, end_day, end_month, end_year); priv->busy_periods_end.hour = end_hour; priv->busy_periods_end.minute = end_minute; priv->end_busy_range_set = TRUE; return TRUE; } /* Clears all busy times for the given attendee. */ void e_meeting_attendee_clear_busy_periods (EMeetingAttendee *ia) { EMeetingAttendeePrivate *priv; g_return_if_fail (E_IS_MEETING_ATTENDEE (ia)); priv = ia->priv; g_array_set_size (priv->busy_periods, 0); priv->busy_periods_sorted = TRUE; g_date_clear (&priv->busy_periods_start.date, 1); priv->busy_periods_start.hour = 0; priv->busy_periods_start.minute = 0; g_date_clear (&priv->busy_periods_end.date, 1); priv->busy_periods_end.hour = 0; priv->busy_periods_end.minute = 0; priv->longest_period_in_days = 0; }