#include <glib.h>
#include "glk.h"
+#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);
+ }
+}
/**
* glk_current_time:
glk_current_time(glktimeval_t *time)
{
g_return_if_fail(time != NULL);
+ gli_real_time_to_time(g_get_real_time(), time);
}
/**
glsi32
glk_current_simple_time(glui32 factor)
{
- g_return_val_if_fail(factor != 0, -1);
-
- return -1;
+ g_return_val_if_fail(factor != 0, 0);
+
+ gint64 sec = g_get_real_time() / G_USEC_PER_SEC;
+ return gli_simplify_time(sec, factor);
}
/**
{
g_return_if_fail(time != NULL);
g_return_if_fail(date != NULL);
+
+ time_t timestamp = time->low_sec;
+ if (sizeof(timestamp) > 4) {
+ timestamp += ((gint64)time->high_sec << 32);
+ }
+
+ GDateTime *dt = g_date_time_new_from_unix_utc(timestamp);
+ gli_date_from_gdatetime(date, dt);
+ g_date_time_unref(dt);
+ date->microsec = time->microsec;
}
/**
{
g_return_if_fail(time != NULL);
g_return_if_fail(date != NULL);
+
+ time_t timestamp = time->low_sec;
+ if (sizeof(timestamp) > 4) {
+ timestamp += ((int64_t)time->high_sec << 32);
+ }
+
+ GDateTime *dt = g_date_time_new_from_unix_local(timestamp);
+ gli_date_from_gdatetime(date, dt);
+ g_date_time_unref(dt);
+ date->microsec = time->microsec;
}
/**
{
g_return_if_fail(factor != 0);
g_return_if_fail(date != NULL);
+
+ time_t timestamp = (time_t)time * factor;
+
+ GDateTime *dt = g_date_time_new_from_unix_utc(timestamp);
+ gli_date_from_gdatetime(date, dt);
+ g_date_time_unref(dt);
+ date->microsec = 0;
}
/**
{
g_return_if_fail(factor != 0);
g_return_if_fail(date != NULL);
+
+ time_t timestamp = (time_t)time * factor;
+
+ GDateTime *dt = g_date_time_new_from_unix_local(timestamp);
+ gli_date_from_gdatetime(date, dt);
+ g_date_time_unref(dt);
+ date->microsec = 0;
}
/**
* If the time cannot be represented by the platform's time library, this may
* return -1 for the seconds value. (I.e., the @high_sec and @low_sec fields
* both $FFFFFFFF. The microseconds field is undefined in this case.)
+ * <note><title>Chimara</title><para>
+ * Chimara does not currently support years earlier than 1 C.E. or later than
+ * 9999 C.E.
+ * </para></note>
*/
void
glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time)
{
g_return_if_fail(date != NULL);
g_return_if_fail(time != NULL);
+
+ GTimeZone *utc = g_time_zone_new_utc();
+ GDateTime *dt = gli_date_to_gdatetime(date, utc);
+ g_time_zone_unref(utc);
+ if(dt == NULL) {
+ time->high_sec = -1;
+ time->low_sec = -1;
+ return;
+ }
+ gint64 timestamp = g_date_time_to_unix(dt);
+ int microsec = g_date_time_get_microsecond(dt);
+ g_date_time_unref(dt);
+ gli_unix_time_to_time(timestamp, microsec, time);
}
/**
{
g_return_if_fail(date != NULL);
g_return_if_fail(time != NULL);
+
+ GTimeZone *local = g_time_zone_new_local();
+ GDateTime *dt = gli_date_to_gdatetime(date, local);
+ g_time_zone_unref(local);
+ if(dt == NULL) {
+ time->high_sec = -1;
+ time->low_sec = -1;
+ return;
+ }
+ gint64 timestamp = g_date_time_to_unix(dt);
+ int microsec = g_date_time_get_microsecond(dt);
+ g_date_time_unref(dt);
+ gli_unix_time_to_time(timestamp, microsec, time);
}
/**
*
* If the time cannot be represented by the platform's time library, this may
* return -1.
+ * <note><title>Chimara</title><para>
+ * Chimara does not currently support years earlier than 1 C.E. or later than
+ * 9999 C.E.
+ * </para></note>
*
* Returns: a timestamp divided by @factor, and truncated to 32 bits, or -1 on
* error.
glsi32
glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor)
{
- g_return_val_if_fail(date != NULL, -1);
- g_return_val_if_fail(factor != 0, -1);
-
- return -1;
+ g_return_val_if_fail(date != NULL, 0);
+ g_return_val_if_fail(factor != 0, 0);
+
+ GTimeZone *utc = g_time_zone_new_utc();
+ GDateTime *dt = gli_date_to_gdatetime(date, utc);
+ g_time_zone_unref(utc);
+ if(dt == NULL)
+ return -1;
+ gint64 timestamp = g_date_time_to_unix(dt);
+ g_date_time_unref(dt);
+
+ return gli_simplify_time(timestamp, factor);
}
/**
glsi32
glk_date_to_simple_time_local(glkdate_t *date, glui32 factor)
{
- g_return_val_if_fail(date != NULL, -1);
- g_return_val_if_fail(factor != 0, -1);
-
- return -1;
+ g_return_val_if_fail(date != NULL, 0);
+ g_return_val_if_fail(factor != 0, 0);
+
+ GTimeZone *local = g_time_zone_new_local();
+ GDateTime *dt = gli_date_to_gdatetime(date, local);
+ g_time_zone_unref(local);
+ if(dt == NULL)
+ return -1;
+ gint64 timestamp = g_date_time_to_unix(dt);
+ g_date_time_unref(dt);
+
+ return gli_simplify_time(timestamp, factor);
}