diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs
index f662a5d5..7acb9559 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs
@@ -81,7 +81,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{typeof(float), (o, id, col) => ReadSingle(o, id)},
{typeof(decimal), (o, id, col) => ReadDecimal(o, id)},
{typeof(DateTime), ReadDateTime},
- {typeof(DateTimeOffset), (o, id, col) => ReadDateTimeOffset(o, id)},
+ {typeof(DateTimeOffset), ReadDateTimeOffset},
{typeof(TimeSpan), (o, id, col) => ReadTimeSpan(o, id)},
{typeof(byte[]), (o, id, col) => ReadBytes(o, id)},
{typeof(Guid), (o, id, col) => ReadGuid(o, id)},
@@ -454,8 +454,9 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
///
/// Offset into the file to read the DateTimeOffset from
/// Internal ID of the row that will be stored in the cell
+ /// Column metadata, used for determining what precision to output
/// A DateTimeOffset
- internal FileStreamReadResult ReadDateTimeOffset(long offset, long rowId)
+ internal FileStreamReadResult ReadDateTimeOffset(long offset, long rowId, DbColumnWrapper col)
{
// DateTimeOffset is represented by DateTime.Ticks followed by TimeSpan.Ticks
// both as Int64 values
@@ -466,8 +467,14 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
return new DateTimeOffset(new DateTime(dtTicks), new TimeSpan(dtOffset));
}, null, dt =>
{
- string formatString = $"{DateFormatString} {TimeFormatString}.fffffff zzz";
-
+ int scale = Math.Min(col.NumericScale ?? 7, 7);
+ string formatString = $"{DateFormatString} {TimeFormatString}";
+ if (scale > 0)
+ {
+ string millisecondString = new string('f', scale);
+ formatString += $".{millisecondString}";
+ }
+ formatString += " zzz";
return dt.ToString(formatString);
});
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/QueryExecution/DataTypeTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/QueryExecution/DataTypeTests.cs
index fe8a645c..815751bd 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/QueryExecution/DataTypeTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/QueryExecution/DataTypeTests.cs
@@ -68,6 +68,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.QueryExecution
public async Task DateTimeOffsetTest()
{
await ExecuteAndVerifyResult("SELECT CAST('2020-01-01' AS DATETIMEOFFSET)", "2020-01-01 00:00:00.0000000 +00:00");
+ await ExecuteAndVerifyResult("SELECT CAST('2020-01-01' AS DATETIMEOFFSET(6))", "2020-01-01 00:00:00.000000 +00:00");
+ await ExecuteAndVerifyResult("SELECT CAST('2020-01-01' AS DATETIMEOFFSET(0))", "2020-01-01 00:00:00 +00:00");
}
[Test]
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/DataStorage/ServiceBufferFileStreamReaderWriterTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/DataStorage/ServiceBufferFileStreamReaderWriterTests.cs
index 26a6a74d..cafd7f48 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/DataStorage/ServiceBufferFileStreamReaderWriterTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/DataStorage/ServiceBufferFileStreamReaderWriterTests.cs
@@ -432,14 +432,39 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution.DataStorage
{
DateTimeOffset.Now, DateTimeOffset.UtcNow, DateTimeOffset.MinValue, DateTimeOffset.MaxValue
};
+
+ // Setup: Create a DATETIMEOFFSET column
+ DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn { DataTypeName = "datetimeoffset", NumericScale = 6 });
+
foreach (DateTimeOffset value in testValues)
{
string displayValue = VerifyReadWrite(sizeof(long)*2 + 1, value, (writer, val) => writer.WriteDateTimeOffset(val),
- (reader, rowId) => reader.ReadDateTimeOffset(0, rowId));
+ (reader, rowId) => reader.ReadDateTimeOffset(0, rowId, col));
- // Make sure the display value has a time string with 7 milliseconds and a time zone
- Assert.True(Regex.IsMatch(displayValue, @"^[\d]{4}-[\d]{2}-[\d]{2} [\d]{2}:[\d]{2}:[\d]{2}\.[\d]{7} [+-][01][\d]:[\d]{2}$"));
+ // Make sure the display value has a time string with 6 milliseconds and a time zone
+ Assert.True(Regex.IsMatch(displayValue, @"^[\d]{4}-[\d]{2}-[\d]{2} [\d]{2}:[\d]{2}:[\d]{2}\.[\d]{6} [+-][01][\d]:[\d]{2}$"));
+ }
+ }
+ [Test]
+ public void DateTimeOffsetZeroScaleTest()
+ {
+ // Setup: Create some test values
+ DateTimeOffset[] testValues =
+ {
+ DateTimeOffset.Now, DateTimeOffset.UtcNow, DateTimeOffset.MinValue, DateTimeOffset.MaxValue
+ };
+
+ // Setup: Create a DATETIMEOFFSET column
+ DbColumnWrapper col = new DbColumnWrapper(new TestDbColumn { DataTypeName = "datetimeoffset", NumericScale = 0 });
+
+ foreach (DateTimeOffset value in testValues)
+ {
+ string displayValue = VerifyReadWrite(sizeof(long) * 2 + 1, value, (writer, val) => writer.WriteDateTimeOffset(val),
+ (reader, rowId) => reader.ReadDateTimeOffset(0, rowId, col));
+
+ // Make sure the display value has a time string with no millisecond and a time zone
+ Assert.True(Regex.IsMatch(displayValue, @"^[\d]{4}-[\d]{2}-[\d]{2} [\d]{2}:[\d]{2}:[\d]{2} [+-][01][\d]:[\d]{2}$"));
}
}