+#include "magic.h"
+
+/* Parts adapted from Andrew Plotkin's Cheapglk implementation */
+
+/* Copy a GDateTime to a glkdate. */
+static void
+gli_date_from_gdatetime(glkdate_t *date, GDateTime *dt)
+{
+ date->year = g_date_time_get_year(dt);
+ date->month = g_date_time_get_month(dt);
+ date->day = g_date_time_get_day_of_month(dt);
+ /* GDateTime has 1-7, with 1 = Monday; Glk has 0-6, with 0 = Sunday */
+ date->weekday = g_date_time_get_day_of_week(dt) % G_DATE_SUNDAY;
+ date->hour = g_date_time_get_hour(dt);
+ date->minute = g_date_time_get_minute(dt);
+ date->second = g_date_time_get_second(dt);
+}
+
+/* Copy a glkdate to a GDateTime.
+ This is used in the "glk_date_to_..." functions, which are supposed
+ to normalize the glkdate. We're going to rely on GDateTime to do that.
+ Returns NULL if the date is not supported by GDateTime.
+ Call g_date_time_unref() on the return value when done.
+ */
+static GDateTime *
+gli_date_to_gdatetime(glkdate_t *date, GTimeZone *tz)
+{
+ /* Combining seconds and microseconds into one floating-point number should
+ * take care of normalizing any negative microseconds or microseconds > one
+ * million */
+ double seconds = date->second + (double)date->microsec / G_USEC_PER_SEC;
+ if( G_UNLIKELY(date->year < 1) ) {
+ WARNING("Years earlier than 1 C.E. are not currently supported.");
+ return NULL;
+ }
+ if( G_UNLIKELY(date->year > 9999) ) {
+ WARNING("Years later than 9999 C.E. are not currently supported.");
+ return NULL;
+ }
+ return g_date_time_new(tz,
+ date->year,
+ date->month,
+ date->day,
+ date->hour,
+ date->minute,
+ seconds);
+}
+
+/* Convert a Unix timestamp (seconds since Jan 1 1970) to a glktimeval,
+ * adding a number of microseconds as well. */
+static void
+gli_unix_time_to_time(gint64 sec, int microsec, glktimeval_t *time)
+{
+ time->high_sec = (sec >> 32) & 0xFFFFFFFF;
+ time->low_sec = sec & 0xFFFFFFFF;
+ time->microsec = microsec;
+}
+
+/* Convert a gint64 microseconds value, as returned by g_get_real_time(),
+ * to a glktimeval. */
+static void
+gli_real_time_to_time(gint64 real_time, glktimeval_t *time)
+{
+ gint64 unix_time = real_time / G_USEC_PER_SEC;
+ int microsec = real_time % G_USEC_PER_SEC;
+ gli_unix_time_to_time(unix_time, microsec, time);
+}
+
+/* Divide a Unix timestamp by a (positive) value. */
+static glsi32
+gli_simplify_time(gint64 timestamp, glui32 factor)
+{
+ /* We want to round towards negative infinity, which takes a little
+ bit of fussing. */
+ if (timestamp >= 0) {
+ return timestamp / (gint64)factor;
+ }
+ else {
+ return -1 - (((gint64)-1 - timestamp) / (gint64)factor);
+ }
+}