mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-02 07:23:41 +02:00
gdate: Add week-of-year APIs for weeks starting on any day
`GDate` currently has some API which is specific to which day of the week you think the week starts on: * `g_date_get_monday_week_of_year()` * `g_date_get_monday_weeks_in_year()` * `g_date_get_sunday_week_of_year()` * `g_date_get_sunday_weeks_in_year()` This is to deal with the difference between locales which think that the week starts on a Sunday (such as `LANG=en_US.utf8` or `LANG=he_IL.utf8`), or a Monday (such as `LANG=en_GB.utf8`). However, there are some locales which think that the week starts on a Saturday (such as `LANG=ar_EG.utf8`). Currently, GLib provides no API for those. So, add some API which is parameterised by the first day of the week, which will deal with weeks which start on a Saturday (and also any other day of the week, although I don’t believe there are any countries which use a day other than Saturday, Sunday or Monday). Signed-off-by: Philip Withnall <pwithnall@gnome.org> Fixes: #3617
This commit is contained in:
147
glib/gdate.c
147
glib/gdate.c
@@ -742,27 +742,9 @@ g_date_get_day_of_year (const GDate *d)
|
||||
* Returns: week of the year
|
||||
*/
|
||||
guint
|
||||
g_date_get_monday_week_of_year (const GDate *d)
|
||||
g_date_get_monday_week_of_year (const GDate *date)
|
||||
{
|
||||
GDateWeekday wd;
|
||||
guint day;
|
||||
GDate first;
|
||||
|
||||
g_return_val_if_fail (g_date_valid (d), 0);
|
||||
|
||||
if (!d->dmy)
|
||||
g_date_update_dmy (d);
|
||||
|
||||
g_return_val_if_fail (d->dmy, 0);
|
||||
|
||||
g_date_clear (&first, 1);
|
||||
|
||||
g_date_set_dmy (&first, 1, 1, d->year);
|
||||
|
||||
wd = g_date_get_weekday (&first) - 1; /* make Monday day 0 */
|
||||
day = g_date_get_day_of_year (d) - 1;
|
||||
|
||||
return ((day + wd)/7U + (wd == 0 ? 1 : 0));
|
||||
return g_date_get_week_of_year (date, G_DATE_MONDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -776,28 +758,52 @@ g_date_get_monday_week_of_year (const GDate *d)
|
||||
* Returns: week number
|
||||
*/
|
||||
guint
|
||||
g_date_get_sunday_week_of_year (const GDate *d)
|
||||
g_date_get_sunday_week_of_year (const GDate *date)
|
||||
{
|
||||
GDateWeekday wd;
|
||||
guint day;
|
||||
GDate first;
|
||||
|
||||
g_return_val_if_fail (g_date_valid (d), 0);
|
||||
|
||||
if (!d->dmy)
|
||||
g_date_update_dmy (d);
|
||||
return g_date_get_week_of_year (date, G_DATE_SUNDAY);
|
||||
}
|
||||
|
||||
g_return_val_if_fail (d->dmy, 0);
|
||||
|
||||
g_date_clear (&first, 1);
|
||||
|
||||
g_date_set_dmy (&first, 1, 1, d->year);
|
||||
|
||||
wd = g_date_get_weekday (&first);
|
||||
if (wd == 7) wd = 0; /* make Sunday day 0 */
|
||||
day = g_date_get_day_of_year (d) - 1;
|
||||
|
||||
return ((day + wd)/7U + (wd == 0 ? 1 : 0));
|
||||
/**
|
||||
* g_date_get_week_of_year:
|
||||
* @date: a [struct@GLib.Date]
|
||||
* @first_day_of_week: the day which is considered the first day of the week
|
||||
* (for example, this would be [enum@GLib.DateWeekday.SUNDAY] in US locales,
|
||||
* [enum@GLib.DateWeekday.MONDAY] in British locales, and
|
||||
* [enum@GLib.DateWeekday.SATURDAY] in Egyptian locales
|
||||
*
|
||||
* Calculates the week of the year during which this date falls.
|
||||
*
|
||||
* The result depends on which day is considered the first day of the week,
|
||||
* which varies by locale. Both `date` and `first_day_of_week` must be valid.
|
||||
*
|
||||
* If @date is before the start of the first week of the year (for example,
|
||||
* before the first Monday in January if @first_day_of_week is
|
||||
* [enum@GLib.DateWeekday.MONDAY]) then zero will be returned.
|
||||
*
|
||||
* Returns: week number (starting from 1), or `0` if @date is before the start
|
||||
* of the first week of the year
|
||||
* Since: 2.86
|
||||
*/
|
||||
unsigned int
|
||||
g_date_get_week_of_year (const GDate *date,
|
||||
GDateWeekday first_day_of_week)
|
||||
{
|
||||
GDate first_day_of_year;
|
||||
unsigned int n_days_before_first_week;
|
||||
|
||||
g_return_val_if_fail (g_date_valid (date), 0);
|
||||
g_return_val_if_fail (first_day_of_week != G_DATE_BAD_WEEKDAY, 0);
|
||||
|
||||
if (!date->dmy)
|
||||
g_date_update_dmy (date);
|
||||
|
||||
g_return_val_if_fail (date->dmy, 0);
|
||||
|
||||
g_date_clear (&first_day_of_year, 1);
|
||||
g_date_set_dmy (&first_day_of_year, 1, 1, date->year);
|
||||
|
||||
n_days_before_first_week = (first_day_of_week - g_date_get_weekday (&first_day_of_year) + 7) % 7;
|
||||
return (g_date_get_day_of_year (date) + 6 - n_days_before_first_week) / 7;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1964,23 +1970,7 @@ g_date_get_days_in_month (GDateMonth month,
|
||||
guint8
|
||||
g_date_get_monday_weeks_in_year (GDateYear year)
|
||||
{
|
||||
GDate d;
|
||||
|
||||
g_return_val_if_fail (g_date_valid_year (year), 0);
|
||||
|
||||
g_date_clear (&d, 1);
|
||||
g_date_set_dmy (&d, 1, 1, year);
|
||||
if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
|
||||
g_date_set_dmy (&d, 31, 12, year);
|
||||
if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
|
||||
if (g_date_is_leap_year (year))
|
||||
{
|
||||
g_date_set_dmy (&d, 2, 1, year);
|
||||
if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
|
||||
g_date_set_dmy (&d, 30, 12, year);
|
||||
if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
|
||||
}
|
||||
return 52;
|
||||
return g_date_get_weeks_in_year (year, G_DATE_MONDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1999,22 +1989,51 @@ g_date_get_monday_weeks_in_year (GDateYear year)
|
||||
*/
|
||||
guint8
|
||||
g_date_get_sunday_weeks_in_year (GDateYear year)
|
||||
{
|
||||
return g_date_get_weeks_in_year (year, G_DATE_SUNDAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_date_get_weeks_in_year:
|
||||
* @year: year to count weeks in
|
||||
* @first_day_of_week: the day which is considered the first day of the week
|
||||
* (for example, this would be [enum@GLib.DateWeekday.SUNDAY] in US locales,
|
||||
* [enum@GLib.DateWeekday.MONDAY] in British locales, and
|
||||
* [enum@GLib.DateWeekday.SATURDAY] in Egyptian locales
|
||||
*
|
||||
* Calculates the number of weeks in the year.
|
||||
*
|
||||
* The result depends on which day is considered the first day of the week,
|
||||
* which varies by locale. `first_day_of_week` must be valid.
|
||||
*
|
||||
* The result will be either 52 or 53. Years always have 52 seven-day periods,
|
||||
* plus one or two extra days depending on whether it’s a leap year. This
|
||||
* function effectively calculates how many @first_day_of_week days there are in
|
||||
* the year.
|
||||
*
|
||||
* Returns: the number of weeks in @year
|
||||
* Since: 2.86
|
||||
*/
|
||||
guint8
|
||||
g_date_get_weeks_in_year (GDateYear year,
|
||||
GDateWeekday first_day_of_week)
|
||||
{
|
||||
GDate d;
|
||||
|
||||
|
||||
g_return_val_if_fail (g_date_valid_year (year), 0);
|
||||
|
||||
g_return_val_if_fail (first_day_of_week != G_DATE_BAD_WEEKDAY, 0);
|
||||
|
||||
g_date_clear (&d, 1);
|
||||
g_date_set_dmy (&d, 1, 1, year);
|
||||
if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
|
||||
if (g_date_get_weekday (&d) == first_day_of_week) return 53;
|
||||
g_date_set_dmy (&d, 31, 12, year);
|
||||
if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
|
||||
if (g_date_is_leap_year (year))
|
||||
if (g_date_get_weekday (&d) == first_day_of_week) return 53;
|
||||
if (g_date_is_leap_year (year))
|
||||
{
|
||||
g_date_set_dmy (&d, 2, 1, year);
|
||||
if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
|
||||
if (g_date_get_weekday (&d) == first_day_of_week) return 53;
|
||||
g_date_set_dmy (&d, 30, 12, year);
|
||||
if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
|
||||
if (g_date_get_weekday (&d) == first_day_of_week) return 53;
|
||||
}
|
||||
return 52;
|
||||
}
|
||||
|
@@ -167,6 +167,9 @@ GLIB_AVAILABLE_IN_ALL
|
||||
guint g_date_get_monday_week_of_year (const GDate *date);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint g_date_get_sunday_week_of_year (const GDate *date);
|
||||
GLIB_AVAILABLE_IN_2_86
|
||||
guint g_date_get_week_of_year (const GDate *date,
|
||||
GDateWeekday first_day_of_week);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint g_date_get_iso8601_week_of_year (const GDate *date);
|
||||
|
||||
@@ -250,6 +253,9 @@ GLIB_AVAILABLE_IN_ALL
|
||||
guint8 g_date_get_monday_weeks_in_year (GDateYear year) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
guint8 g_date_get_sunday_weeks_in_year (GDateYear year) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_2_86
|
||||
guint8 g_date_get_weeks_in_year (GDateYear year,
|
||||
GDateWeekday first_day_of_week) G_GNUC_CONST;
|
||||
|
||||
/* Returns the number of days between the two dates. If date2 comes
|
||||
before date1, a negative value is return. */
|
||||
|
@@ -448,6 +448,18 @@ test_dates (void)
|
||||
g_assert_cmpint (g_date_get_sunday_weeks_in_year (0), ==, 0);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
/* g_date_get_week_of_year (d) */
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
|
||||
"*assertion *failed*");
|
||||
g_assert_cmpint (g_date_get_week_of_year (d, G_DATE_MONDAY), ==, 0);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
/* g_date_get_weeks_in_year (y) */
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
|
||||
"*assertion *failed*");
|
||||
g_assert_cmpint (g_date_get_weeks_in_year (0, G_DATE_MONDAY), ==, 0);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
/* g_date_get_iso8601_week_of_year (d) */
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
|
||||
"*assertion *failed*");
|
||||
@@ -494,6 +506,8 @@ test_dates (void)
|
||||
|
||||
g_assert_cmpint (g_date_get_sunday_weeks_in_year (1792), ==, 53);
|
||||
|
||||
g_assert_cmpint (g_date_get_weeks_in_year (2000, G_DATE_SATURDAY), ==, 53);
|
||||
|
||||
/* Trigger the update of the dmy/julian parts */
|
||||
d = g_date_new_julian (1);
|
||||
g_assert_cmpint (g_date_get_day_of_year (d), ==, 1);
|
||||
@@ -1412,6 +1426,8 @@ test_year (gconstpointer t)
|
||||
|
||||
guint32 first_day_of_year = G_DATE_BAD_JULIAN;
|
||||
guint16 days_in_year = g_date_is_leap_year (y) ? 366 : 365;
|
||||
guint saturday_week_of_year = 0;
|
||||
guint saturday_weeks_in_year = g_date_get_weeks_in_year (y, G_DATE_SATURDAY);
|
||||
guint sunday_week_of_year = 0;
|
||||
guint sunday_weeks_in_year = g_date_get_sunday_weeks_in_year (y);
|
||||
guint monday_week_of_year = 0;
|
||||
@@ -1420,6 +1436,7 @@ test_year (gconstpointer t)
|
||||
|
||||
g_assert_true (g_date_valid_year (y));
|
||||
/* Years ought to have roundabout 52 weeks */
|
||||
g_assert (saturday_weeks_in_year == 52 || saturday_weeks_in_year == 53);
|
||||
g_assert (sunday_weeks_in_year == 52 || sunday_weeks_in_year == 53);
|
||||
g_assert (monday_weeks_in_year == 52 || monday_weeks_in_year == 53);
|
||||
|
||||
@@ -1511,6 +1528,21 @@ test_year (gconstpointer t)
|
||||
|
||||
sunday_week_of_year = g_date_get_sunday_week_of_year (d);
|
||||
|
||||
g_assert_cmpint (g_date_get_week_of_year (d, G_DATE_SATURDAY),
|
||||
<=, saturday_weeks_in_year);
|
||||
g_assert_cmpint (g_date_get_week_of_year (d, G_DATE_SATURDAY),
|
||||
>=, saturday_week_of_year);
|
||||
if (g_date_get_weekday (d) == G_DATE_SATURDAY)
|
||||
g_assert_cmpint (g_date_get_week_of_year (d, G_DATE_SATURDAY) -
|
||||
saturday_week_of_year,
|
||||
==, 1);
|
||||
else
|
||||
g_assert_cmpint (g_date_get_week_of_year (d, G_DATE_SATURDAY) -
|
||||
saturday_week_of_year,
|
||||
==, 0);
|
||||
|
||||
saturday_week_of_year = g_date_get_week_of_year (d, G_DATE_SATURDAY);
|
||||
|
||||
g_assert_cmpint (g_date_compare (d, d), ==, 0);
|
||||
|
||||
i = 1;
|
||||
@@ -1753,6 +1785,60 @@ test_valid_dmy (void)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_week_of_year (void)
|
||||
{
|
||||
const struct
|
||||
{
|
||||
unsigned int year;
|
||||
unsigned int month;
|
||||
unsigned int day;
|
||||
unsigned int expected_saturday_week_of_year;
|
||||
unsigned int expected_sunday_week_of_year;
|
||||
unsigned int expected_monday_week_of_year;
|
||||
}
|
||||
vectors[] =
|
||||
{
|
||||
{ 2000, 1, 1, 1, 0, 0 }, /* first day of the year is a Saturday */
|
||||
{ 2000, 1, 7, 1, 1, 1 },
|
||||
{ 2000, 1, 8, 2, 1, 1 },
|
||||
{ 2001, 1, 1, 0, 0, 1 }, /* first day of the year is a Monday */
|
||||
{ 2001, 1, 7, 1, 1, 1 },
|
||||
{ 2001, 1, 8, 1, 1, 2 },
|
||||
{ 2002, 1, 1, 0, 0, 0 }, /* first day of the year is a Tuesday */
|
||||
{ 2002, 1, 7, 1, 1, 1 },
|
||||
{ 2002, 1, 8, 1, 1, 1 },
|
||||
{ 2003, 1, 1, 0, 0, 0 }, /* first day of the year is a Wednesday */
|
||||
{ 2003, 1, 7, 1, 1, 1 },
|
||||
{ 2003, 1, 8, 1, 1, 1 },
|
||||
{ 2004, 1, 1, 0, 0, 0 }, /* first day of the year is a Thursday */
|
||||
{ 2004, 1, 7, 1, 1, 1 },
|
||||
{ 2004, 1, 8, 1, 1, 1 },
|
||||
{ 2006, 1, 1, 0, 1, 0 }, /* first day of the year is a Sunday */
|
||||
{ 2006, 1, 7, 1, 1, 1 },
|
||||
{ 2006, 1, 8, 1, 2, 1 },
|
||||
{ 2010, 1, 1, 0, 0, 0 }, /* first day of the year is a Friday */
|
||||
{ 2010, 1, 7, 1, 1, 1 },
|
||||
{ 2010, 1, 8, 1, 1, 1 },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < G_N_ELEMENTS (vectors); i++)
|
||||
{
|
||||
GDate date;
|
||||
|
||||
g_date_clear (&date, 1);
|
||||
g_date_set_dmy (&date, vectors[i].day, vectors[i].month, vectors[i].year);
|
||||
g_test_message ("Considering %04u-%02u-%02u",
|
||||
vectors[i].year, vectors[i].month, vectors[i].day);
|
||||
|
||||
g_assert_cmpuint (g_date_get_week_of_year (&date, G_DATE_SATURDAY), ==, vectors[i].expected_saturday_week_of_year);
|
||||
g_assert_cmpuint (g_date_get_week_of_year (&date, G_DATE_SUNDAY), ==, vectors[i].expected_sunday_week_of_year);
|
||||
g_assert_cmpuint (g_date_get_week_of_year (&date, G_DATE_MONDAY), ==, vectors[i].expected_monday_week_of_year);
|
||||
g_assert_cmpuint (g_date_get_sunday_week_of_year (&date), ==, vectors[i].expected_sunday_week_of_year);
|
||||
g_assert_cmpuint (g_date_get_monday_week_of_year (&date), ==, vectors[i].expected_monday_week_of_year);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char** argv)
|
||||
{
|
||||
@@ -1806,6 +1892,7 @@ main (int argc, char** argv)
|
||||
}
|
||||
g_test_add_func ("/date/copy", test_copy);
|
||||
g_test_add_func ("/date/valid-dmy", test_valid_dmy);
|
||||
g_test_add_func ("/date/week-of-year", test_week_of_year);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Reference in New Issue
Block a user