LCOV - code coverage report
Current view: top level - libmogwai-tariff - period.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 283 291 97.3 %
Date: 2022-06-30 20:59:16 Functions: 22 22 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 197 264 74.6 %

           Branch data     Line data    Source code
       1                 :            : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
       2                 :            :  *
       3                 :            :  * Copyright © 2018 Endless Mobile, Inc.
       4                 :            :  *
       5                 :            :  * This library is free software; you can redistribute it and/or
       6                 :            :  * modify it under the terms of the GNU Lesser General Public
       7                 :            :  * License as published by the Free Software Foundation; either
       8                 :            :  * version 2.1 of the License, or (at your option) any later version.
       9                 :            :  *
      10                 :            :  * This library is distributed in the hope that it will be useful,
      11                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13                 :            :  * Lesser General Public License for more details.
      14                 :            :  *
      15                 :            :  * You should have received a copy of the GNU Lesser General Public
      16                 :            :  * License along with this library; if not, write to the Free Software
      17                 :            :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
      18                 :            :  *
      19                 :            :  * Authors:
      20                 :            :  *  - Philip Withnall <withnall@endlessm.com>
      21                 :            :  */
      22                 :            : 
      23                 :            : #include "config.h"
      24                 :            : 
      25                 :            : #include <glib/gi18n-lib.h>
      26                 :            : #include <glib.h>
      27                 :            : #include <glib-object.h>
      28                 :            : #include <gio/gio.h>
      29                 :            : #include <libmogwai-tariff/enums.h>
      30                 :            : #include <libmogwai-tariff/period.h>
      31                 :            : 
      32                 :            : 
      33         [ +  + ]:         12 : G_DEFINE_QUARK (MwtPeriodError, mwt_period_error)
      34                 :            : 
      35                 :            : static void mwt_period_constructed  (GObject      *object);
      36                 :            : static void mwt_period_dispose      (GObject      *object);
      37                 :            : static void mwt_period_get_property (GObject      *object,
      38                 :            :                                      guint         property_id,
      39                 :            :                                      GValue       *value,
      40                 :            :                                      GParamSpec   *pspec);
      41                 :            : static void mwt_period_set_property (GObject      *object,
      42                 :            :                                      guint         property_id,
      43                 :            :                                      const GValue *value,
      44                 :            :                                      GParamSpec   *pspec);
      45                 :            : 
      46                 :            : /**
      47                 :            :  * MwtPeriod:
      48                 :            :  *
      49                 :            :  * A representation of a period in a tariff where the tariff properties are
      50                 :            :  * constant (for example, a single capacity limit applies to the whole period).
      51                 :            :  *
      52                 :            :  * It has a start and end time, and properties which control how it repeats (if
      53                 :            :  * at all). The start time is inclusive, but the end time is exclusive (which
      54                 :            :  * makes handling of leap seconds at the end of a period easier).
      55                 :            :  *
      56                 :            :  * Repeats take leap years and timezone changes into account. For example, if a
      57                 :            :  * period spans 01:00 to 06:00 on 31st January, and repeats every month, a
      58                 :            :  * recurrence will happen on 28th February (or 29th February on a leap year),
      59                 :            :  * on 31st March, 30th April, etc.
      60                 :            :  *
      61                 :            :  * If a period spans 01:00 to 02:00 on a normal day, and a DST transition
      62                 :            :  * happens where the clocks go forward by 1 hour at 01:00 on a certain day, any
      63                 :            :  * recurrence of the period on that day will be skipped. Recurrences on days
      64                 :            :  * after the DST transition will happen at 01:00 to 02:00 in the new timezone.
      65                 :            :  *
      66                 :            :  * For a DST transition where the clocks go backward by 1 hour at 02:00 on a
      67                 :            :  * certain day, the time span 01:00–02:00 will happen twice. Any recurrence of a
      68                 :            :  * period which spans 01:00 to 02:00 will happen on the first occurrence of the
      69                 :            :  * time span, and will not repeat during the second occurrence.
      70                 :            :  *
      71                 :            :  * The #MwtPeriod class is immutable once loaded or constructed.
      72                 :            :  *
      73                 :            :  * Since: 0.1.0
      74                 :            :  */
      75                 :            : struct _MwtPeriod
      76                 :            : {
      77                 :            :   GObject parent;
      78                 :            : 
      79                 :            :   GDateTime *start;  /* (owned) */
      80                 :            :   GDateTime *end;  /* (owned) */
      81                 :            : 
      82                 :            :   MwtPeriodRepeatType repeat_type;
      83                 :            :   guint repeat_period;
      84                 :            : 
      85                 :            :   guint64 capacity_limit;
      86                 :            : };
      87                 :            : 
      88                 :            : typedef enum
      89                 :            : {
      90                 :            :   PROP_START = 1,
      91                 :            :   PROP_END,
      92                 :            :   PROP_REPEAT_TYPE,
      93                 :            :   PROP_REPEAT_PERIOD,
      94                 :            :   PROP_CAPACITY_LIMIT,
      95                 :            : } MwtPeriodProperty;
      96                 :            : 
      97   [ +  +  +  -  :       3476 : G_DEFINE_TYPE (MwtPeriod, mwt_period, G_TYPE_OBJECT)
                   +  + ]
      98                 :            : 
      99                 :            : static void
     100                 :         13 : mwt_period_class_init (MwtPeriodClass *klass)
     101                 :            : {
     102                 :         13 :   GObjectClass *object_class = (GObjectClass *) klass;
     103                 :         13 :   GParamSpec *props[PROP_CAPACITY_LIMIT + 1] = { NULL, };
     104                 :            : 
     105                 :         13 :   object_class->constructed = mwt_period_constructed;
     106                 :         13 :   object_class->dispose = mwt_period_dispose;
     107                 :         13 :   object_class->get_property = mwt_period_get_property;
     108                 :         13 :   object_class->set_property = mwt_period_set_property;
     109                 :            : 
     110                 :            :   /**
     111                 :            :    * MwtPeriod:start:
     112                 :            :    *
     113                 :            :    * Date/Time when the period starts for the first time (inclusive).
     114                 :            :    *
     115                 :            :    * Since: 0.1.0
     116                 :            :    */
     117                 :         13 :   props[PROP_START] =
     118                 :         13 :       g_param_spec_boxed ("start", "Start Date/Time",
     119                 :            :                           "Date/Time when the period starts for the first time.",
     120                 :            :                           G_TYPE_DATE_TIME,
     121                 :            :                           G_PARAM_READWRITE |
     122                 :            :                           G_PARAM_CONSTRUCT_ONLY |
     123                 :            :                           G_PARAM_STATIC_STRINGS);
     124                 :            : 
     125                 :            :   /**
     126                 :            :    * MwtPeriod:end:
     127                 :            :    *
     128                 :            :    * Date/Time when the period ends for the first time (exclusive). Repeats of
     129                 :            :    * this period will occur after this date/time.
     130                 :            :    *
     131                 :            :    * Since: 0.1.0
     132                 :            :    */
     133                 :         13 :   props[PROP_END] =
     134                 :         13 :       g_param_spec_boxed ("end", "End Date/Time",
     135                 :            :                           "Date/Time when the period ends for the first time.",
     136                 :            :                           G_TYPE_DATE_TIME,
     137                 :            :                           G_PARAM_READWRITE |
     138                 :            :                           G_PARAM_CONSTRUCT_ONLY |
     139                 :            :                           G_PARAM_STATIC_STRINGS);
     140                 :            : 
     141                 :            :   /**
     142                 :            :    * MwtPeriod:repeat-type:
     143                 :            :    *
     144                 :            :    * How this period repeats (if at all).
     145                 :            :    *
     146                 :            :    * A more generalised way of repeating periods may be added in future,
     147                 :            :    * following the example of iCalendar’s `RRULE`.
     148                 :            :    *
     149                 :            :    * Since: 0.1.0
     150                 :            :    */
     151                 :         13 :   props[PROP_REPEAT_TYPE] =
     152                 :         13 :       g_param_spec_enum ("repeat-type", "Repeat Type",
     153                 :            :                          "How this period repeats (if at all).",
     154                 :            :                          MWT_TYPE_PERIOD_REPEAT_TYPE,
     155                 :            :                          MWT_PERIOD_REPEAT_NONE,
     156                 :            :                          G_PARAM_READWRITE |
     157                 :            :                          G_PARAM_CONSTRUCT_ONLY |
     158                 :            :                          G_PARAM_STATIC_STRINGS);
     159                 :            : 
     160                 :            :   /**
     161                 :            :    * MwtPeriod:repeat-period:
     162                 :            :    *
     163                 :            :    * The period between repeats of this period. For example, if
     164                 :            :    * #MwtPeriod:repeat-type was %MWT_PERIOD_REPEAT_HOUR and
     165                 :            :    * #MwtPeriod:repeat-period was 6, this period would repeat once every 6
     166                 :            :    * hours.
     167                 :            :    *
     168                 :            :    * Since: 0.1.0
     169                 :            :    */
     170                 :         13 :   props[PROP_REPEAT_PERIOD] =
     171                 :         13 :       g_param_spec_uint ("repeat-period", "Repeat Period",
     172                 :            :                          "The period between repeats of this period.",
     173                 :            :                          0, G_MAXUINT, 0,
     174                 :            :                          G_PARAM_READWRITE |
     175                 :            :                          G_PARAM_CONSTRUCT_ONLY |
     176                 :            :                          G_PARAM_STATIC_STRINGS);
     177                 :            : 
     178                 :            :   /**
     179                 :            :    * MwtPeriod:capacity-limit:
     180                 :            :    *
     181                 :            :    * Limit on the download capacity allowed during each repeat of this period,
     182                 :            :    * in bytes. If this is zero, no downloading is allowed during any repeat of
     183                 :            :    * this period. If it is %G_MAXUINT64, no limit is applied.
     184                 :            :    *
     185                 :            :    * The default is %G_MAXUINT64 (no limit).
     186                 :            :    *
     187                 :            :    * Since: 0.1.0
     188                 :            :    */
     189                 :         13 :   props[PROP_CAPACITY_LIMIT] =
     190                 :         13 :       g_param_spec_uint64 ("capacity-limit", "Capacity Limit",
     191                 :            :                            "Limit on the download capacity allowed during "
     192                 :            :                            "each repeat of this period, in bytes.",
     193                 :            :                            0, G_MAXUINT64, G_MAXUINT64,
     194                 :            :                            G_PARAM_READWRITE |
     195                 :            :                            G_PARAM_CONSTRUCT_ONLY |
     196                 :            :                            G_PARAM_STATIC_STRINGS);
     197                 :            : 
     198                 :         13 :   g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
     199                 :         13 : }
     200                 :            : 
     201                 :            : static void
     202                 :        142 : mwt_period_init (MwtPeriod *self)
     203                 :            : {
     204                 :            :   /* Nothing to do here. */
     205                 :        142 : }
     206                 :            : 
     207                 :            : static void
     208                 :        142 : mwt_period_constructed (GObject *object)
     209                 :            : {
     210                 :        142 :   MwtPeriod *self = MWT_PERIOD (object);
     211                 :            : 
     212                 :        142 :   G_OBJECT_CLASS (mwt_period_parent_class)->constructed (object);
     213                 :            : 
     214                 :            :   /* Validate properties. */
     215         [ -  + ]:        142 :   g_assert (mwt_period_validate (self->start, self->end, self->repeat_type,
     216                 :            :                                  self->repeat_period, NULL));
     217                 :        142 : }
     218                 :            : 
     219                 :            : static void
     220                 :        142 : mwt_period_dispose (GObject *object)
     221                 :            : {
     222                 :        142 :   MwtPeriod *self = MWT_PERIOD (object);
     223                 :            : 
     224         [ +  - ]:        142 :   g_clear_pointer (&self->start, g_date_time_unref);
     225         [ +  - ]:        142 :   g_clear_pointer (&self->end, g_date_time_unref);
     226                 :            : 
     227                 :            :   /* Chain up to the parent class */
     228                 :        142 :   G_OBJECT_CLASS (mwt_period_parent_class)->dispose (object);
     229                 :        142 : }
     230                 :            : 
     231                 :            : static void
     232                 :          5 : mwt_period_get_property (GObject    *object,
     233                 :            :                          guint       property_id,
     234                 :            :                          GValue     *value,
     235                 :            :                          GParamSpec *pspec)
     236                 :            : {
     237                 :          5 :   MwtPeriod *self = MWT_PERIOD (object);
     238                 :            : 
     239   [ +  +  +  +  :          5 :   switch ((MwtPeriodProperty) property_id)
                   +  - ]
     240                 :            :     {
     241                 :          1 :     case PROP_START:
     242                 :          1 :       g_value_set_boxed (value, self->start);
     243                 :          1 :       break;
     244                 :          1 :     case PROP_END:
     245                 :          1 :       g_value_set_boxed (value, self->end);
     246                 :          1 :       break;
     247                 :          1 :     case PROP_REPEAT_TYPE:
     248                 :          1 :       g_value_set_enum (value, self->repeat_type);
     249                 :          1 :       break;
     250                 :          1 :     case PROP_REPEAT_PERIOD:
     251                 :          1 :       g_value_set_uint (value, self->repeat_period);
     252                 :          1 :       break;
     253                 :          1 :     case PROP_CAPACITY_LIMIT:
     254                 :          1 :       g_value_set_uint64 (value, self->capacity_limit);
     255                 :          1 :       break;
     256                 :          0 :     default:
     257                 :          0 :       g_assert_not_reached ();
     258                 :            :     }
     259                 :          5 : }
     260                 :            : 
     261                 :            : static void
     262                 :        710 : mwt_period_set_property (GObject      *object,
     263                 :            :                          guint         property_id,
     264                 :            :                          const GValue *value,
     265                 :            :                          GParamSpec   *pspec)
     266                 :            : {
     267                 :        710 :   MwtPeriod *self = MWT_PERIOD (object);
     268                 :            : 
     269   [ +  +  +  +  :        710 :   switch ((MwtPeriodProperty) property_id)
                   +  - ]
     270                 :            :     {
     271                 :        142 :     case PROP_START:
     272                 :            :       /* Construct only. */
     273         [ -  + ]:        142 :       g_assert (self->start == NULL);
     274                 :        142 :       self->start = g_value_dup_boxed (value);
     275                 :        142 :       break;
     276                 :        142 :     case PROP_END:
     277                 :            :       /* Construct only. */
     278         [ -  + ]:        142 :       g_assert (self->end == NULL);
     279                 :        142 :       self->end = g_value_dup_boxed (value);
     280                 :        142 :       break;
     281                 :        142 :     case PROP_REPEAT_TYPE:
     282                 :            :       /* Construct only. */
     283         [ -  + ]:        142 :       g_assert (self->repeat_type == MWT_PERIOD_REPEAT_NONE);
     284                 :        142 :       self->repeat_type = g_value_get_enum (value);
     285                 :        142 :       break;
     286                 :        142 :     case PROP_REPEAT_PERIOD:
     287                 :            :       /* Construct only. */
     288         [ -  + ]:        142 :       g_assert (self->repeat_period == 0);
     289                 :        142 :       self->repeat_period = g_value_get_uint (value);
     290                 :        142 :       break;
     291                 :        142 :     case PROP_CAPACITY_LIMIT:
     292                 :            :       /* Construct only. */
     293         [ -  + ]:        142 :       g_assert (self->capacity_limit == 0);
     294                 :        142 :       self->capacity_limit = g_value_get_uint64 (value);
     295                 :        142 :       break;
     296                 :          0 :     default:
     297                 :          0 :       g_assert_not_reached ();
     298                 :            :     }
     299                 :        710 : }
     300                 :            : 
     301                 :            : /**
     302                 :            :  * mwt_period_validate:
     303                 :            :  * @start: (nullable): start date/time (see #MwtPeriod:start)
     304                 :            :  * @end: (nullable): end date/time (see #MwtPeriod:end)
     305                 :            :  * @repeat_type: repeat type (see #MwtPeriod:repeat-type)
     306                 :            :  * @repeat_period: repeat period (see #MwtPeriod:repeat-period)
     307                 :            :  * @error: return location for a #GError, or %NULL
     308                 :            :  *
     309                 :            :  * Validate the given #MwtPeriod properties, returning %MWT_PERIOD_ERROR_INVALID
     310                 :            :  * if any of them are invalid. All inputs are allowed to the property arguments
     311                 :            :  * (except @error): no inputs are a programmer error.
     312                 :            :  *
     313                 :            :  * It is guaranteed that if this function returns %TRUE for a given set of
     314                 :            :  * inputs, mwt_period_new() will succeed for those inputs.
     315                 :            :  *
     316                 :            :  * Returns: %TRUE if valid, %FALSE otherwise
     317                 :            :  * Since: 0.1.0
     318                 :            :  */
     319                 :            : gboolean
     320                 :        163 : mwt_period_validate (GDateTime            *start,
     321                 :            :                      GDateTime            *end,
     322                 :            :                      MwtPeriodRepeatType   repeat_type,
     323                 :            :                      guint                 repeat_period,
     324                 :            :                      GError              **error)
     325                 :            : {
     326   [ +  +  -  + ]:        163 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
     327                 :            : 
     328   [ +  +  +  + ]:        163 :   if (start == NULL ||
     329         [ +  + ]:        161 :       end == NULL ||
     330                 :        161 :       g_date_time_compare (start, end) >= 0)
     331                 :            :     {
     332                 :          3 :       g_set_error_literal (error, MWT_PERIOD_ERROR, MWT_PERIOD_ERROR_INVALID,
     333                 :            :                            _("Invalid start/end times for period."));
     334                 :          3 :       return FALSE;
     335                 :            :     }
     336                 :            : 
     337         [ +  - ]:        160 :   if (!((gint) repeat_type >= MWT_PERIOD_REPEAT_NONE &&
     338         [ +  + ]:        160 :         (gint) repeat_type <= MWT_PERIOD_REPEAT_YEAR))
     339                 :            :     {
     340                 :          1 :       g_set_error_literal (error, MWT_PERIOD_ERROR, MWT_PERIOD_ERROR_INVALID,
     341                 :            :                            _("Invalid repeat type for period."));
     342                 :          1 :       return FALSE;
     343                 :            :     }
     344                 :            : 
     345         [ +  + ]:        159 :   if ((repeat_type == MWT_PERIOD_REPEAT_NONE) != (repeat_period == 0))
     346                 :            :     {
     347                 :          2 :       g_set_error_literal (error, MWT_PERIOD_ERROR, MWT_PERIOD_ERROR_INVALID,
     348                 :            :                            _("Invalid repeat properties for period."));
     349                 :          2 :       return FALSE;
     350                 :            :     }
     351                 :            : 
     352                 :        157 :   return TRUE;
     353                 :            : }
     354                 :            : 
     355                 :            : /**
     356                 :            :  * mwt_period_new:
     357                 :            :  * @start: start date/time (see #MwtPeriod:start)
     358                 :            :  * @end: end date/time (see #MwtPeriod:end)
     359                 :            :  * @repeat_type: repeat type (see #MwtPeriod:repeat-type)
     360                 :            :  * @repeat_period: repeat period (see #MwtPeriod:repeat-period)
     361                 :            :  * @...: additional properties, given as property name followed by value, with
     362                 :            :  *    a %NULL terminator
     363                 :            :  *
     364                 :            :  * Create a #MwtPeriod object with the given properties. The @start, @end,
     365                 :            :  * @repeat_type and @repeat_period properties are required. The varargs can be
     366                 :            :  * used to specify the limits which apply to this period and which differ from
     367                 :            :  * their default values. The varargs are specified in the same format as used
     368                 :            :  * by g_object_new(), and the list must be %NULL terminated.
     369                 :            :  *
     370                 :            :  * Note that any 64-bit varargs must be cast to the correct type (for example,
     371                 :            :  * using G_GUINT64_CONSTANT()), or the wrong number of bytes will be put on
     372                 :            :  * the varargs list on non-64-bit architectures.
     373                 :            :  *
     374                 :            :  * All inputs to this function must have been validated with
     375                 :            :  * mwt_period_validate() first. It is a programmer error to provide invalid
     376                 :            :  * inputs.
     377                 :            :  *
     378                 :            :  * Returns: (transfer full): a new #MwtPeriod
     379                 :            :  * Since: 0.1.0
     380                 :            :  */
     381                 :            : MwtPeriod *
     382                 :        142 : mwt_period_new (GDateTime            *start,
     383                 :            :                 GDateTime            *end,
     384                 :            :                 MwtPeriodRepeatType   repeat_type,
     385                 :            :                 guint                 repeat_period,
     386                 :            :                 const gchar          *first_property_name,
     387                 :            :                 ...)
     388                 :            : {
     389                 :            :   /* Leave property validation to constructed(). */
     390                 :            : 
     391                 :            :   /* FIXME: This is mildly horrific. Do something clever with
     392                 :            :    * g_object_new_with_properties(). */
     393                 :        142 :   guint64 capacity_limit = G_MAXUINT64;
     394         [ +  + ]:        142 :   if (g_strcmp0 (first_property_name, "capacity-limit") == 0)
     395                 :            :     {
     396                 :            :       va_list args;
     397                 :         28 :       va_start (args, first_property_name);
     398                 :         28 :       capacity_limit = va_arg (args, guint64);
     399                 :         28 :       va_end (args);
     400                 :            :     }
     401                 :            : 
     402                 :        142 :   return g_object_new (MWT_TYPE_PERIOD,
     403                 :            :                        "start", start,
     404                 :            :                        "end", end,
     405                 :            :                        "repeat-type", repeat_type,
     406                 :            :                        "repeat-period", repeat_period,
     407                 :            :                        "capacity-limit", capacity_limit,
     408                 :            :                        NULL);
     409                 :            : }
     410                 :            : 
     411                 :            : /**
     412                 :            :  * mwt_period_get_start:
     413                 :            :  * @self: a #MwtPeriod
     414                 :            :  *
     415                 :            :  * Get the value of #MwtPeriod:start.
     416                 :            :  *
     417                 :            :  * Returns: start date/time (inclusive)
     418                 :            :  * Since: 0.1.0
     419                 :            :  */
     420                 :            : GDateTime *
     421                 :        547 : mwt_period_get_start (MwtPeriod *self)
     422                 :            : {
     423         [ -  + ]:        547 :   g_return_val_if_fail (MWT_IS_PERIOD (self), NULL);
     424                 :            : 
     425                 :        547 :   return self->start;
     426                 :            : }
     427                 :            : 
     428                 :            : /**
     429                 :            :  * mwt_period_get_end:
     430                 :            :  * @self: a #MwtPeriod
     431                 :            :  *
     432                 :            :  * Get the value of #MwtPeriod:end.
     433                 :            :  *
     434                 :            :  * Returns: end date/time (exclusive)
     435                 :            :  * Since: 0.1.0
     436                 :            :  */
     437                 :            : GDateTime *
     438                 :        534 : mwt_period_get_end (MwtPeriod *self)
     439                 :            : {
     440         [ -  + ]:        534 :   g_return_val_if_fail (MWT_IS_PERIOD (self), NULL);
     441                 :            : 
     442                 :        534 :   return self->end;
     443                 :            : }
     444                 :            : 
     445                 :            : /**
     446                 :            :  * mwt_period_get_repeat_type:
     447                 :            :  * @self: a #MwtPeriod
     448                 :            :  *
     449                 :            :  * Get the value of #MwtPeriod:repeat-type.
     450                 :            :  *
     451                 :            :  * Returns: repeat type
     452                 :            :  * Since: 0.1.0
     453                 :            :  */
     454                 :            : MwtPeriodRepeatType
     455                 :         25 : mwt_period_get_repeat_type (MwtPeriod *self)
     456                 :            : {
     457         [ -  + ]:         25 :   g_return_val_if_fail (MWT_IS_PERIOD (self), MWT_PERIOD_REPEAT_NONE);
     458                 :            : 
     459   [ +  +  -  + ]:         25 :   g_assert (self->repeat_type != MWT_PERIOD_REPEAT_NONE ||
     460                 :            :             self->repeat_period == 0);
     461                 :            : 
     462                 :         25 :   return self->repeat_type;
     463                 :            : }
     464                 :            : 
     465                 :            : /**
     466                 :            :  * mwt_period_get_repeat_period:
     467                 :            :  * @self: a #MwtPeriod
     468                 :            :  *
     469                 :            :  * Get the value of #MwtPeriod:repeat-period.
     470                 :            :  *
     471                 :            :  * Returns: repeat period
     472                 :            :  * Since: 0.1.0
     473                 :            :  */
     474                 :            : guint
     475                 :         25 : mwt_period_get_repeat_period (MwtPeriod *self)
     476                 :            : {
     477         [ -  + ]:         25 :   g_return_val_if_fail (MWT_IS_PERIOD (self), 0);
     478                 :            : 
     479   [ +  +  -  + ]:         25 :   g_assert (self->repeat_period != 0 ||
     480                 :            :             self->repeat_type == MWT_PERIOD_REPEAT_NONE);
     481                 :            : 
     482                 :         25 :   return self->repeat_period;
     483                 :            : }
     484                 :            : 
     485                 :            : /**
     486                 :            :  * mwt_period_get_capacity_limit:
     487                 :            :  * @self: a #MwtPeriod
     488                 :            :  *
     489                 :            :  * Get the value of #MwtPeriod:capacity-limit.
     490                 :            :  *
     491                 :            :  * Returns: capacity limit, in bytes
     492                 :            :  * Since: 0.1.0
     493                 :            :  */
     494                 :            : guint64
     495                 :        120 : mwt_period_get_capacity_limit (MwtPeriod *self)
     496                 :            : {
     497         [ -  + ]:        120 :   g_return_val_if_fail (MWT_IS_PERIOD (self), 0);
     498                 :            : 
     499                 :        120 :   return self->capacity_limit;
     500                 :            : }
     501                 :            : 
     502                 :            : /* Add @n repeat periods to #MwtPeriod:start and #MwtPeriod:end, and return a
     503                 :            :  * new #GDateTime for each of them. @n must be positive.
     504                 :            :  *
     505                 :            :  * @out_start and @out_end are both (inout), ignoring and freeing whatever old
     506                 :            :  * value is passed in, and returning the result.
     507                 :            :  *
     508                 :            :  * If either of the dates could not be updated (if we’ve hit the limits in the
     509                 :            :  * date type, for example), %FALSE will be returned and @out_start and @out_end
     510                 :            :  * will be set to %NULL.
     511                 :            :  *
     512                 :            :  * The #MwtPeriod:repeat-type must not be %MWT_PERIOD_REPEAT_NONE, and the
     513                 :            :  * #MwtPeriod:repeat-period must not be zero.
     514                 :            :  *
     515                 :            :  * @out_was_empty is set to %TRUE (and %FALSE is returned) if the nth recurrence
     516                 :            :  * of the period was empty (for example, due to DST adjustments). See
     517                 :            :  * get_nth_recurrence_skip_empty() for the recommended way to handle this.
     518                 :            :  */
     519                 :            : static gboolean
     520                 :       1767 : get_nth_recurrence (MwtPeriod  *self,
     521                 :            :                     guint64     n,
     522                 :            :                     GDateTime **out_start,
     523                 :            :                     GDateTime **out_end,
     524                 :            :                     gboolean   *out_was_empty)
     525                 :            : {
     526                 :       1767 :   g_autoptr(GDateTime) new_start = NULL;
     527                 :       3534 :   g_autoptr(GDateTime) new_end = NULL;
     528                 :            : 
     529         [ -  + ]:       1767 :   g_assert (self->repeat_period != 0);
     530         [ -  + ]:       1767 :   g_assert (n != 0);
     531                 :            : 
     532         [ +  + ]:       1767 :   if (n > G_MAXINT / self->repeat_period)
     533                 :          1 :     goto done;
     534                 :            : 
     535                 :       1766 :   gint addand = self->repeat_period * n;
     536                 :            : 
     537   [ +  +  +  +  :       1766 :   switch (self->repeat_type)
                   +  - ]
     538                 :            :     {
     539                 :         14 :     case MWT_PERIOD_REPEAT_HOUR:
     540                 :         14 :       new_start = g_date_time_add_hours (self->start, addand);
     541                 :         14 :       new_end = g_date_time_add_hours (self->end, addand);
     542                 :         14 :       break;
     543                 :       1372 :     case MWT_PERIOD_REPEAT_DAY:
     544                 :       1372 :       new_start = g_date_time_add_days (self->start, addand);
     545                 :       1372 :       new_end = g_date_time_add_days (self->end, addand);
     546                 :       1372 :       break;
     547                 :        197 :     case MWT_PERIOD_REPEAT_WEEK:
     548                 :        197 :       new_start = g_date_time_add_weeks (self->start, addand);
     549                 :        197 :       new_end = g_date_time_add_weeks (self->end, addand);
     550                 :        197 :       break;
     551                 :        116 :     case MWT_PERIOD_REPEAT_MONTH:
     552                 :        116 :       new_start = g_date_time_add_months (self->start, addand);
     553                 :        116 :       new_end = g_date_time_add_months (self->end, addand);
     554                 :        116 :       break;
     555                 :         67 :     case MWT_PERIOD_REPEAT_YEAR:
     556                 :         67 :       new_start = g_date_time_add_years (self->start, addand);
     557                 :         67 :       new_end = g_date_time_add_years (self->end, addand);
     558                 :         67 :       break;
     559                 :          0 :     case MWT_PERIOD_REPEAT_NONE:
     560                 :            :       /* Must be handled by the caller. */
     561                 :            :     default:
     562                 :          0 :       g_assert_not_reached ();
     563                 :            :     }
     564                 :            : 
     565                 :       1767 : done:
     566                 :            :   /* We must not return one date without the other. If one, but not both, of the
     567                 :            :    * g_date_time_add_*() calls failed, squash the other result. */
     568   [ +  +  +  + ]:       1767 :   if (new_start == NULL || new_end == NULL)
     569                 :            :     {
     570         [ +  + ]:         14 :       g_clear_pointer (&new_start, g_date_time_unref);
     571         [ -  + ]:         14 :       g_clear_pointer (&new_end, g_date_time_unref);
     572                 :            :     }
     573                 :            : 
     574                 :       1767 :   gboolean was_empty = FALSE;
     575                 :            : 
     576   [ +  +  +  -  :       3520 :   if (new_start != NULL && new_end != NULL &&
                   +  + ]
     577                 :       1753 :       g_date_time_equal (new_start, new_end))
     578                 :            :     {
     579         [ +  - ]:         16 :       g_clear_pointer (&new_start, g_date_time_unref);
     580         [ +  - ]:         16 :       g_clear_pointer (&new_end, g_date_time_unref);
     581                 :         16 :       was_empty = TRUE;
     582                 :            :     }
     583                 :            : 
     584   [ +  +  +  -  :       1767 :   g_assert (new_start == NULL || new_end == NULL ||
                   -  + ]
     585                 :            :             g_date_time_compare (new_start, new_end) < 0);
     586   [ +  +  +  -  :       1767 :   g_assert (!was_empty || (new_start == NULL && new_end == NULL));
                   +  - ]
     587                 :            : 
     588         [ +  + ]:       1767 :   g_clear_pointer (out_start, g_date_time_unref);
     589         [ +  + ]:       1767 :   g_clear_pointer (out_end, g_date_time_unref);
     590                 :       1767 :   *out_start = g_steal_pointer (&new_start);
     591                 :       1767 :   *out_end = g_steal_pointer (&new_end);
     592                 :       1767 :   *out_was_empty = was_empty;
     593                 :            : 
     594   [ +  +  +  - ]:       1767 :   return (*out_start != NULL && *out_end != NULL);
     595                 :            : }
     596                 :            : 
     597                 :            : /* Version of get_nth_recurrence() which skips over empty recurrences.
     598                 :            :  * @inout_n_skipped_periods must be provided, as the number of recurrences which
     599                 :            :  * have already been skipped; it will be updated to include any additional skips
     600                 :            :  * from this function call. Otherwise, semantics are the same as
     601                 :            :  * get_nth_recurrence(). */
     602                 :            : static gboolean
     603                 :       1751 : get_nth_recurrence_skip_empty (MwtPeriod  *self,
     604                 :            :                                guint64     n,
     605                 :            :                                GDateTime **out_start,
     606                 :            :                                GDateTime **out_end,
     607                 :            :                                guint64    *inout_n_skipped_periods)
     608                 :            : {
     609                 :            :   gboolean retval;
     610                 :            :   gboolean was_empty;
     611                 :       1751 :   guint64 n_skipped_periods = *inout_n_skipped_periods;
     612                 :            : 
     613                 :            :   do
     614                 :            :     {
     615                 :            :       /* @n_skipped_periods should never be able to overflow, since a guint64
     616                 :            :        * can store every second in the range of #GDateTime. */
     617         [ -  + ]:       1767 :       g_assert (n_skipped_periods <= G_MAXUINT64 - n);
     618                 :            : 
     619                 :       1767 :       retval = get_nth_recurrence (self, n + n_skipped_periods,
     620                 :            :                                    out_start, out_end, &was_empty);
     621         [ +  + ]:       1767 :       if (was_empty)
     622                 :         16 :         n_skipped_periods++;
     623                 :            :     }
     624         [ +  + ]:       1767 :   while (was_empty);
     625                 :            : 
     626         [ +  + ]:       1751 :   g_debug ("%s: returning %s, n_skipped_periods: %" G_GUINT64_FORMAT " → %" G_GUINT64_FORMAT,
     627                 :            :            G_STRFUNC, retval ? "yes" : "no", *inout_n_skipped_periods, n_skipped_periods);
     628                 :            : 
     629                 :       1751 :   *inout_n_skipped_periods = n_skipped_periods;
     630                 :       1751 :   return retval;
     631                 :            : }
     632                 :            : 
     633                 :            : /**
     634                 :            :  * get_nearest_recurrences:
     635                 :            :  * @self: a #MwtPeriod
     636                 :            :  * @when: the time to check
     637                 :            :  * @out_contains_start: (optional) (nullable) (out) (transfer full): return
     638                 :            :  *    location for the start time of the recurrence containing @when
     639                 :            :  * @out_contains_end: (optional) (nullable) (out) (transfer full): return
     640                 :            :  *    location for the end time of the recurrence containing @when
     641                 :            :  * @out_next_start: (optional) (nullable) (out) (transfer full): return
     642                 :            :  *    location for the start time of the recurrence after @when
     643                 :            :  * @out_next_end: (optional) (nullable) (out) (transfer full): return
     644                 :            :  *    location for the end time of the recurrence after @when
     645                 :            :  *
     646                 :            :  * Get the recurrence of the #MwtPeriod which contains @when, and the next
     647                 :            :  * recurrence after that. Either or both of these may be %NULL:
     648                 :            :  *
     649                 :            :  *  - The recurrence containing @when may be %NULL if no recurrence of the
     650                 :            :  *    period actually contains @when.
     651                 :            :  *  - The next recurrence may be %NULL if the #MwtPeriod:repeat-type is
     652                 :            :  *    %MWT_PERIOD_REPEAT_NONE.
     653                 :            :  *  - Or if the next recurrence would exceed the limits of #GDateTime (the year
     654                 :            :  *    9999).
     655                 :            :  *
     656                 :            :  * If a recurrence is returned, both the start and end #GDateTimes are
     657                 :            :  * guaranteed to be non-%NULL. The start and end times are guaranteed to be
     658                 :            :  * in order, and non-equal. If a recurrence of the period is empty due to
     659                 :            :  * falling in DST transition, it is skipped and the next non-empty recurrence is
     660                 :            :  * returned.
     661                 :            :  */
     662                 :            : static void
     663                 :       1032 : get_nearest_recurrences (MwtPeriod  *self,
     664                 :            :                          GDateTime  *when,
     665                 :            :                          GDateTime **out_contains_start,
     666                 :            :                          GDateTime **out_contains_end,
     667                 :            :                          GDateTime **out_next_start,
     668                 :            :                          GDateTime **out_next_end)
     669                 :            : {
     670                 :       1032 :   g_autoptr(GDateTime) retval_contains_start = NULL;
     671                 :       1032 :   g_autoptr(GDateTime) retval_contains_end = NULL;
     672                 :       1032 :   g_autoptr(GDateTime) retval_next_start = NULL;
     673                 :       1032 :   g_autoptr(GDateTime) retval_next_end = NULL;
     674                 :            : 
     675                 :            :   /* We have to allocate these here to avoid memory corruption due to the
     676                 :            :    * combination of g_autoptr() and goto. This is unfortunate, since the goto
     677                 :            :    * in this function is fairly useful.
     678                 :            :    * See: https://blog.fishsoup.net/2015/11/05/attributecleanup-mixed-declarations-and-code-and-goto/ */
     679                 :       1032 :   g_autoptr(GDateTime) start = NULL;
     680                 :       1032 :   g_autoptr(GDateTime) end = NULL;
     681                 :            : 
     682                 :       1032 :   guint64 n_skipped_periods = 0;
     683                 :            : 
     684                 :            :   /* Get the base time if @when is %NULL, or if @when is before the base start
     685                 :            :    * time. */
     686   [ +  +  +  + ]:       1032 :   if (when == NULL || g_date_time_compare (when, self->start) < 0)
     687                 :            :     {
     688                 :         73 :       retval_next_start = g_date_time_ref (self->start);
     689                 :         73 :       retval_next_end = g_date_time_ref (self->end);
     690                 :         73 :       goto done;
     691                 :            :     }
     692                 :            : 
     693                 :            :   /* We can assume this from this point onwards. */
     694         [ -  + ]:        959 :   g_assert (when != NULL);
     695                 :            : 
     696                 :            :   /* Does the base time for the period contain @when? */
     697   [ +  -  +  + ]:       1918 :   if (g_date_time_compare (self->start, when) <= 0 &&
     698                 :        959 :       g_date_time_compare (when, self->end) < 0)
     699                 :            :     {
     700                 :        124 :       retval_contains_start = g_date_time_ref (self->start);
     701                 :        124 :       retval_contains_end = g_date_time_ref (self->end);
     702                 :            : 
     703         [ +  + ]:        124 :       if (self->repeat_type != MWT_PERIOD_REPEAT_NONE)
     704                 :            :         {
     705                 :            :           /* Failure here will result in (@retval_next_start == retval_next_end == NULL),
     706                 :            :            * which is what we want. */
     707                 :         85 :           get_nth_recurrence_skip_empty (self, 1,
     708                 :            :                                          &retval_next_start, &retval_next_end, &n_skipped_periods);
     709                 :            :         }
     710                 :            : 
     711                 :        124 :       goto done;
     712                 :            :     }
     713                 :            : 
     714                 :            :   /* Do recurrences happen at all? */
     715         [ +  + ]:        835 :   if (self->repeat_type == MWT_PERIOD_REPEAT_NONE ||
     716         [ -  + ]:        816 :       self->repeat_period == 0)
     717                 :         19 :     goto done;
     718                 :            : 
     719                 :            :   /* Firstly, work out a lower bound on the number of periods which could have
     720                 :            :    * elapsed between @self->start and @when. We can use this to jump ahead to
     721                 :            :    * roughly when the most appropriate recurrence could happen to contain @when,
     722                 :            :    * avoiding a load of iterations and #GDateTime allocations. */
     723                 :            :   GTimeSpan max_period_span;
     724                 :            : 
     725   [ +  +  +  +  :        816 :   switch (self->repeat_type)
                   +  - ]
     726                 :            :     {
     727                 :          6 :     case MWT_PERIOD_REPEAT_HOUR:
     728                 :          6 :       max_period_span = G_TIME_SPAN_HOUR;
     729                 :          6 :       break;
     730                 :        689 :     case MWT_PERIOD_REPEAT_DAY:
     731                 :        689 :       max_period_span = G_TIME_SPAN_DAY;
     732                 :        689 :       break;
     733                 :         84 :     case MWT_PERIOD_REPEAT_WEEK:
     734                 :         84 :       max_period_span = G_TIME_SPAN_DAY * 7;
     735                 :         84 :       break;
     736                 :         24 :     case MWT_PERIOD_REPEAT_MONTH:
     737                 :            :       /* The longest month has 31 days. Go for 32 days just in case there’s a
     738                 :            :        * DST transition during the month (lengthening it by 1 hour). */
     739                 :         24 :       max_period_span = G_TIME_SPAN_DAY * 32;
     740                 :         24 :       break;
     741                 :         13 :     case MWT_PERIOD_REPEAT_YEAR:
     742                 :            :       /* A year is typically 365 days. Go for 367 just in case of added time
     743                 :            :        * in DST transitions or it being a leap year. */
     744                 :         13 :       max_period_span = G_TIME_SPAN_DAY * 367;
     745                 :         13 :       break;
     746                 :          0 :     case MWT_PERIOD_REPEAT_NONE:
     747                 :            :       /* Handled above. */
     748                 :            :     default:
     749                 :          0 :       g_assert_not_reached ();
     750                 :            :     }
     751                 :            : 
     752                 :        816 :   GTimeSpan diff = g_date_time_difference (when, self->start);
     753         [ -  + ]:        816 :   g_assert (diff >= 0);
     754   [ +  -  +  - ]:        816 :   g_assert (max_period_span != 0 && self->repeat_period != 0);
     755                 :        816 :   guint64 min_n_periods = ((diff / max_period_span) / self->repeat_period);
     756                 :            : 
     757                 :        816 :   g_debug ("%s: diff: %" G_GINT64_FORMAT ", min_n_periods: %" G_GUINT64_FORMAT,
     758                 :            :            G_STRFUNC, diff, min_n_periods);
     759                 :            : 
     760                 :        816 :   start = g_date_time_ref (self->start);
     761                 :        816 :   end = g_date_time_ref (self->end);
     762                 :            : 
     763   [ +  +  +  + ]:       1552 :   if (min_n_periods > 0 &&
     764                 :        736 :       !get_nth_recurrence_skip_empty (self, min_n_periods,
     765                 :            :                                       &start, &end, &n_skipped_periods))
     766                 :          2 :     goto done;
     767                 :            : 
     768                 :            :   /* Add periods individually until we either match or overshoot. We need to be
     769                 :            :    * careful to always add from the initial date, as addition to dates is not
     770                 :            :    * transitive. For example, if a period spans 01:00 to 02:00 one week, and a
     771                 :            :    * DST transition happens the next week where the clocks go forward by 1h at
     772                 :            :    * 01:00, adding 1 week to the initial @start/@end date/times will produce
     773                 :            :    * times of 02:00 to 02:00, not 01:00 to 02:00; adding 1 week more to those
     774                 :            :    * date/times will not undo the error. However, adding 2 weeks from the
     775                 :            :    * original date/times will avoid the times getting adjusted to avoid the
     776                 :            :    * missing DST time at all; the errors will not compound. */
     777         [ +  + ]:       1235 :   for (guint64 i = 1; g_date_time_compare (start, when) <= 0; i++)
     778                 :            :     {
     779                 :            :       /* @min_n_periods + @i can never really overflow, given that a guint64 can
     780                 :            :        * represent every second in the range of a #GDateTime, and the minimum
     781                 :            :        * interval we operate in is hours. */
     782         [ -  + ]:        930 :       g_assert (i <= G_MAXUINT64 - min_n_periods);
     783                 :            : 
     784                 :            :       /* Match now? */
     785   [ +  -  +  + ]:       1860 :       if (g_date_time_compare (start, when) <= 0 &&
     786                 :        930 :           g_date_time_compare (when, end) < 0)
     787                 :            :         {
     788                 :        502 :           retval_contains_start = g_date_time_ref (start);
     789                 :        502 :           retval_contains_end = g_date_time_ref (end);
     790                 :            : 
     791                 :            :           /* Failure here will result in (@retval_next_start == retval_next_end == NULL),
     792                 :            :            * which is what we want. */
     793                 :        502 :           get_nth_recurrence_skip_empty (self, min_n_periods + i,
     794                 :            :                                          &retval_next_start, &retval_next_end, &n_skipped_periods);
     795                 :            : 
     796                 :        502 :           goto done;
     797                 :            :         }
     798                 :            : 
     799         [ +  + ]:        428 :       if (!get_nth_recurrence_skip_empty (self, min_n_periods + i,
     800                 :            :                                           &start, &end, &n_skipped_periods))
     801                 :          7 :         goto done;
     802                 :            :     }
     803                 :            : 
     804                 :            :   /* If we’ve reached this point, we have (@start > @when), so there is no
     805                 :            :    * recurrence which contains @when, but we should return the start and end
     806                 :            :    * times of the next recurrence. */
     807         [ -  + ]:        305 :   g_assert (g_date_time_compare (start, when) > 0);
     808         [ -  + ]:        305 :   g_assert (retval_contains_start == NULL);
     809         [ -  + ]:        305 :   g_assert (retval_contains_end == NULL);
     810                 :        305 :   retval_next_start = g_date_time_ref (start);
     811                 :        305 :   retval_next_end = g_date_time_ref (end);
     812                 :        305 :   goto done;
     813                 :            : 
     814                 :       1032 : done:
     815         [ -  + ]:       1032 :   g_assert ((retval_contains_start == NULL) == (retval_contains_end == NULL));
     816         [ -  + ]:       1032 :   g_assert ((retval_next_start == NULL) == (retval_next_end == NULL));
     817   [ +  +  +  -  :       1032 :   g_assert (retval_contains_start == NULL || when == NULL ||
                   -  + ]
     818                 :            :             g_date_time_compare (retval_contains_start, when) <= 0);
     819   [ +  +  +  -  :       1032 :   g_assert (retval_contains_end == NULL || when == NULL ||
                   -  + ]
     820                 :            :             g_date_time_compare (retval_contains_end, when) > 0);
     821   [ +  +  +  +  :       1032 :   g_assert (retval_next_start == NULL || when == NULL ||
                   -  + ]
     822                 :            :             g_date_time_compare (retval_next_start, when) > 0);
     823   [ +  +  +  +  :       1032 :   g_assert (retval_next_end == NULL || when == NULL ||
                   -  + ]
     824                 :            :             g_date_time_compare (retval_next_end, when) > 0);
     825   [ +  +  +  -  :       1032 :   g_assert (retval_contains_start == NULL || retval_contains_end == NULL ||
                   -  + ]
     826                 :            :             g_date_time_compare (retval_contains_start, retval_contains_end) < 0);
     827   [ +  +  +  -  :       1032 :   g_assert (retval_next_start == NULL || retval_next_end == NULL ||
                   -  + ]
     828                 :            :             g_date_time_compare (retval_next_start, retval_next_end) < 0);
     829   [ +  +  -  + ]:       1032 :   g_assert (when != NULL || retval_contains_start == NULL);
     830   [ +  +  -  + ]:       1032 :   g_assert (when != NULL || retval_contains_end == NULL);
     831   [ +  +  -  + ]:       1032 :   g_assert (when != NULL || retval_next_start == self->start);
     832   [ +  +  -  + ]:       1032 :   g_assert (when != NULL || retval_next_end == self->end);
     833                 :            : 
     834         [ +  + ]:       1032 :   if (out_contains_start != NULL)
     835                 :        851 :     *out_contains_start = g_steal_pointer (&retval_contains_start);
     836         [ +  + ]:       1032 :   if (out_contains_end != NULL)
     837                 :        851 :     *out_contains_end = g_steal_pointer (&retval_contains_end);
     838         [ +  + ]:       1032 :   if (out_next_start != NULL)
     839                 :        181 :     *out_next_start = g_steal_pointer (&retval_next_start);
     840         [ +  + ]:       1032 :   if (out_next_end != NULL)
     841                 :        181 :     *out_next_end = g_steal_pointer (&retval_next_end);
     842                 :       1032 : }
     843                 :            : 
     844                 :            : /**
     845                 :            :  * mwt_period_contains_time:
     846                 :            :  * @self: a #MwtPeriod
     847                 :            :  * @when: the time to check
     848                 :            :  * @out_start: (optional) (out) (transfer full) (nullable): return location for
     849                 :            :  *    the start time of the recurrence which contains @when
     850                 :            :  * @out_end: (optional) (out) (transfer full) (nullable): return location for
     851                 :            :  *    the end time of the recurrence which contains @when
     852                 :            :  *
     853                 :            :  * Check whether @when lies within the given #MwtPeriod or any of its
     854                 :            :  * recurrences. If it does, @out_start and @out_end will (if provided) will be
     855                 :            :  * set to the start and end times of the recurrence which contains @when.
     856                 :            :  *
     857                 :            :  * If @when does not fall within a recurrence of the #MwtPeriod, @out_start
     858                 :            :  * and @out_end will be set to %NULL.
     859                 :            :  *
     860                 :            :  * Returns: %TRUE if @when lies in the period, %FALSE otherwise
     861                 :            :  * Since: 0.1.0
     862                 :            :  */
     863                 :            : gboolean
     864                 :        851 : mwt_period_contains_time (MwtPeriod  *self,
     865                 :            :                           GDateTime  *when,
     866                 :            :                           GDateTime **out_start,
     867                 :            :                           GDateTime **out_end)
     868                 :            : {
     869         [ -  + ]:        851 :   g_return_val_if_fail (MWT_IS_PERIOD (self), FALSE);
     870         [ -  + ]:        851 :   g_return_val_if_fail (when != NULL, FALSE);
     871                 :            : 
     872                 :        851 :   g_autoptr(GDateTime) retval_start = NULL;
     873                 :       1702 :   g_autoptr(GDateTime) retval_end = NULL;
     874                 :            : 
     875                 :            :   /* Get the recurrence which contains @when, then. If no recurrence contains
     876                 :            :    * @when, @retval_start and @retval_end will be set to %NULL. */
     877                 :        851 :   get_nearest_recurrences (self, when, &retval_start, &retval_end, NULL, NULL);
     878                 :            : 
     879   [ +  +  +  - ]:        851 :   gboolean retval = (retval_start != NULL && retval_end != NULL);
     880                 :            : 
     881         [ +  + ]:        851 :   if (out_start != NULL)
     882                 :         96 :     *out_start = g_steal_pointer (&retval_start);
     883         [ +  + ]:        851 :   if (out_end != NULL)
     884                 :        355 :     *out_end = g_steal_pointer (&retval_end);
     885                 :            : 
     886                 :        851 :   return retval;
     887                 :            : }
     888                 :            : 
     889                 :            : /**
     890                 :            :  * mwt_period_get_next_recurrence:
     891                 :            :  * @self: a #MwtPeriod
     892                 :            :  * @after: (nullable): time to get the next recurrence after
     893                 :            :  * @out_next_start: (out) (optional) (nullable) (transfer full): return location
     894                 :            :  *    for the start time of the next recurrence after @after
     895                 :            :  * @out_next_end: (out) (optional) (nullable) (transfer full): return location
     896                 :            :  *    for the end time of the next recurrence after @after
     897                 :            :  *
     898                 :            :  * Get the start and end time of the first recurrence of #MwtPeriod with a start
     899                 :            :  * time greater than @after. If @after is %NULL, this will be the base start and
     900                 :            :  * end time of the #MwtPeriod.
     901                 :            :  *
     902                 :            :  * If #MwtPeriod:repeat-type is %MWT_PERIOD_REPEAT_NONE, and @after is
     903                 :            :  * non-%NULL, @out_next_start and @out_next_end will be set to %NULL and %FALSE
     904                 :            :  * will be returned.
     905                 :            :  *
     906                 :            :  * If the first recurrence after @after exceeds the limits of #GDateTime (the
     907                 :            :  * end of the year 9999), @out_next_start and @out_next_end will be set to %NULL
     908                 :            :  * and %FALSE will be returned.
     909                 :            :  *
     910                 :            :  * If a recurrence is returned, both @out_next_start and @out_next_end are
     911                 :            :  * guaranteed to be non-%NULL (if provided). The returned recurrence is
     912                 :            :  * guaranteed to be non-empty: if the next recurrence after @after would be
     913                 :            :  * empty due to a DST transition, the first following non-empty recurrence will
     914                 :            :  * be returned.
     915                 :            :  *
     916                 :            :  * Returns: %TRUE if a next recurrence was found, %FALSE otherwise
     917                 :            :  * Since: 0.1.0
     918                 :            :  */
     919                 :            : gboolean
     920                 :        181 : mwt_period_get_next_recurrence (MwtPeriod  *self,
     921                 :            :                                 GDateTime  *after,
     922                 :            :                                 GDateTime **out_next_start,
     923                 :            :                                 GDateTime **out_next_end)
     924                 :            : {
     925         [ -  + ]:        181 :   g_return_val_if_fail (MWT_IS_PERIOD (self), FALSE);
     926                 :            : 
     927                 :        181 :   g_autoptr(GDateTime) retval_next_start = NULL;
     928                 :        362 :   g_autoptr(GDateTime) retval_next_end = NULL;
     929                 :            : 
     930                 :            :   /* Get the recurrence which contains @when, then. If no recurrence contains
     931                 :            :    * @when, @retval_start and @retval_end will be set to %NULL. */
     932                 :        181 :   get_nearest_recurrences (self, after, NULL, NULL, &retval_next_start, &retval_next_end);
     933                 :            : 
     934   [ +  +  +  - ]:        181 :   gboolean retval = (retval_next_start != NULL && retval_next_end != NULL);
     935                 :            : 
     936         [ +  - ]:        181 :   if (out_next_start != NULL)
     937                 :        181 :     *out_next_start = g_steal_pointer (&retval_next_start);
     938         [ +  + ]:        181 :   if (out_next_end != NULL)
     939                 :         96 :     *out_next_end = g_steal_pointer (&retval_next_end);
     940                 :            : 
     941                 :        181 :   return retval;
     942                 :            : }

Generated by: LCOV version 1.16