Skip to content

Commit

Permalink
Add reader functions for DateTimeOffset. Fixes #175
Browse files Browse the repository at this point in the history
  • Loading branch information
caleblloyd authored and bgrainger committed Feb 8, 2017
1 parent 48603cb commit ee218ed
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 6 deletions.
4 changes: 4 additions & 0 deletions docs/content/api/mysql-data-reader.md
Expand Up @@ -22,3 +22,7 @@ Additionally, MySqlDataReader provides the following public properties and metho

Gets the value of the specified column as an sbyte
***
`public DateTimeOffset GetDateTimeOffset(int ordinal)`

Gets the value of the specified column as a DateTimeOffset with an offset of 0
***
10 changes: 10 additions & 0 deletions src/MySqlConnector/MySqlClient/MySqlDataReader.cs
Expand Up @@ -169,6 +169,8 @@ protected override DbDataReader GetDbDataReader(int ordinal)

public override DateTime GetDateTime(int ordinal) => GetResultSet().GetCurrentRow().GetDateTime(ordinal);

public DateTimeOffset GetDateTimeOffset(int ordinal) => GetResultSet().GetCurrentRow().GetDateTimeOffset(ordinal);

public override string GetString(int ordinal) => GetResultSet().GetCurrentRow().GetString(ordinal);

public override decimal GetDecimal(int ordinal) => GetResultSet().GetCurrentRow().GetDecimal(ordinal);
Expand All @@ -191,6 +193,14 @@ public override void Close()
}
#endif

public override T GetFieldValue<T>(int ordinal)
{
if (typeof(T) == typeof(DateTimeOffset))
return (T) Convert.ChangeType(GetDateTimeOffset(ordinal), typeof(T));

return base.GetFieldValue<T>(ordinal);
}

protected override void Dispose(bool disposing)
{
try
Expand Down
5 changes: 5 additions & 0 deletions src/MySqlConnector/MySqlClient/Results/Row.cs
Expand Up @@ -229,6 +229,11 @@ public DateTime GetDateTime(int ordinal)
return (DateTime) GetValue(ordinal);
}

public DateTimeOffset GetDateTimeOffset(int ordinal)
{
return new DateTimeOffset(DateTime.SpecifyKind(GetDateTime(ordinal), DateTimeKind.Utc));
}

public string GetString(int ordinal)
{
return (string) GetValue(ordinal);
Expand Down
76 changes: 70 additions & 6 deletions tests/SideBySide.New/DataTypes.cs
Expand Up @@ -2,6 +2,7 @@
using System.Data.Common;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Dapper;
using MySql.Data.MySqlClient;
Expand Down Expand Up @@ -421,6 +422,9 @@ public async Task GetGuid(string column, Type fieldType)
public void QueryDate(string column, string dataTypeName, object[] expected)
{
DoQuery("times", column, dataTypeName, ConvertToDateTime(expected), reader => reader.GetDateTime(0));
#if !BASELINE
DoQuery("times", column, dataTypeName, ConvertToDateTimeOffset(expected), reader => (reader as MySqlDataReader).GetDateTimeOffset(0), matchesDefaultType: false);
#endif
}

[Theory]
Expand All @@ -436,13 +440,17 @@ public void QueryZeroDateTime(bool convertZeroDateTime)
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = @"select cast(0 as date), cast(0 as datetime);";
using (var reader = cmd.ExecuteReader())
using (var reader = cmd.ExecuteReader() as MySqlDataReader)
{
Assert.True(reader.Read());
if (convertZeroDateTime)
{
Assert.Equal(DateTime.MinValue, reader.GetDateTime(0));
Assert.Equal(DateTime.MinValue, reader.GetDateTime(1));
#if !BASELINE
Assert.Equal(DateTimeOffset.MinValue, reader.GetDateTimeOffset(0));
Assert.Equal(DateTimeOffset.MinValue, reader.GetDateTimeOffset(1));
#endif
}
else
{
Expand All @@ -452,6 +460,8 @@ public void QueryZeroDateTime(bool convertZeroDateTime)
#else
Assert.Throws<InvalidCastException>(() => reader.GetDateTime(0));
Assert.Throws<InvalidCastException>(() => reader.GetDateTime(1));
Assert.Throws<InvalidCastException>(() => reader.GetDateTimeOffset(0));
Assert.Throws<InvalidCastException>(() => reader.GetDateTimeOffset(1));
#endif
}
}
Expand Down Expand Up @@ -598,14 +608,32 @@ private static byte[] GetBytes(DbDataReader reader)
return result;
}

private void DoQuery(string table, string column, string dataTypeName, object[] expected, Func<DbDataReader, object> getValue, object baselineCoercedNullValue = null, bool omitWhereTest = false, MySqlConnection connection=null)
private void DoQuery(
string table,
string column,
string dataTypeName,
object[] expected,
Func<DbDataReader, object> getValue,
object baselineCoercedNullValue = null,
bool omitWhereTest = false,
bool matchesDefaultType = true,
MySqlConnection connection=null)
{
DoQuery<GetValueWhenNullException>(table, column, dataTypeName, expected, getValue, baselineCoercedNullValue, omitWhereTest, connection);
DoQuery<GetValueWhenNullException>(table, column, dataTypeName, expected, getValue, baselineCoercedNullValue, omitWhereTest, matchesDefaultType, connection);
}

// NOTE: baselineCoercedNullValue is to work around inconsistencies in mysql-connector-net; DBNull.Value will
// be coerced to 0 by some reader.GetX() methods, but not others.
private void DoQuery<TException>(string table, string column, string dataTypeName, object[] expected, Func<DbDataReader, object> getValue, object baselineCoercedNullValue = null, bool omitWhereTest = false, MySqlConnection connection=null)
private void DoQuery<TException>(
string table,
string column,
string dataTypeName,
object[] expected,
Func<DbDataReader, object> getValue,
object baselineCoercedNullValue = null,
bool omitWhereTest = false,
bool matchesDefaultType = true,
MySqlConnection connection=null)
where TException : Exception
{
connection = connection ?? m_database.Connection;
Expand All @@ -632,9 +660,33 @@ private void DoQuery<TException>(string table, string column, string dataTypeNam
}
else
{
Assert.Equal(value, reader.GetValue(0));
Assert.Equal(value, getValue(reader));
Assert.Equal(value.GetType(), reader.GetFieldType(0));

// test `reader.GetValue` and `reader.GetFieldType` if value matches default type
if (matchesDefaultType)
{
Assert.Equal(value, reader.GetValue(0));
Assert.Equal(value.GetType(), reader.GetFieldType(0));
}

// test `reader.GetFieldValue<value.GetType()>`
var syncMethod = typeof(MySqlDataReader)
.GetMethod("GetFieldValue")
.MakeGenericMethod(value.GetType());
Assert.Equal(value, syncMethod.Invoke(reader, new object[]{ 0 }));

// test `reader.GetFieldValueAsync<value.GetType()>`
var asyncMethod = typeof(MySqlDataReader)
.GetMethod("GetFieldValueAsync", new []{ typeof(int) })
.MakeGenericMethod(value.GetType());
var asyncMethodValue = asyncMethod.Invoke(reader, new object[]{ 0 });
var asyncMethodGetAwaiter = asyncMethodValue.GetType()
.GetMethod("GetAwaiter");
var asyncMethodGetAwaiterValue = asyncMethodGetAwaiter.Invoke(asyncMethodValue, new object[]{ });
var asyncMethodGetResult = asyncMethodGetAwaiterValue.GetType()
.GetMethod("GetResult");
var asyncMethodGetResultValue = asyncMethodGetResult.Invoke(asyncMethodGetAwaiterValue, new object[]{ });
Assert.Equal(value, asyncMethodGetResultValue);
}
}
Assert.False(reader.Read());
Expand Down Expand Up @@ -670,6 +722,18 @@ private static object[] ConvertToDateTime(object[] input)
return output;
}

private static object[] ConvertToDateTimeOffset(object[] input)
{
var output = new object[input.Length];
var dateTimes = ConvertToDateTime(input);
for (int i = 0; i < dateTimes.Length; i++)
{
if (dateTimes[i] != null)
output[i] = new DateTimeOffset(DateTime.SpecifyKind((DateTime)dateTimes[i], DateTimeKind.Utc));
}
return output;
}

private static object[] ConvertToTimeSpan(object[] input)
{
var output = new object[input.Length];
Expand Down

0 comments on commit ee218ed

Please sign in to comment.