X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=libchimara%2Fdatetime.c;h=9d5c2cf2dec83f5e05ead5e015a09d31d9d00bb9;hb=fabc4f9b35b4d4e1bb3c3f67971e8e134889c453;hp=e01ab105ed3655f66ea5c72306a39b61570d9ac2;hpb=f19a194bd066ea4320c1c6b6b8c5a375f7787af5;p=projects%2Fchimara%2Fchimara.git diff --git a/libchimara/datetime.c b/libchimara/datetime.c index e01ab10..9d5c2cf 100644 --- a/libchimara/datetime.c +++ b/libchimara/datetime.c @@ -1,5 +1,117 @@ #include #include "glk.h" +#include "magic.h" + +/* Parts adapted from Andrew Plotkin's Cheapglk implementation */ + +/* Copy a GDateTime to a glkdate. */ +static void +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 * +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; + GDateTime *retval = g_date_time_new(tz, + date->year, + date->month, + date->day, + date->hour, + date->minute, + seconds); + if( G_UNLIKELY(retval == NULL) ) { + if(date->year < 1) + WARNING("Years earlier than 1 C.E. are not currently supported."); + else if(date->year > 9999) + WARNING("Years later than 9999 C.E. are not currently supported."); + else + WARNING("Date is not supported or not valid."); + } + return retval; +} + +/* Convert a Unix timestamp (seconds since Jan 1 1970) to a glktimeval, + * adding a number of microseconds as well. */ +static void +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 +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; + unix_time_to_time(unix_time, microsec, time); +} + +/* Divide a Unix timestamp by a (positive) value. */ +static glsi32 +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); + } +} + +/* Convert a glkdate to a glktimeval, in the given time zone. */ +static void +date_to_time(glkdate_t *date, glktimeval_t *tv, GTimeZone *tz) +{ + GDateTime *dt = date_to_gdatetime(date, tz); + if(dt == NULL) { + tv->high_sec = -1; + tv->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); + unix_time_to_time(timestamp, microsec, tv); +} + +/* Convert a glkdate to a Unix timestamp divided by a value, in the given time +zone. */ +static glsi32 +date_to_simple_time(glkdate_t *date, glui32 factor, GTimeZone *tz) +{ + GDateTime *dt = date_to_gdatetime(date, tz); + if(dt == NULL) + return -1; + gint64 timestamp = g_date_time_to_unix(dt); + g_date_time_unref(dt); + + return simplify_time(timestamp, factor); +} /** * glk_current_time: @@ -24,6 +136,7 @@ void glk_current_time(glktimeval_t *time) { g_return_if_fail(time != NULL); + real_time_to_time(g_get_real_time(), time); } /** @@ -42,9 +155,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 simplify_time(sec, factor); } /** @@ -65,6 +179,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); + date_from_gdatetime(date, dt); + g_date_time_unref(dt); + date->microsec = time->microsec; } /** @@ -80,6 +204,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); + date_from_gdatetime(date, dt); + g_date_time_unref(dt); + date->microsec = time->microsec; } /** @@ -101,6 +235,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); + date_from_gdatetime(date, dt); + g_date_time_unref(dt); + date->microsec = 0; } /** @@ -117,6 +258,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); + date_from_gdatetime(date, dt); + g_date_time_unref(dt); + date->microsec = 0; } /** @@ -131,12 +279,20 @@ 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(); + date_to_time(date, time, utc); + g_time_zone_unref(utc); } /** @@ -160,6 +316,10 @@ 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(); + date_to_time(date, time, local); + g_time_zone_unref(local); } /** @@ -173,6 +333,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 +344,13 @@ 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(); + glsi32 retval = date_to_simple_time(date, factor, utc); + g_time_zone_unref(utc); + return retval; } /** @@ -200,8 +367,11 @@ 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(); + glsi32 retval = date_to_simple_time(date, factor, local); + g_time_zone_unref(local); + return retval; }