From 3cfac71d09dded67485f153fa76d6f063dbb6a76 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Sat, 13 Apr 2013 11:26:34 -0700 Subject: [PATCH] gdatetime: fix floating-point conversion Conversion from floating point to integer requires special care. https://bugzilla.gnome.org/show_bug.cgi?id=697715 --- glib/gdatetime.c | 13 ++++++++++++- glib/tests/gdatetime.c | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/glib/gdatetime.c b/glib/gdatetime.c index e1c726047..045c57885 100644 --- a/glib/gdatetime.c +++ b/glib/gdatetime.c @@ -1295,6 +1295,7 @@ g_date_time_new (GTimeZone *tz, { GDateTime *datetime; gint64 full_time; + gint64 usec; g_return_val_if_fail (tz != NULL, NULL); @@ -1322,10 +1323,20 @@ g_date_time_new (GTimeZone *tz, G_TIME_TYPE_STANDARD, &full_time); + /* This is the correct way to convert a scaled FP value to integer. + * If this surprises you, please observe that (int)(1.000001 * 1e6) + * is 1000000. This is not a problem with precision, it's just how + * FP numbers work. + * See https://bugzilla.gnome.org/show_bug.cgi?id=697715. */ + usec = seconds * USEC_PER_SECOND; + if ((usec + 1) * 1e-6 <= seconds) { + usec++; + } + full_time += UNIX_EPOCH_START * SEC_PER_DAY; datetime->days = full_time / SEC_PER_DAY; datetime->usec = (full_time % SEC_PER_DAY) * USEC_PER_SECOND; - datetime->usec += ((int) (seconds * USEC_PER_SECOND)) % USEC_PER_SECOND; + datetime->usec += usec % USEC_PER_SECOND; return datetime; } diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c index f13b9def5..03a793593 100644 --- a/glib/tests/gdatetime.c +++ b/glib/tests/gdatetime.c @@ -2083,6 +2083,21 @@ test_posix_parse (void) g_time_zone_unref (tz); } +static void +test_GDateTime_floating_point (void) +{ + GDateTime *dt; + GTimeZone *tz; + + g_test_bug ("697715"); + + tz = g_time_zone_new ("-03:00"); + dt = g_date_time_new (tz, 2010, 5, 24, 8, 0, 1.000001); + g_time_zone_unref (tz); + g_assert_cmpint (g_date_time_get_microsecond (dt), ==, 1); + g_date_time_unref (dt); +} + gint main (gint argc, gchar *argv[]) @@ -2140,6 +2155,7 @@ main (gint argc, g_test_add_func ("/GTimeZone/adjust-time", test_adjust_time); g_test_add_func ("/GTimeZone/no-header", test_no_header); g_test_add_func ("/GTimeZone/posix-parse", test_posix_parse); + g_test_add_func ("/GTimeZone/floating-point", test_GDateTime_floating_point); return g_test_run (); }