diff --git a/src/SQLite.cs b/src/SQLite.cs
index 335d5923..60cff5b8 100644
--- a/src/SQLite.cs
+++ b/src/SQLite.cs
@@ -210,6 +210,11 @@ public partial class SQLiteConnection : IDisposable
///
public bool StoreDateTimeAsTicks { get; private set; }
+ ///
+ /// Whether to store TimeSpan properties as ticks (true) or strings (false).
+ ///
+ public bool StoreTimeSpanAsTicks { get; private set; }
+
///
/// The format to use when storing DateTime properties as strings. Ignored if StoreDateTimeAsTicks is true.
///
@@ -560,7 +565,7 @@ public CreateTableResult CreateTable (Type ty, CreateFlags createFlags = CreateF
// Build query.
var query = "create " + @virtual + "table if not exists \"" + map.TableName + "\" " + @using + "(\n";
- var decls = map.Columns.Select (p => Orm.SqlDecl (p, StoreDateTimeAsTicks));
+ var decls = map.Columns.Select (p => Orm.SqlDecl (p, StoreDateTimeAsTicks, StoreTimeSpanAsTicks));
var decl = string.Join (",\n", decls.ToArray ());
query += decl;
query += ")";
@@ -826,7 +831,7 @@ void MigrateTable (TableMapping map, List existingCols)
}
foreach (var p in toBeAdded) {
- var addCol = "alter table \"" + map.TableName + "\" add column " + Orm.SqlDecl (p, StoreDateTimeAsTicks);
+ var addCol = "alter table \"" + map.TableName + "\" add column " + Orm.SqlDecl (p, StoreDateTimeAsTicks, StoreTimeSpanAsTicks);
Execute (addCol);
}
}
@@ -2110,6 +2115,7 @@ public class SQLiteConnectionString
public string UniqueKey { get; }
public string DatabasePath { get; }
public bool StoreDateTimeAsTicks { get; }
+ public bool StoreTimeSpanAsTicks { get; }
public string DateTimeStringFormat { get; }
public System.Globalization.DateTimeStyles DateTimeStyle { get; }
public object Key { get; }
@@ -2219,13 +2225,20 @@ public SQLiteConnectionString (string databasePath, bool storeDateTimeAsTicks, o
///
/// Specifies the format to use when storing DateTime properties as strings.
///
- public SQLiteConnectionString (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks, object key = null, Action preKeyAction = null, Action postKeyAction = null, string vfsName = null, string dateTimeStringFormat = DateTimeSqliteDefaultFormat)
+ ///
+ /// Specifies whether to store TimeSpan properties as ticks (true) or strings (false). You
+ /// absolutely do want to store them as Ticks in all new projects. The value of false is
+ /// only here for backwards compatibility. There is a *significant* speed advantage, with no
+ /// down sides, when setting storeTimeSpanAsTicks = true.
+ ///
+ public SQLiteConnectionString (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks, object key = null, Action preKeyAction = null, Action postKeyAction = null, string vfsName = null, string dateTimeStringFormat = DateTimeSqliteDefaultFormat, bool storeTimeSpanAsTicks = true)
{
if (key != null && !((key is byte[]) || (key is string)))
throw new ArgumentException ("Encryption keys must be strings or byte arrays", nameof (key));
UniqueKey = string.Format ("{0}_{1:X8}", databasePath, (uint)openFlags);
StoreDateTimeAsTicks = storeDateTimeAsTicks;
+ StoreTimeSpanAsTicks = storeTimeSpanAsTicks;
DateTimeStringFormat = dateTimeStringFormat;
DateTimeStyle = "o".Equals (DateTimeStringFormat, StringComparison.OrdinalIgnoreCase) || "r".Equals (DateTimeStringFormat, StringComparison.OrdinalIgnoreCase) ? System.Globalization.DateTimeStyles.RoundtripKind : System.Globalization.DateTimeStyles.None;
Key = key;
@@ -2623,9 +2636,9 @@ public static Type GetType (object obj)
return obj.GetType ();
}
- public static string SqlDecl (TableMapping.Column p, bool storeDateTimeAsTicks)
+ public static string SqlDecl (TableMapping.Column p, bool storeDateTimeAsTicks, bool storeTimeSpanAsTicks)
{
- string decl = "\"" + p.Name + "\" " + SqlType (p, storeDateTimeAsTicks) + " ";
+ string decl = "\"" + p.Name + "\" " + SqlType (p, storeDateTimeAsTicks, storeTimeSpanAsTicks) + " ";
if (p.IsPK) {
decl += "primary key ";
@@ -2643,7 +2656,7 @@ public static string SqlDecl (TableMapping.Column p, bool storeDateTimeAsTicks)
return decl;
}
- public static string SqlType (TableMapping.Column p, bool storeDateTimeAsTicks)
+ public static string SqlType (TableMapping.Column p, bool storeDateTimeAsTicks, bool storeTimeSpanAsTicks)
{
var clrType = p.ColumnType;
if (clrType == typeof (Boolean) || clrType == typeof (Byte) || clrType == typeof (UInt16) || clrType == typeof (SByte) || clrType == typeof (Int16) || clrType == typeof (Int32) || clrType == typeof (UInt32) || clrType == typeof (Int64)) {
@@ -2661,7 +2674,7 @@ public static string SqlType (TableMapping.Column p, bool storeDateTimeAsTicks)
return "varchar";
}
else if (clrType == typeof (TimeSpan)) {
- return "bigint";
+ return storeTimeSpanAsTicks ? "bigint" : "time";
}
else if (clrType == typeof (DateTime)) {
return storeDateTimeAsTicks ? "bigint" : "datetime";
@@ -2947,13 +2960,13 @@ void BindAll (Sqlite3Statement stmt)
b.Index = nextIdx++;
}
- BindParameter (stmt, b.Index, b.Value, _conn.StoreDateTimeAsTicks, _conn.DateTimeStringFormat);
+ BindParameter (stmt, b.Index, b.Value, _conn.StoreDateTimeAsTicks, _conn.DateTimeStringFormat, _conn.StoreTimeSpanAsTicks);
}
}
static IntPtr NegativePointer = new IntPtr (-1);
- internal static void BindParameter (Sqlite3Statement stmt, int index, object value, bool storeDateTimeAsTicks, string dateTimeStringFormat)
+ internal static void BindParameter (Sqlite3Statement stmt, int index, object value, bool storeDateTimeAsTicks, string dateTimeStringFormat, bool storeTimeSpanAsTicks)
{
if (value == null) {
SQLite3.BindNull (stmt, index);
@@ -2978,7 +2991,12 @@ internal static void BindParameter (Sqlite3Statement stmt, int index, object val
SQLite3.BindDouble (stmt, index, Convert.ToDouble (value));
}
else if (value is TimeSpan) {
- SQLite3.BindInt64 (stmt, index, ((TimeSpan)value).Ticks);
+ if (storeTimeSpanAsTicks) {
+ SQLite3.BindInt64 (stmt, index, ((TimeSpan)value).Ticks);
+ }
+ else {
+ SQLite3.BindText (stmt, index, ((TimeSpan)value).ToString (), -1, NegativePointer);
+ }
}
else if (value is DateTime) {
if (storeDateTimeAsTicks) {
@@ -3061,7 +3079,17 @@ object ReadCol (Sqlite3Statement stmt, int index, SQLite3.ColType type, Type clr
return (float)SQLite3.ColumnDouble (stmt, index);
}
else if (clrType == typeof (TimeSpan)) {
- return new TimeSpan (SQLite3.ColumnInt64 (stmt, index));
+ if (_conn.StoreTimeSpanAsTicks) {
+ return new TimeSpan (SQLite3.ColumnInt64 (stmt, index));
+ }
+ else {
+ var text = SQLite3.ColumnString (stmt, index);
+ TimeSpan resultTime;
+ if (!TimeSpan.TryParseExact (text, "c", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.TimeSpanStyles.None, out resultTime)) {
+ resultTime = TimeSpan.Parse (text);
+ }
+ return resultTime;
+ }
}
else if (clrType == typeof (DateTime)) {
if (_conn.StoreDateTimeAsTicks) {
@@ -3174,7 +3202,7 @@ public int ExecuteNonQuery (object[] source)
//bind the values.
if (source != null) {
for (int i = 0; i < source.Length; i++) {
- SQLiteCommand.BindParameter (Statement, i + 1, source[i], Connection.StoreDateTimeAsTicks, Connection.DateTimeStringFormat);
+ SQLiteCommand.BindParameter (Statement, i + 1, source[i], Connection.StoreDateTimeAsTicks, Connection.DateTimeStringFormat, Connection.StoreTimeSpanAsTicks);
}
}
r = SQLite3.Step (Statement);
diff --git a/src/SQLiteAsync.cs b/src/SQLiteAsync.cs
index 77255098..ac82d828 100644
--- a/src/SQLiteAsync.cs
+++ b/src/SQLiteAsync.cs
@@ -137,6 +137,11 @@ public Task EnableWriteAheadLoggingAsync ()
/// Whether to store DateTime properties as ticks (true) or strings (false).
///
public bool StoreDateTimeAsTicks => GetConnection ().StoreDateTimeAsTicks;
+
+ ///
+ /// Whether to store TimeSpan properties as ticks (true) or strings (false).
+ ///
+ public bool StoreTimeSpanAsTicks => GetConnection ().StoreTimeSpanAsTicks;
///
/// Whether to writer queries to during execution.
diff --git a/tests/SQLite.Tests.csproj b/tests/SQLite.Tests.csproj
index 77d8dc7a..06e4de58 100644
--- a/tests/SQLite.Tests.csproj
+++ b/tests/SQLite.Tests.csproj
@@ -84,6 +84,7 @@
+
diff --git a/tests/TimeSpanTest.cs b/tests/TimeSpanTest.cs
new file mode 100644
index 00000000..af169a17
--- /dev/null
+++ b/tests/TimeSpanTest.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Threading.Tasks;
+
+#if NETFX_CORE
+using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
+using SetUp = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestInitializeAttribute;
+using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute;
+using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute;
+#else
+using NUnit.Framework;
+#endif
+
+namespace SQLite.Tests
+{
+ [TestFixture]
+ public class TimeSpanTest
+ {
+ class TestObj
+ {
+ [PrimaryKey, AutoIncrement]
+ public int Id { get; set; }
+
+ public string Name { get; set; }
+ public TimeSpan Duration { get; set; }
+ }
+
+ [Test]
+ public void AsTicks ()
+ {
+ var db = new TestDb (TimeSpanAsTicks (true));
+ TestTimeSpan (db);
+ }
+
+ [Test]
+ public void AsStrings ()
+ {
+ var db = new TestDb (TimeSpanAsTicks (false));
+ TestTimeSpan (db);
+ }
+
+ [Test]
+ public void AsyncAsTicks ()
+ {
+ var db = new SQLiteAsyncConnection (TimeSpanAsTicks (true));
+ TestAsyncTimeSpan (db);
+ }
+
+ [Test]
+ public void AsyncAsStrings ()
+ {
+ var db = new SQLiteAsyncConnection (TimeSpanAsTicks (false));
+ TestAsyncTimeSpan (db);
+ }
+
+ SQLiteConnectionString TimeSpanAsTicks (bool asTicks = true) => new SQLiteConnectionString (TestPath.GetTempFileName (), SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite, true, storeTimeSpanAsTicks: asTicks);
+
+ void TestAsyncTimeSpan (SQLiteAsyncConnection db)
+ {
+ db.CreateTableAsync ().Wait ();
+
+ TestObj o, o2;
+
+ o = new TestObj {
+ Duration = new TimeSpan (42, 12, 33, 20, 501),
+ };
+ db.InsertAsync (o).Wait ();
+ o2 = db.GetAsync (o.Id).Result;
+ Assert.AreEqual (o.Duration, o2.Duration);
+ }
+
+ void TestTimeSpan (TestDb db)
+ {
+ db.CreateTable ();
+
+ TestObj o, o2;
+
+ o = new TestObj {
+ Duration = new TimeSpan (42, 12, 33, 20, 501),
+ };
+ db.Insert (o);
+ o2 = db.Get (o.Id);
+ Assert.AreEqual (o.Duration, o2.Duration);
+ }
+ }
+}