[tracker/tracker-0.10] libtracker-data: Support fractional seconds in xsd:dateTime values



commit 74897f12a2f9345f4f3498eacead11c99795504b
Author: JÃrg Billeter <j bitron ch>
Date:   Wed Nov 9 16:21:02 2011 +0100

    libtracker-data: Support fractional seconds in xsd:dateTime values
    
    Fixes NB#290480.

 src/libtracker-common/Makefile.am                  |    3 +-
 src/libtracker-common/libtracker-common.vapi       |    2 +-
 src/libtracker-common/tracker-date-time.c          |   60 +++++++++++++------
 src/libtracker-common/tracker-date-time.h          |    8 +-
 src/libtracker-data/Makefile.am                    |    3 +-
 src/libtracker-data/tracker-data-update.c          |   14 +++-
 src/libtracker-data/tracker-sparql-expression.vala |    2 +-
 src/libtracker-data/tracker-sparql-query.vala      |    4 +-
 tests/functional-tests/01-insertion.py             |    4 +-
 tests/libtracker-data/subqueries/subqueries-1.out  |    4 +-
 .../subqueries/subqueries-union-2.out              |   10 ++--
 11 files changed, 72 insertions(+), 42 deletions(-)
---
diff --git a/src/libtracker-common/Makefile.am b/src/libtracker-common/Makefile.am
index 1131c1b..22a51b2 100644
--- a/src/libtracker-common/Makefile.am
+++ b/src/libtracker-common/Makefile.am
@@ -63,7 +63,8 @@ libtracker_common_la_LDFLAGS = \
 
 libtracker_common_la_LIBADD = \
 	$(BUILD_LIBS) \
-	$(LIBTRACKER_COMMON_LIBS)
+	$(LIBTRACKER_COMMON_LIBS) \
+	-lm
 
 if HAVE_TRACKER_FTS
 libtracker_common_la_LIBADD += $(top_builddir)/src/libstemmer/libstemmer.la
diff --git a/src/libtracker-common/libtracker-common.vapi b/src/libtracker-common/libtracker-common.vapi
index b9b9c43..4f38dbe 100644
--- a/src/libtracker-common/libtracker-common.vapi
+++ b/src/libtracker-common/libtracker-common.vapi
@@ -19,7 +19,7 @@
 
 namespace Tracker {
 	[CCode (cheader_filename = "libtracker-common/tracker-date-time.h")]
-	public int string_to_date (string date_string, out int offset) throws DateError;
+	public double string_to_date (string date_string, out int offset) throws DateError;
 
 	[CCode (cheader_filename = "libtracker-common/tracker-date-time.h")]
 	public errordomain DateError {
diff --git a/src/libtracker-common/tracker-date-time.c b/src/libtracker-common/tracker-date-time.c
index 49412c0..56eeed5 100644
--- a/src/libtracker-common/tracker-date-time.c
+++ b/src/libtracker-common/tracker-date-time.c
@@ -27,9 +27,10 @@
 
 #include <strings.h>
 #include <string.h>
+#include <math.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
-#include <stdlib.h>
 
 #include <glib.h>
 
@@ -40,7 +41,7 @@ GQuark tracker_date_error_quark (void) {
 	return g_quark_from_static_string ("tracker_date_error-quark");
 }
 
-time_t
+gdouble
 tracker_string_to_date (const gchar *date_string,
                         gint        *offset_p,
                         GError      **error)
@@ -54,7 +55,7 @@ tracker_string_to_date (const gchar *date_string,
 	GMatchInfo *match_info;
 	gchar      *match;
 	struct tm tm;
-	time_t    t;
+	gdouble t;
 	gint offset;
 
 	g_return_val_if_fail (date_string, -1);
@@ -171,10 +172,20 @@ tracker_string_to_date (const gchar *date_string,
 		offset = -timezone + (tm.tm_isdst > 0 ? 3600 : 0);
 #else
 		t2 = timegm (&tm);
-		offset = t2 - t;
+		offset = t2 - (time_t) t;
 #endif
 	}
 
+	match = g_match_info_fetch (match_info, 7);
+	if (match) {
+		char milliseconds[4] = "000\0";
+		/* first character of match is decimal point
+		   we're interested in a maximum of 3 decimal places (milliseconds) */
+		memcpy (milliseconds, match + 1, MIN (3, strlen (match + 1)));
+		t += (gdouble) atoi (milliseconds) / 1000;
+		g_free (match);
+	}
+
 	g_match_info_free (match_info);
 
 	if (offset_p) {
@@ -185,19 +196,30 @@ tracker_string_to_date (const gchar *date_string,
 }
 
 gchar *
-tracker_date_to_string (time_t date_time)
+tracker_date_to_string (gdouble date_time)
 {
 	gchar     buffer[30];
+	time_t seconds;
+	gint milliseconds;
 	struct tm utc_time;
 	size_t    count;
 
 	memset (buffer, '\0', sizeof (buffer));
 	memset (&utc_time, 0, sizeof (struct tm));
 
-	gmtime_r (&date_time, &utc_time);
+	seconds = (time_t) date_time;
+	milliseconds = (gint) (fmod (date_time, 1) * 1000);
+	gmtime_r (&seconds, &utc_time);
+
+	/* Output is ISO 8601 format : "YYYY-MM-DDThh:mm:ss" */
+	count = strftime (buffer, sizeof (buffer), "%FT%T", &utc_time);
 
-	/* Output is ISO 8601 format : "YYYY-MM-DDThh:mm:ssZ" */
-	count = strftime (buffer, sizeof (buffer), "%FT%TZ", &utc_time);
+	/* Append milliseconds (if non-zero) and time zone */
+	if (milliseconds > 0) {
+		snprintf (buffer + count, sizeof (buffer) - count, ".%03dZ", milliseconds);
+	} else {
+		buffer[count] = 'Z';
+	}
 
 	return count > 0 ? g_strdup (buffer) : NULL;
 }
@@ -205,7 +227,7 @@ tracker_date_to_string (time_t date_time)
 static void
 date_time_value_init (GValue *value)
 {
-	value->data[0].v_int64 = 0;
+	value->data[0].v_double = 0;
 	value->data[1].v_int = 0;
 }
 
@@ -213,7 +235,7 @@ static void
 date_time_value_copy (const GValue *src_value,
                       GValue       *dest_value)
 {
-	dest_value->data[0].v_int64 = src_value->data[0].v_int64;
+	dest_value->data[0].v_double = src_value->data[0].v_double;
 	dest_value->data[1].v_int = src_value->data[1].v_int;
 }
 
@@ -254,13 +276,13 @@ tracker_date_time_get_type (void)
 
 void
 tracker_date_time_set (GValue  *value,
-                       gint64   time,
+                       gdouble  time,
                        gint     offset)
 {
 	g_return_if_fail (G_VALUE_HOLDS (value, TRACKER_TYPE_DATE_TIME));
 	g_return_if_fail (offset >= -14 * 3600 && offset <= 14 * 3600);
 
-	value->data[0].v_int64 = time;
+	value->data[0].v_double = time;
 	value->data[1].v_int = offset;
 }
 
@@ -269,7 +291,7 @@ tracker_date_time_set_from_string (GValue      *value,
                                    const gchar *date_time_string,
                                    GError     **error)
 {
-	gint64 time;
+	gdouble time;
 	gint offset;
 	GError *new_error = NULL;
 
@@ -286,13 +308,13 @@ tracker_date_time_set_from_string (GValue      *value,
 	tracker_date_time_set (value, time, offset);
 }
 
-gint64
+gdouble
 tracker_date_time_get_time (const GValue *value)
 {
 	g_return_val_if_fail (G_VALUE_HOLDS (value, TRACKER_TYPE_DATE_TIME), 0);
 
 	/* UTC timestamp */
-	return value->data[0].v_int64;
+	return value->data[0].v_double;
 }
 
 gint
@@ -307,23 +329,23 @@ tracker_date_time_get_offset (const GValue *value)
 gint
 tracker_date_time_get_local_date (const GValue *value)
 {
-	gint64 local_timestamp;
+	gdouble local_timestamp;
 
 	g_return_val_if_fail (G_VALUE_HOLDS (value, TRACKER_TYPE_DATE_TIME), 0);
 
 	/* return number of days since epoch */
 	local_timestamp = tracker_date_time_get_time (value) + tracker_date_time_get_offset (value);
-	return local_timestamp / 3600 / 24;
+	return (gint) (local_timestamp / 3600 / 24);
 }
 
 gint
 tracker_date_time_get_local_time (const GValue *value)
 {
-	gint64 local_timestamp;
+	gdouble local_timestamp;
 
 	g_return_val_if_fail (G_VALUE_HOLDS (value, TRACKER_TYPE_DATE_TIME), 0);
 
 	/* return local time of day */
 	local_timestamp = tracker_date_time_get_time (value) + tracker_date_time_get_offset (value);
-	return local_timestamp % (24 * 3600);
+	return (int) local_timestamp % (24 * 3600);
 }
diff --git a/src/libtracker-common/tracker-date-time.h b/src/libtracker-common/tracker-date-time.h
index 09b97c1..fee7ba4 100644
--- a/src/libtracker-common/tracker-date-time.h
+++ b/src/libtracker-common/tracker-date-time.h
@@ -43,20 +43,20 @@ GQuark   tracker_date_error_quark              (void);
 GType    tracker_date_time_get_type            (void);
 
 void     tracker_date_time_set                 (GValue       *value,
-                                                gint64        time,
+                                                gdouble       time,
                                                 gint          offset);
 void     tracker_date_time_set_from_string     (GValue       *value,
                                                 const gchar  *date_time_string,
                                                 GError      **error);
-gint64   tracker_date_time_get_time            (const GValue *value);
+gdouble  tracker_date_time_get_time            (const GValue *value);
 gint     tracker_date_time_get_offset          (const GValue *value);
 gint     tracker_date_time_get_local_date      (const GValue *value);
 gint     tracker_date_time_get_local_time      (const GValue *value);
 
-time_t   tracker_string_to_date                (const gchar  *date_string,
+gdouble  tracker_string_to_date                (const gchar  *date_string,
                                                 gint         *offset,
                                                 GError      **error);
-gchar *  tracker_date_to_string                (time_t        date_time);
+gchar *  tracker_date_to_string                (gdouble       date_time);
 
 G_END_DECLS
 
diff --git a/src/libtracker-data/Makefile.am b/src/libtracker-data/Makefile.am
index 08b648b..72c02a0 100644
--- a/src/libtracker-data/Makefile.am
+++ b/src/libtracker-data/Makefile.am
@@ -49,7 +49,8 @@ libtracker_data_la_LIBADD =                            \
 	$(top_builddir)/src/gvdb/libgvdb.la \
 	$(top_builddir)/src/libtracker-common/libtracker-common.la \
 	$(BUILD_LIBS)                                  \
-	$(LIBTRACKER_DATA_LIBS)
+	$(LIBTRACKER_DATA_LIBS)                        \
+	-lm
 
 if HAVE_TRACKER_FTS
 libtracker_data_la_LIBADD +=                           \
diff --git a/src/libtracker-data/tracker-data-update.c b/src/libtracker-data/tracker-data-update.c
index 5cfef9d..c14a8c9 100644
--- a/src/libtracker-data/tracker-data-update.c
+++ b/src/libtracker-data/tracker-data-update.c
@@ -22,6 +22,7 @@
 
 #include <string.h>
 #include <stdlib.h>
+#include <math.h>
 #include <time.h>
 
 #include <libtracker-common/tracker-date-time.h>
@@ -699,7 +700,7 @@ statement_bind_gvalue (TrackerDBStatement *stmt,
 		break;
 	default:
 		if (type == TRACKER_TYPE_DATE_TIME) {
-			tracker_db_statement_bind_int (stmt, (*idx)++, tracker_date_time_get_time (value));
+			tracker_db_statement_bind_double (stmt, (*idx)++, tracker_date_time_get_time (value));
 			tracker_db_statement_bind_int (stmt, (*idx)++, tracker_date_time_get_local_date (value));
 			tracker_db_statement_bind_int (stmt, (*idx)++, tracker_date_time_get_local_time (value));
 		} else {
@@ -1287,8 +1288,9 @@ value_equal (GValue *value1,
 		if (type == TRACKER_TYPE_DATE_TIME) {
 			/* ignore UTC offset for comparison, irrelevant for comparison according to xsd:dateTime spec
 			 * http://www.w3.org/TR/xmlschema-2/#dateTime
+			 * also ignore sub-millisecond as this is a floating point comparison
 			 */
-			return tracker_date_time_get_time (value1) == tracker_date_time_get_time (value2);
+			return fabs (tracker_date_time_get_time (value1) - tracker_date_time_get_time (value2)) < 0.001;
 		}
 		g_assert_not_reached ();
 	}
@@ -1394,9 +1396,13 @@ get_property_values (TrackerProperty *property)
 				tracker_db_cursor_get_value (cursor, 0, &gvalue);
 				if (G_VALUE_TYPE (&gvalue)) {
 					if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME) {
-						gint time;
+						gdouble time;
 
-						time = g_value_get_int64 (&gvalue);
+						if (G_VALUE_TYPE (&gvalue) == G_TYPE_INT64) {
+							time = g_value_get_int64 (&gvalue);
+						} else {
+							time = g_value_get_double (&gvalue);
+						}
 						g_value_unset (&gvalue);
 						g_value_init (&gvalue, TRACKER_TYPE_DATE_TIME);
 						/* UTC offset is irrelevant for comparison */
diff --git a/src/libtracker-data/tracker-sparql-expression.vala b/src/libtracker-data/tracker-sparql-expression.vala
index b40b5cd..3aa6597 100644
--- a/src/libtracker-data/tracker-sparql-expression.vala
+++ b/src/libtracker-data/tracker-sparql-expression.vala
@@ -322,7 +322,7 @@ class Tracker.Sparql.Expression : Object {
 			break;
 		case PropertyType.DATETIME:
 			// ISO 8601 format
-			sql.insert (begin, "strftime (\"%Y-%m-%dT%H:%M:%SZ\", ");
+			sql.insert (begin, "strftime (\"%Y-%m-%dT%H:%M:%fZ\", ");
 			sql.append (", \"unixepoch\")");
 			break;
 		default:
diff --git a/src/libtracker-data/tracker-sparql-query.vala b/src/libtracker-data/tracker-sparql-query.vala
index cab2779..b84103d 100644
--- a/src/libtracker-data/tracker-sparql-query.vala
+++ b/src/libtracker-data/tracker-sparql-query.vala
@@ -514,9 +514,9 @@ public class Tracker.Sparql.Query : Object {
 					throw new Sparql.Error.TYPE ("`%s' is not a valid boolean".printf (binding.literal));
 				}
 			} else if (binding.data_type == PropertyType.DATE) {
-				stmt.bind_int (i, string_to_date (binding.literal + "T00:00:00Z", null));
+				stmt.bind_int (i, (int) string_to_date (binding.literal + "T00:00:00Z", null));
 			} else if (binding.data_type == PropertyType.DATETIME) {
-				stmt.bind_int (i, string_to_date (binding.literal, null));
+				stmt.bind_double (i, string_to_date (binding.literal, null));
 			} else if (binding.data_type == PropertyType.INTEGER) {
 				stmt.bind_int (i, int.parse (binding.literal));
 			} else {
diff --git a/tests/functional-tests/01-insertion.py b/tests/functional-tests/01-insertion.py
index 8d9942e..763e433 100755
--- a/tests/functional-tests/01-insertion.py
+++ b/tests/functional-tests/01-insertion.py
@@ -226,7 +226,7 @@ class TrackerStoreInsertionTests (CommonTrackerStoreTest):
                         self.assertEquals (len (result), 1)
                         self.assertEquals (len (result[0]), 2)
                         self.assertEquals (int (result[0][0]), i)
-                        self.assertEquals (result[0][1], "2000-01-01T00:4%d:47Z" % (i))
+                        self.assertEquals (result[0][1], "2000-01-01T00:4%d:47.000Z" % (i))
 
                 self.tracker.update ("""
                 DELETE { <test://instance-1> a rdfs:Resource. }
@@ -257,7 +257,7 @@ class TrackerStoreInsertionTests (CommonTrackerStoreTest):
                         self.assertEquals (len (result), 1)
                         self.assertEquals (len (result[0]), 2)
                         self.assertEquals (int (result[0][0]), i)
-                        self.assertEquals (result[0][1], "2000-01-01T00:4%d:47Z" % (i))
+                        self.assertEquals (result[0][1], "2000-01-01T00:4%d:47.000Z" % (i))
 
                 self.tracker.update ("""
                 DELETE { <test://instance-1> a rdfs:Resource. }
diff --git a/tests/libtracker-data/subqueries/subqueries-1.out b/tests/libtracker-data/subqueries/subqueries-1.out
index 905166a..c443a3d 100644
--- a/tests/libtracker-data/subqueries/subqueries-1.out
+++ b/tests/libtracker-data/subqueries/subqueries-1.out
@@ -1,2 +1,2 @@
-"http://example.org/ns#group2";	"2009-12-05T00:00:00Z"	"http://example.org/ns#msg5";	"3"
-"http://example.org/ns#group1";	"2009-12-02T00:00:00Z"	"http://example.org/ns#msg2";	"2"
+"http://example.org/ns#group2";	"2009-12-05T00:00:00.000Z"	"http://example.org/ns#msg5";	"3"
+"http://example.org/ns#group1";	"2009-12-02T00:00:00.000Z"	"http://example.org/ns#msg2";	"2"
diff --git a/tests/libtracker-data/subqueries/subqueries-union-2.out b/tests/libtracker-data/subqueries/subqueries-union-2.out
index 2de3f5d..838fe3b 100644
--- a/tests/libtracker-data/subqueries/subqueries-union-2.out
+++ b/tests/libtracker-data/subqueries/subqueries-union-2.out
@@ -1,5 +1,5 @@
-"http://example.org/ns#msg1";	"2009-12-01T00:00:00Z"
-"http://example.org/ns#msg2";	"2009-12-02T00:00:00Z"
-"http://example.org/ns#msg3";	"2009-12-03T00:00:00Z"
-"http://example.org/ns#msg4";	"2009-12-04T00:00:00Z"
-"http://example.org/ns#msg5";	"2009-12-05T00:00:00Z"
+"http://example.org/ns#msg1";	"2009-12-01T00:00:00.000Z"
+"http://example.org/ns#msg2";	"2009-12-02T00:00:00.000Z"
+"http://example.org/ns#msg3";	"2009-12-03T00:00:00.000Z"
+"http://example.org/ns#msg4";	"2009-12-04T00:00:00.000Z"
+"http://example.org/ns#msg5";	"2009-12-05T00:00:00.000Z"



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]