Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for DateOnly and TimeOnly (SqlParameter value and GetFieldValue(Async) ) #1813

Merged
merged 3 commits into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
ErikEJ marked this conversation as resolved.
Show resolved Hide resolved
}
}
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