X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=libchimara%2Fdatetime.c;h=c70e2b4d381e21ac06dd67c0feeb2fc1e130acfd;hb=5fb29158df45248e3a4734e00bc1a9e7a9871044;hp=e01ab105ed3655f66ea5c72306a39b61570d9ac2;hpb=f19a194bd066ea4320c1c6b6b8c5a375f7787af5;p=projects%2Fchimara%2Fchimara.git diff --git a/libchimara/datetime.c b/libchimara/datetime.c index e01ab10..c70e2b4 100644 --- a/libchimara/datetime.c +++ b/libchimara/datetime.c @@ -1,5 +1,86 @@ #include #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: @@ -24,6 +105,7 @@ void glk_current_time(glktimeval_t *time) { g_return_if_fail(time != NULL); + gli_real_time_to_time(g_get_real_time(), time); } /** @@ -42,9 +124,10 @@ glk_current_time(glktimeval_t *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); } /** @@ -65,6 +148,16 @@ glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date) { 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; } /** @@ -80,6 +173,16 @@ glk_time_to_date_local(glktimeval_t *time, glkdate_t *date) { 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; } /** @@ -101,6 +204,13 @@ glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) { 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; } /** @@ -117,6 +227,13 @@ glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) { 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; } /** @@ -131,12 +248,29 @@ glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) * 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.) + * Chimara + * Chimara does not currently support years earlier than 1 C.E. or later than + * 9999 C.E. + * */ 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); } /** @@ -160,6 +294,19 @@ glk_date_to_time_local(glkdate_t *date, glktimeval_t *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); } /** @@ -173,6 +320,10 @@ glk_date_to_time_local(glkdate_t *date, glktimeval_t *time) * * If the time cannot be represented by the platform's time library, this may * return -1. + * Chimara + * Chimara does not currently support years earlier than 1 C.E. or later than + * 9999 C.E. + * * * Returns: a timestamp divided by @factor, and truncated to 32 bits, or -1 on * error. @@ -180,10 +331,18 @@ glk_date_to_time_local(glkdate_t *date, glktimeval_t *time) 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); } /** @@ -200,8 +359,16 @@ glk_date_to_simple_time_utc(glkdate_t *date, glui32 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); }