Skip to content

Commit

Permalink
Add support for DateOnly and TimeOnly (SqlParameter value and GetFiel…
Browse files Browse the repository at this point in the history
…dValue(Async) ) (#1813)
  • Loading branch information
ErikEJ authored Nov 3, 2022
1 parent 708cf3a commit 1c03b1d
Show file tree
Hide file tree
Showing 8 changed files with 566 additions and 21 deletions.
32 changes: 16 additions & 16 deletions doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml
Original file line number Diff line number Diff line change
Expand Up @@ -327,14 +327,14 @@
|||||
|-|-|-|-|
|Boolean|Byte|Char|DateTime|
|DateTimeOffset|Decimal|Double|Float|
|Guid|Int16|Int32|Int64|
|SqlBoolean|SqlByte|SqlDateTime|SqlDecimal|
|SqlDouble|SqlGuid|SqlInt16|SqlInt32|
|SqlInt64|SqlMoney|SqlSingle|SqlString|
|Stream|String|TextReader|UDT, which can be any CLR type marked with <xref:Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute>.|
|XmlReader||||
|Boolean|Byte|Char|DateOnly (.NET 6 or later)|
|DateTime|DateTimeOffset|Decimal|Double|
|Float|Guid|Int16|Int32|
|Int64|SqlBoolean|SqlByte|SqlDateTime|
|SqlDecimal|SqlDouble|SqlGuid|SqlInt16|
|SqlInt32|SqlInt64|SqlMoney|SqlSingle|
|SqlString|Stream|String|TextReader|
|TimeOnly (.NET 6 or later)|XmlReader||UDT, which can be any CLR type marked with <xref:Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute>.|
For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
Expand Down Expand Up @@ -369,14 +369,14 @@
|||||
|-|-|-|-|
|Boolean|Byte|Char|DateTime|
|DateTimeOffset|Decimal|Double|Float|
|Guid|Int16|Int32|Int64|
|SqlBoolean|SqlByte|SqlDateTime|SqlDecimal|
|SqlDouble|SqlGuid|SqlInt16|SqlInt32|
|SqlInt64|SqlMoney|SqlSingle|SqlString|
|Stream|String|TextReader|UDT, which can be any CLR type marked with <xref:Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute>.|
|XmlReader||||
|Boolean|Byte|Char|DateOnly (.NET 6 or later)|
|DateTime|DateTimeOffset|Decimal|Double|
|Float|Guid|Int16|Int32|
|Int64|SqlBoolean|SqlByte|SqlDateTime|
|SqlDecimal|SqlDouble|SqlGuid|SqlInt16|
|SqlInt32|SqlInt64|SqlMoney|SqlSingle|
|SqlString|Stream|String|TextReader|
|TimeOnly (.NET 6 or later)|XmlReader||UDT, which can be any CLR type marked with <xref:Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute>.|
For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2843,6 +2843,16 @@ private T GetFieldValueFromSqlBufferInternal<T>(SqlBuffer data, _SqlMetaData met
{
return (T)(object)data.DateTime;
}
#if NET6_0_OR_GREATER
else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType)
{
return (T)(object)data.DateOnly;
}
else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType)
{
return (T)(object)data.TimeOnly;
}
#endif
else if (typeof(T) == typeof(XmlReader))
{
// XmlReader only allowed on XML types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,37 @@ internal TimeSpan Time
}
}

#if NET6_0_OR_GREATER
internal TimeOnly TimeOnly
{
get
{
ThrowIfNull();

if (StorageType.Time == _type)
{
return new TimeOnly(_value._timeInfo._ticks);
}

return (TimeOnly)Value; // anything else we haven't thought of goes through boxing.
}
}

internal DateOnly DateOnly
{
get
{
ThrowIfNull();

if (StorageType.Date == _type)
{
return DateOnly.MinValue.AddDays(_value._int32);
}
return (DateOnly)Value; // anything else we haven't thought of goes through boxing.
}
}
#endif

internal DateTimeOffset DateTimeOffset
{
get
Expand Down Expand Up @@ -1097,7 +1128,7 @@ internal Type GetTypeFromStorageType(bool isSqlType)
return typeof(SqlGuid);
case StorageType.SqlXml:
return typeof(SqlXml);
// Date DateTime2 and DateTimeOffset have no direct Sql type to contain them
// Time Date DateTime2 and DateTimeOffset have no direct Sql type to contain them
}
}
else
Expand Down Expand Up @@ -1144,6 +1175,10 @@ internal Type GetTypeFromStorageType(bool isSqlType)
return typeof(DateTime);
case StorageType.DateTimeOffset:
return typeof(DateTimeOffset);
#if NET6_0_OR_GREATER
case StorageType.Time:
return typeof(TimeOnly);
#endif
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,16 @@ private static MetaType GetMetaTypeFromValue(Type dataType, object value, bool i
{
return MetaDateTimeOffset;
}
#if NET6_0_OR_GREATER
else if (dataType == typeof(DateOnly))
{
return s_metaDate;
}
else if (dataType == typeof(TimeOnly))
{
return MetaTime;
}
#endif
else
{
// UDT ?
Expand Down Expand Up @@ -630,6 +640,10 @@ internal static object GetSqlValueFromComVariant(object comVal)
break;
case TimeSpan:
case DateTimeOffset:
#if NET6_0_OR_GREATER
case TimeOnly:
case DateOnly:
#endif
sqlVal = comVal;
break;
default:
Expand Down Expand Up @@ -739,7 +753,7 @@ internal static SqlDbType GetSqlDbTypeFromOleDbType(short dbType, string typeNam
break; // no direct mapping, just use SqlDbType.Variant;
}
return sqlType;
#else
#else
// OleDbTypes not supported
return SqlDbType.Variant;
#endif // NETFRAMEWORK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2252,6 +2252,16 @@ internal static object CoerceValue(object value, MetaType destinationType, out b
{
value = new DateTimeOffset((DateTime)value);
}
#if NET6_0_OR_GREATER
else if ((currentType == typeof(DateOnly)) && (destinationType.SqlDbType == SqlDbType.Date))
{
value = ((DateOnly)value).ToDateTime(new TimeOnly(0, 0));
}
else if ((currentType == typeof(TimeOnly)) && (destinationType.SqlDbType == SqlDbType.Time))
{
value = ((TimeOnly)value).ToTimeSpan();
}
#endif
else if (
TdsEnums.SQLTABLE == destinationType.TDSType &&
(
Expand Down
110 changes: 110 additions & 0 deletions src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,64 @@ public void Constructor2_Value_DateTime()
Assert.Equal(string.Empty, p.XmlSchemaCollectionOwningSchema);
}

#if NET6_0_OR_GREATER
[Fact]
public void Constructor2_Value_DateOnly()
{
DateOnly value = new DateOnly(2004, 8, 24);
SqlParameter p = new SqlParameter("dateonly", value);

Assert.Equal(DbType.Date, p.DbType);
Assert.Equal(ParameterDirection.Input, p.Direction);
Assert.False(p.IsNullable);
Assert.Equal(0, p.LocaleId);
Assert.Equal(0, p.Offset);
Assert.Equal("dateonly", p.ParameterName);
Assert.Equal(0, p.Precision);
Assert.Equal(0, p.Scale);
Assert.Equal(0, p.Size);
Assert.Equal(string.Empty, p.SourceColumn);
Assert.False(p.SourceColumnNullMapping);
Assert.Equal(DataRowVersion.Current, p.SourceVersion);
Assert.Equal(SqlDbType.Date, p.SqlDbType);
Assert.Equal(value, p.SqlValue);
Assert.Equal(string.Empty, p.TypeName);
Assert.Equal(string.Empty, p.UdtTypeName);
Assert.Equal(value, p.Value);
Assert.Equal(string.Empty, p.XmlSchemaCollectionDatabase);
Assert.Equal(string.Empty, p.XmlSchemaCollectionName);
Assert.Equal(string.Empty, p.XmlSchemaCollectionOwningSchema);
}

[Fact]
public void Constructor2_Value_TimeOnly()
{
TimeOnly value = new TimeOnly(9, 7, 42, 321);
SqlParameter p = new SqlParameter("timeonly", value);

Assert.Equal(DbType.Time, p.DbType);
Assert.Equal(ParameterDirection.Input, p.Direction);
Assert.False(p.IsNullable);
Assert.Equal(0, p.LocaleId);
Assert.Equal(0, p.Offset);
Assert.Equal("timeonly", p.ParameterName);
Assert.Equal(0, p.Precision);
Assert.Equal(0, p.Scale);
Assert.Equal(0, p.Size);
Assert.Equal(string.Empty, p.SourceColumn);
Assert.False(p.SourceColumnNullMapping);
Assert.Equal(DataRowVersion.Current, p.SourceVersion);
Assert.Equal(SqlDbType.Time, p.SqlDbType);
Assert.Equal(value, p.SqlValue);
Assert.Equal(string.Empty, p.TypeName);
Assert.Equal(string.Empty, p.UdtTypeName);
Assert.Equal(value, p.Value);
Assert.Equal(string.Empty, p.XmlSchemaCollectionDatabase);
Assert.Equal(string.Empty, p.XmlSchemaCollectionName);
Assert.Equal(string.Empty, p.XmlSchemaCollectionOwningSchema);
}
#endif

[Fact]
public void Constructor2_Value_Null()
{
Expand Down Expand Up @@ -383,6 +441,58 @@ public void InferType_CharArray()
Assert.Equal(value, p.Value);
}

#if NET6_0_OR_GREATER
[Fact]
public void InferType_DateOnly()
{
DateOnly value;
SqlParameter param;

value = DateOnly.FromDateTime(DateTime.Now.Date);
param = new SqlParameter();
param.Value = value;
Assert.Equal(SqlDbType.Date, param.SqlDbType);
Assert.Equal(DbType.Date, param.DbType);

value = DateOnly.FromDateTime(DateTime.Now.Date);
param = new SqlParameter();
param.Value = value;
Assert.Equal(SqlDbType.Date, param.SqlDbType);
Assert.Equal(DbType.Date, param.DbType);

value = DateOnly.FromDateTime(new DateTime(1973, 8, 13));
param = new SqlParameter();
param.Value = value;
Assert.Equal(SqlDbType.Date, param.SqlDbType);
Assert.Equal(DbType.Date, param.DbType);
}

[Fact]
public void InferType_TimeOnly()
{
TimeOnly value;
SqlParameter param;

value = TimeOnly.FromDateTime(DateTime.Now);
param = new SqlParameter();
param.Value = value;
Assert.Equal(SqlDbType.Time, param.SqlDbType);
Assert.Equal(DbType.Time, param.DbType);

value = TimeOnly.FromDateTime(DateTime.Now);
param = new SqlParameter();
param.Value = value;
Assert.Equal(SqlDbType.Time, param.SqlDbType);
Assert.Equal(DbType.Time, param.DbType);

value = TimeOnly.FromDateTime(new DateTime(2022, 10, 22, 15, 27, 38));
param = new SqlParameter();
param.Value = value;
Assert.Equal(SqlDbType.Time, param.SqlDbType);
Assert.Equal(DbType.Time, param.DbType);
}
#endif

[Fact]
public void InferType_DateTime()
{
Expand Down
Loading

0 comments on commit 1c03b1d

Please sign in to comment.