chiark / gitweb /
Textual representation of more facets
authorThomas Thurman <tthurman@gnome.org>
Tue, 26 Oct 2010 06:06:49 +0000 (02:06 -0400)
committerThomas Thurman <tthurman@gnome.org>
Tue, 26 Oct 2010 06:06:49 +0000 (02:06 -0400)
breviary/calendar.c [new file with mode: 0644]

diff --git a/breviary/calendar.c b/breviary/calendar.c
new file mode 100644 (file)
index 0000000..cc72c75
--- /dev/null
@@ -0,0 +1,679 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "calendar.h"
+
+struct _Breviary {
+
+  time_t time;
+
+  /**
+   * Whether this is Year 1 or 2.
+   */
+  unsigned int year1or2: 2;
+
+  unsigned int hour:6;
+
+  /* Flags */
+  unsigned int debug:1;
+  unsigned int bvmblue:1;
+  unsigned int adventblue:1;
+  unsigned int rose:1;
+
+  unsigned int facets[5];
+
+};
+
+Breviary *
+breviary_new (time_t when)
+{
+  Breviary *result = malloc (sizeof (Breviary));
+
+  result->time = when;
+  result->facets[0] = 0;
+
+  result->debug = 0;
+  result->bvmblue = 0;
+  result->adventblue = 0;
+  result->rose = 0;
+
+  return result;
+}
+
+void
+breviary_set_flag (Breviary *breviary,
+                  const char *flag,
+                  int value)
+{
+  value = !!value;
+
+  if (strcmp("debug", flag)==0) {
+    breviary->debug = value;
+  } else if (strcmp("bvmblue", flag)==0) {
+    breviary->bvmblue = value;
+  } else if (strcmp("adventblue", flag)==0) {
+    breviary->adventblue = value;
+  } else if (strcmp("rose", flag)==0) {
+    breviary->rose = value;
+  }
+}
+
+long
+ymd_to_serial (int y, int m, int d)
+{
+  struct tm time;
+
+  time.tm_sec = 0;
+  time.tm_min = 0;
+  time.tm_hour = 0;
+  time.tm_mday = d;
+  time.tm_mon = m-1; /* January is 0 */
+  time.tm_year = y;
+  time.tm_wday = 0;
+  time.tm_yday = 0;
+  time.tm_isdst = 0;
+
+  return mktime(&time) / (60*60*24);
+}
+
+/**
+ * Returns the serial number of the Sunday immediately
+ * before the parameter, unless the parameter represents
+ * a Sunday, in which case it is returned unchanged.
+ *
+ * \param serial  The serial number of a day.
+ * \result        A Sunday within a week before "serial".
+ */
+long
+rewind_to_sunday (long serial)
+{
+  serial += 4;
+  return (serial - (serial%7)) - 4;
+}
+
+/**
+ * Returns the date of Easter for the given calendar year.
+ *
+ * \param y  The number of years after 1900 (for
+ *           example, 2010 is 110).
+ * \result   The date of Easter for the given year,
+ *           expressed as a number of days after
+ *           1 January 1970.
+ */
+unsigned long
+year_to_easter (int y)
+{
+  int c, g, k, l, i, j, m, d;
+
+  y = y + 1900;
+  c = y/100;
+  g = y%19;
+  k = (c-17) / 25;
+  i = (c - c/4 - (c-k)/3 + 19*g + 15) % 30;
+  i = i - (i/28)*(1- (i/28)*(29/(i+1))*((21-g)/11));
+  j = (y + y/4 + i + 2 - c + c/4) % 7;
+  l = i - j;
+  m = 3 + (l+40)/44;
+  d = l + 28 - 31*(m/4);
+
+  return ymd_to_serial (y-1900, m, d);
+}
+
+/**
+ * Returns the date of Advent Sunday, the first
+ * day of the liturgical year, for the given calendar year.
+ *
+ * \param y  The number of years after 1900 (for
+ *           example, 2010 is 110).
+ * \result   The date of Advent Sunday for the given
+ *           year, expressed as a number of days
+ *           after 1 January 1970.
+ */
+unsigned long
+year_to_advent_sunday (int y)
+{
+  /* Firstly, discover which day Christmas falls on */
+  struct tm time;
+  long result = ymd_to_serial (y, 12, 25);
+  time_t christmas = result * (60*60*24);
+
+  gmtime_r (&christmas,
+           &time);
+
+  if (time.tm_wday==0)
+    {
+      /* Christmas falls on a Sunday */
+      result -= 7;
+    }
+  else
+    {
+      /*
+       * It was that many days after the
+       * Sunday we want.
+       */
+      result -= time.tm_wday;
+    }
+
+  /* Now skip backwards three more weeks */
+  result -= (3*7);
+
+  return result;
+
+}
+
+static void
+dump_date (const char* message,
+          long serial)
+{
+  struct tm tm;
+  time_t time = serial * (24*60*60);
+
+  gmtime_r (&time,
+           &tm);
+
+  printf ("%s - %ld - %d-%d-%d\n",
+         message,
+         serial,
+         tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday);
+}
+
+void
+breviary_ensure_initialised (Breviary *breviary)
+{
+  struct tm tm;
+  long target_serial,
+    easter_serial,
+    advent_sunday_serial,
+    week_count_start_serial,
+    season_start_serial,
+    sunday_before_season_start_serial;
+  int season = SEASON_NONE;
+  int days_after_easter;
+  int days_after_week_count_start;
+
+  if (breviary->facets[0]!=0)
+    return;
+
+#if 0
+  printf ("Breviary is not initialised; doing it now.\n");
+#endif
+
+  breviary->facets[0] = 1;
+  breviary->facets[1] = 1;
+  breviary->facets[2] = 1;
+  breviary->facets[3] = 1;
+  breviary->facets[4] = 0;
+
+  gmtime_r (&(breviary->time),
+           &tm);
+
+  breviary->hour = tm.tm_hour;
+
+  breviary->facets[0] =
+    10100 +
+    tm.tm_mon*100 +
+    tm.tm_mday;
+
+  target_serial =
+    ymd_to_serial (tm.tm_year,
+                  tm.tm_mon+1,
+                  tm.tm_mday);
+
+#if 0
+  dump_date ("Target date",
+            target_serial);
+#endif
+
+  easter_serial = year_to_easter (tm.tm_year);
+
+#if 0
+  dump_date ("Easter",
+            easter_serial);
+#endif
+
+  days_after_easter = target_serial - easter_serial;
+
+  breviary->facets[3] =
+    40500 +
+    days_after_easter;
+
+  week_count_start_serial =
+    rewind_to_sunday (ymd_to_serial (tm.tm_year,
+                                    1, 1));
+
+#if 0
+  dump_date ("Measuring weeks from",
+            week_count_start_serial);
+#endif
+
+  days_after_week_count_start =
+    target_serial - week_count_start_serial;
+
+  breviary->facets[1] =
+    20000 +
+    10 * (days_after_week_count_start/7) +
+    days_after_week_count_start%7;
+
+  advent_sunday_serial =
+    year_to_advent_sunday (tm.tm_year);
+
+#if 0
+  dump_date ("Advent Sunday",
+            advent_sunday_serial);
+#endif
+
+  /* Now to calculate the season */
+
+  if (days_after_easter > -47 &&
+      days_after_easter < 0)
+    {
+      breviary->facets[2] =
+       SEASON_LENT;
+      season_start_serial =
+       easter_serial - 47;
+    }
+  else if (days_after_easter >= 0 &&
+          days_after_easter <= 49)
+    {
+      /*
+       * yes, this is correct: Pentecost
+       * itself is in Easter season, not
+       * Pentecost season
+       */
+
+      breviary->facets[2] =
+       SEASON_EASTER;
+      season_start_serial =
+       easter_serial;
+    }
+  else if (target_serial >= advent_sunday_serial &&
+          breviary->facets[0] < 11225)
+    {
+      breviary->facets[2] =
+       SEASON_ADVENT;
+      season_start_serial =
+       advent_sunday_serial;
+    }
+  else if (breviary->facets[0] >= 11225 ||
+          breviary->facets[0] <= 10105)
+    {
+      int year = tm.tm_year;
+
+      if (breviary->facets[0] <= 10105)
+       {
+         year--;
+       }
+
+      breviary->facets[2] =
+       SEASON_CHRISTMAS;
+      season_start_serial =
+       ymd_to_serial (year, 12, 25);
+    }
+  else if (days_after_easter <= -47)
+    {
+      breviary->facets[2] =
+       SEASON_EPIPHANY;
+      season_start_serial =
+       ymd_to_serial (tm.tm_year,
+                      1, 6);
+    }
+  else
+    {
+      breviary->facets[2] =
+       SEASON_PENTECOST;
+      season_start_serial =
+       easter_serial + 50;
+    }
+
+#if 0
+  dump_date ("Season starts on",
+            season_start_serial);
+#endif
+
+  sunday_before_season_start_serial =
+    rewind_to_sunday (season_start_serial);
+
+  if (sunday_before_season_start_serial ==
+      season_start_serial)
+    {
+      /*
+       * If the season begins on a Sunday,
+       * the first week is 1, not 0.
+       */
+      sunday_before_season_start_serial -= 7;
+    }
+
+  if (target_serial ==
+      advent_sunday_serial-7)
+    {
+      /*
+       * The feast of Christ the King gets a
+       * special case: it's coded as week
+       * 99 of Pentecost season.  (See the
+       * docs for a discussion of why.)
+       */
+
+      breviary->facets[2] += 990;
+    }
+  else
+    {
+      breviary->facets[2] += 10 *
+       ((target_serial - sunday_before_season_start_serial)/7);
+    }
+
+  /* And lastly, add the day of the week. */
+  breviary->facets[2] +=
+    days_after_week_count_start%7;
+
+#if 0
+  
+  /* Year 1 or 2? */
+
+  breviary->year1or2 = 1 + (tm.tm_year%2);
+  if (target_serial >= advent_sunday_serial)
+    {
+      /*
+       * If we're on or after Advent Sunday, we're
+       * into the following year, so flip the
+       * year number.
+       */
+      breviary->year1or2 = 3 - breviary->year1or2;
+    }
+
+  printf ("Year 1 or 2? %d\n", breviary->year1or2);
+
+  /* Calculate the season. */
+#endif
+  
+}
+
+const int*
+breviary_get_facets (Breviary *breviary)
+{
+  breviary_ensure_initialised (breviary);
+
+  return &(breviary->facets[0]);
+}
+
+void
+breviary_free (Breviary *breviary)
+{
+  free (breviary);
+}
+
+static void
+dump_sundays (void)
+{
+  long current = ymd_to_serial (75, 1, 1);
+  int day;
+
+  printf ("# Dump Sundays\n");
+
+  for (day=1; day<32; day++) {
+    struct tm tm;
+    char *is_sunday = "";
+    time_t time;
+
+    time = current * (60*60*24);
+
+    gmtime_r (&time,
+             &tm);
+
+    if (tm.tm_wday==0)
+      {
+       is_sunday = " (Sunday)";
+      }
+
+    printf ("%d-%02d-%02d %10ld %10ld%s\n",
+           tm.tm_year+1900,
+           tm.tm_mon+1,
+           tm.tm_mday,
+           current,
+           rewind_to_sunday (current),
+           is_sunday);
+
+    current++;
+  }
+}
+
+char *
+breviary_explain_facet (int facet)
+{
+  const size_t size = 15;
+  char *result = malloc (size);
+
+  result[0] = 0;
+
+  switch (facet / 10000)
+    {
+    case 1: /* Gregorian */
+      {
+       int month = (facet%10000)/100;
+       int day = facet%100;
+       char *month_name = NULL;
+       
+       switch (month)
+         {
+         case  1: month_name = "Jan"; break;
+         case  2: month_name = "Feb"; break;
+         case  3: month_name = "Mar"; break;
+         case  4: month_name = "Apr"; break;
+         case  5: month_name = "May"; break;
+         case  6: month_name = "Jun"; break;
+         case  7: month_name = "Jul"; break;
+         case  8: month_name = "Aug"; break;
+         case  9: month_name = "Sep"; break;
+         case 10: month_name = "Oct"; break;
+         case 11: month_name = "Nov"; break;
+         case 12: month_name = "Dec"; break;
+         }
+
+       if (month_name)
+         {
+           snprintf (result, size,
+                     "%s %2d",
+                     month_name, day);
+         }
+      }
+      break;
+
+    case 2: /* Week number */
+      {
+       int week = (facet-20000) / 10;
+       int day = facet % 10;
+       char* day_name = NULL;
+
+       switch (day)
+         {
+         case  0: day_name = "Sun"; break;
+         case  1: day_name = "Mon"; break;
+         case  2: day_name = "Tue"; break;
+         case  3: day_name = "Wed"; break;
+         case  4: day_name = "Thu"; break;
+         case  5: day_name = "Fri"; break;
+         case  6: day_name = "Sat"; break;
+         }
+
+       if (day_name)
+         {
+           snprintf (result, size,
+                     "%s, week %d",
+                     day_name,
+                     week);
+         }
+      }
+      break;
+
+    case 3: /* Season */
+      {
+       int season = (facet / 1000) * 1000;
+       int week = (facet/10) % 100;
+       char *season_name = NULL;
+
+       switch (season)
+         {
+         case SEASON_ADVENT:
+           season_name = "Adve";
+           break;
+
+         case SEASON_CHRISTMAS:
+           season_name = "Chri";
+           break;
+
+         case SEASON_EPIPHANY:
+           season_name = "Ephi";
+           break;
+
+         case SEASON_LENT:
+           season_name = "Lent";
+           break;
+
+         case SEASON_EASTER:
+           season_name = "East";
+           break;
+
+         case SEASON_PENTECOST:
+           season_name = "Pent";
+           break;
+         }
+
+       if (season_name)
+         {
+           snprintf (result, size,
+                     "%s %d",
+                     season_name, week);
+         }
+      }
+      break;
+
+    case 4: /* Easter */
+      {
+       int value = facet - 40500;
+       char sign = '+';
+
+       if (value<0)
+         {
+           value = -value;
+           sign = '-';
+         }
+
+       snprintf (result, size,
+                 "Easter%c%03d",
+                 sign, value);
+      }
+      break;
+    }
+
+  if (result[0]==0)
+    {
+      snprintf (result, size,
+               "Unknown");
+    }
+
+  return result;
+}
+
+void
+breviary_free_string (char *string)
+{
+  if (string==NULL)
+    {
+      return;
+    }
+
+  free (string);
+}
+
+static void
+dump_facets (void)
+{
+  long current = ymd_to_serial (75, 1, 1);
+  int i;
+  const int *cursor;
+  Breviary *breviary;
+
+  printf ("# Dump facets\n");
+
+  for (i=0; i<365; i++) {
+    struct tm tm;
+    time_t time;
+
+    time = current * (60*60*24);
+
+    gmtime_r (&time,
+             &tm);
+
+    printf ("%d-%02d-%02d",
+           tm.tm_year+1900,
+           tm.tm_mon+1,
+           tm.tm_mday);
+
+    breviary = breviary_new (time);
+
+    cursor = breviary_get_facets (breviary);
+
+    while (*cursor)
+      {
+       char *explanation =
+         breviary_explain_facet (*cursor);
+       printf (" %d %-15s", *cursor,
+               explanation);
+       breviary_free_string (explanation);
+       cursor++;
+      }
+
+    breviary_free (breviary);
+
+    printf ("\n");
+
+    current++;
+  }
+
+}
+
+int
+breviary_test_dump_feast (unsigned int feast)
+{
+  int year;
+  struct tm tm;
+
+  switch (feast)
+    {
+    case 1:
+      dump_sundays ();
+      return;
+
+    case 2:
+      dump_facets ();
+      return;
+
+    case 3:
+    case 4:
+      break;
+
+    default:
+      printf ("# Unknown request %d\n", feast);
+      return;
+    }
+
+  for (year=70; year<=120; year++)
+    {
+      int date;
+      time_t time;
+
+      if (feast==3)
+       date = year_to_easter (year);
+      else
+       date = year_to_advent_sunday (year);
+
+      time = (time_t) date * (24*60*60);
+
+      gmtime_r (&time,
+               &tm);
+
+      printf ("%d-%d-%d\n",
+             tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday);
+    }
+}
+
+/* EOF calendar.c */