Commit 6d75067b authored by Jürg Billeter's avatar Jürg Billeter

libtracker-data: Support fractional seconds in xsd:dateTime values

Fixes NB#290480.
parent 665ac21b
......@@ -66,7 +66,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
......
......@@ -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 {
......
......@@ -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);
}
......@@ -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
......
......@@ -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 += \
......
......@@ -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 */
......
......@@ -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:
......
......@@ -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 {
......
......@@ -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. }
......
"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"
"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"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment