question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Microsoft.Data.Sqlite SqliteConnection does not support DateTime

See original GitHub issue

Microsoft.Data.Sqlite 6.0 .NET 6.0

If we save a datetime value to Sqlite database (integer column type), and then use Microsoft.Data.Sqlite.SqliteConnection to read it, Microsoft.Data.Sqlite will convert it to a string.

Tested:

  1. SqliteCommand.ExecuteScalar
  2. DbDataAdapter.Fill DataSet/DataTable

C# code:

using Microsoft.Data.Sqlite;
using System.Data;
using System.Data.Common;

namespace TestNamespace
{
    public class DataAdapter : DbDataAdapter
    {

    }

    public class TestClass
    {
        public static void Main()
        {
            var conn = new SqliteConnection("Data Source=:memory:");
            conn.Open();

            SqliteCommand cmd;
            object? data;

            cmd = conn.CreateCommand();
            cmd.CommandText = @"CREATE TABLE UserTable (""CreationTime"" integer)";
            cmd.ExecuteNonQuery();

            cmd = conn.CreateCommand();
            cmd.CommandText = @"INSERT INTO UserTable values (@P1)";
            cmd.Parameters.Add("P1", SqliteType.Integer).Value = DateTime.Now;
            cmd.ExecuteNonQuery();

            cmd = conn.CreateCommand();
            cmd.CommandText = "select CreationTime from UserTable";
            data = cmd.ExecuteScalar();

            Console.WriteLine("Use ExecuteScalar:");
            Console.WriteLine("Type: " + data?.GetType());
            Console.WriteLine("Value: " + data);
            Console.WriteLine();

            cmd = conn.CreateCommand();
            cmd.CommandText = "select * from UserTable";

            var da = new DataAdapter();
            da.SelectCommand = cmd;

            var dataTable = new DataTable();
            da.Fill(dataTable);

            Console.WriteLine("Use DataAdapter.Fill DataSet or DataTable:");
            Console.WriteLine("Type: " + dataTable.Rows[0][0]?.GetType());
            Console.WriteLine("Value: " + dataTable.Rows[0][0]);
        }
    }
}

The Console displays:

Type: System.String

It cannot directly restore the data to Datetime. For all tables, it is still necessary to convert the string to DateTime again.

Or can Microsoft.Data.Sqlite directly convert it to DateTime?

p.s.

The Sqlite offical library can provide this API to convert DateTime value. Not sure if it will affect the datetime data.

https://system.data.sqlite.org/

using System.Data.SQLite;

var stringBuilder = new SQLiteConnectionStringBuilder();

stringBuilder.DateTimeFormat = SQLiteDateFormats.Ticks;
stringBuilder.DateTimeKind = DateTimeKind.Utc;
    //
    //     This implementation of SQLite for ADO.NET can process date/time fields in databases
    //     in one of six formats.
    //
    //     ISO8601 format is more compatible, readable, fully-processable, but less accurate
    //     as it does not provide time down to fractions of a second. JulianDay is the numeric
    //     format the SQLite uses internally and is arguably the most compatible with 3rd
    //     party tools. It is not readable as text without post-processing. Ticks less compatible
    //     with 3rd party tools that query the database, and renders the DateTime field
    //     unreadable as text without post-processing. UnixEpoch is more compatible with
    //     Unix systems. InvariantCulture allows the configured format for the invariant
    //     culture format to be used and is human readable. CurrentCulture allows the configured
    //     format for the current culture to be used and is also human readable. The preferred
    //     order of choosing a DateTime format is JulianDay, ISO8601, and then Ticks. Ticks
    //     is mainly present for legacy code support.
    public enum SQLiteDateFormats
    {
        //
        //     Use the value of DateTime.Ticks. This value is not recommended and is not well
        //     supported with LINQ.
        Ticks = 0,
        //
        //     Use the ISO-8601 format. Uses the "yyyy-MM-dd HH:mm:ss.FFFFFFFK" format for UTC
        //     DateTime values and "yyyy-MM-dd HH:mm:ss.FFFFFFF" format for local DateTime values).
        ISO8601 = 1,
        //
        //     The interval of time in days and fractions of a day since January 1, 4713 BC.
        JulianDay = 2,
        //
        //     The whole number of seconds since the Unix epoch (January 1, 1970).
        UnixEpoch = 3,
        //
        //     Any culture-independent string value that the .NET Framework can interpret as
        //     a valid DateTime.
        InvariantCulture = 4,
        //
        //     Any string value that the .NET Framework can interpret as a valid DateTime using
        //     the current culture.
        CurrentCulture = 5,
        //
        //     The default format for this provider.
        Default = 1
    }

And it supports UTC Time:

    //
    //     Specifies whether a System.DateTime object represents a local time, a Coordinated
    //     Universal Time (UTC), or is not specified as either local time or UTC.
    public enum DateTimeKind
    {
        //
        //     The time represented is not specified as either local time or Coordinated Universal
        //     Time (UTC).
        Unspecified,
        //
        //     The time represented is UTC.
        Utc,
        //
        //     The time represented is local time.
        Local
    }

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
rojicommented, Dec 2, 2021

but if you use DbDataAdapter to fill in the DataSet or DataTable, you will also get the string type, which cannot be automated.

DbDataAdapter is generally considered legacy - I’d recommend considering either using DbDataReader directly (DbDataAdapter is a layer on top of that), or better yet, a data technology like Dapper or Entity Framework Core. I’m not sure, but there may be a way of configuring DbDataAdapter with the expected data type per column, which would help in this case.

Unfortunately, this really is a limitation of the Sqlite database itself, since it doesn’t actually have a timestamp type.

Of course there is only one field in this table. What if there are more than ten fields.

If you are selecting more than one field, then you need to use ExecuteReader in any case - ExecuteScalar cannot be used to select more than one value. At that point you can simply call reader.GetFieldValue<DateTime>() yourself. This really is

1reaction
rojicommented, Dec 2, 2021

@roland5572 since SQLite has no actual timestamp type in the database, Microsoft.Data.Sqlite has no way of knowing that it should return a DateTime here; this is why you need to tell it that via GetFieldValue.

You can easily wrap the above in a simple extension method, such as the following:

static class SqliteCommandExtensions
{
    public static DateTime ExecuteScalarDateTime(this SqliteCommand command)
    {
        using var reader = command.ExecuteReader();
        reader.Read();
        return reader.GetFieldValue<DateTime>(0);
    }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

sqlite throwing a "String not recognized as a valid datetime"
It seems that System.Data.SQLite does not handle DateTime s correctly. I tried DateTime.ParseExact and gave the format in my database (mm/dd ...
Read more >
It should be documented that the connection string must ...
ArgumentException: Keyword not supported: 'datetimeformat'.\r\n at Microsoft.Data.Sqlite.SqliteConnectionStringBuilder.GetIndex(String keyword)\ ...
Read more >
Transactions - Microsoft.Data.Sqlite
Transactions let you group multiple SQL statements into a single unit of work that is committed to the database as one atomic unit....
Read more >
Auto populate from sqlite database - Microsoft Q&A
I'm using dictionary and item list to pull information from SQLite database. Populating number and time is no issue the rest of the...
Read more >
The "String was not recognized as a valid DateTime" error ...
The "String was not recognized as a valid DateTime" error occurs in non invariant culture if schedulerCOntrol is connected to SQLite.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found