Skip to content
5 changes: 5 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,9 @@
</ItemGroup>

<Import Condition="Exists('User.Directory.Build.props')" Project="User.Directory.Build.props" />

<PropertyGroup>
<!-- setting env var or property 'DO_DATEONLY=false' disables DO_DATEONLY feature -->
<DefineConstants Condition="$(TargetFramework)=='net6.0' AND '$(DO_DATEONLY)'!='false'">$(DefineConstants);DO_DATEONLY</DefineConstants>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ public override void Visit(SqlFunctionCall node)
case SqlFunctionType.DateTimeAddYears:
Visit(DateAddYear(node.Arguments[0], node.Arguments[1]));
return;
case SqlFunctionType.DateOnlyAddDays:
Visit(DateAddDay(node.Arguments[0], node.Arguments[1]));
return;
case SqlFunctionType.DateTimeConstruct:
Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateTime(2001, 1, 1)), SqlType.DateTime),
node.Arguments[0] - 2001),
Expand Down
3 changes: 3 additions & 0 deletions Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Translator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ internal class Translator : SqlTranslator
/// <inheritdoc/>
public override string DateTimeFormatString => @"'(TIMESTAMP '\'yyyy\-MM\-dd HH\:mm\:ss\.fff\'\)";

/// <inheritdoc/>
public override string DateOnlyFormatString => @"'(DATE '\'yyyy\-MM\-dd\'\)";

/// <inheritdoc/>
public override string TimeSpanFormatString => "(INTERVAL '{0}{1} {2}:{3}:{4}.{5:000}' DAY(6) TO SECOND(3))";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ public override DataTypeCollection GetDataTypesInfo()
dtc.Interval = DataTypeInfo.Range(SqlType.Interval, commonFeatures,
ValueRange.TimeSpan, "interval");

#if DO_DATEONLY
dtc.DateOnly = DataTypeInfo.Range(SqlType.Date, commonFeatures, ValueRange.DateOnly, "date");
dtc.TimeOnly = DataTypeInfo.Range(SqlType.Time, commonFeatures, ValueRange.TimeOnly, "time");
#endif

dtc.Char = DataTypeInfo.Stream(SqlType.Char, commonFeatures, MaxCharLength, "character", "char", "bpchar");
dtc.VarChar = DataTypeInfo.Stream(SqlType.VarChar, commonFeatures, MaxCharLength, "character varying", "varchar");
dtc.VarCharMax = DataTypeInfo.Regular(SqlType.VarCharMax, commonFeatures, "text");
Expand Down
42 changes: 26 additions & 16 deletions Orm/Xtensive.Orm.SqlServer/Sql.Drivers.SqlServer/v09/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,20 +150,21 @@ public override void Visit(SqlAlterTable node)
/// <inheritdoc/>
public override void Visit(SqlFunctionCall node)
{
var arguments = node.Arguments;
switch (node.FunctionType) {
case SqlFunctionType.CharLength:
(SqlDml.FunctionCall("DATALENGTH", node.Arguments) / 2).AcceptVisitor(this);
(SqlDml.FunctionCall("DATALENGTH", arguments) / 2).AcceptVisitor(this);
return;
case SqlFunctionType.PadLeft:
case SqlFunctionType.PadRight:
GenericPad(node).AcceptVisitor(this);
return;
case SqlFunctionType.Round:
// Round should always be called with 2 arguments
if (node.Arguments.Count == 1) {
if (arguments.Count == 1) {
Visit(SqlDml.FunctionCall(
translator.TranslateToString(SqlFunctionType.Round),
node.Arguments[0],
arguments[0],
SqlDml.Literal(0)));
return;
}
Expand All @@ -173,42 +174,51 @@ public override void Visit(SqlFunctionCall node)
// It's stupid, isn't it?
Visit(SqlDml.FunctionCall(
translator.TranslateToString(SqlFunctionType.Round),
node.Arguments[0],
arguments[0],
SqlDml.Literal(0),
SqlDml.Literal(1)));
return;
case SqlFunctionType.Substring:
if (node.Arguments.Count == 2) {
SqlExpression len = SqlDml.CharLength(node.Arguments[0]);
node = SqlDml.Substring(node.Arguments[0], node.Arguments[1], len);
if (arguments.Count == 2) {
SqlExpression len = SqlDml.CharLength(arguments[0]);
node = SqlDml.Substring(arguments[0], arguments[1], len);
Visit(node);
return;
}
break;
case SqlFunctionType.IntervalToMilliseconds:
Visit(CastToLong(node.Arguments[0]) / NanosecondsPerMillisecond);
Visit(CastToLong(arguments[0]) / NanosecondsPerMillisecond);
return;
case SqlFunctionType.IntervalConstruct:
case SqlFunctionType.IntervalToNanoseconds:
Visit(CastToLong(node.Arguments[0]));
Visit(CastToLong(arguments[0]));
return;
case SqlFunctionType.DateTimeAddMonths:
Visit(DateAddMonth(node.Arguments[0], node.Arguments[1]));
Visit(DateAddMonth(arguments[0], arguments[1]));
return;
case SqlFunctionType.DateTimeAddYears:
Visit(DateAddYear(node.Arguments[0], node.Arguments[1]));
Visit(DateAddYear(arguments[0], arguments[1]));
return;
case SqlFunctionType.DateOnlyAddDays:
Visit(DateAddDay(arguments[0], arguments[1]));
return;
case SqlFunctionType.TimeOnlyAddHours:
Visit(DateAddHour(arguments[0], arguments[1]));
return;
case SqlFunctionType.TimeOnlyAddMinutes:
Visit(DateAddMinute(arguments[0], arguments[1]));
return;
case SqlFunctionType.DateTimeTruncate:
DateTimeTruncate(node.Arguments[0]).AcceptVisitor(this);
DateTimeTruncate(arguments[0]).AcceptVisitor(this);
return;
case SqlFunctionType.DateTimeConstruct:
Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Literal(new DateTime(2001, 1, 1)),
node.Arguments[0] - 2001),
node.Arguments[1] - 1),
node.Arguments[2] - 1));
arguments[0] - 2001),
arguments[1] - 1),
arguments[2] - 1));
return;
case SqlFunctionType.DateTimeToStringIso:
Visit(DateTimeToStringIso(node.Arguments[0]));
Visit(DateTimeToStringIso(arguments[0]));
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,14 @@ public override DataTypeCollection GetDataTypesInfo()
ValueRange.Double, "float");

types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index,
new ValueRange<DateTime>(new DateTime(1753, 1, 1), new DateTime(9999, 12,31)),
new ValueRange<DateTime>(new DateTime(1753, 1, 1), new DateTime(9999, 12, 31)),
"datetime", "smalldatetime");

#if DO_DATEONLY
types.DateOnly = DataTypeInfo.Range(SqlType.Date, common | index, new ValueRange<DateOnly>(new DateOnly(1, 1, 1), new DateOnly(9999, 12, 31)), "date");
types.TimeOnly = DataTypeInfo.Range(SqlType.Time, common | index, new ValueRange<TimeOnly>(TimeOnly.MinValue, TimeOnly.MaxValue), "time");
#endif

types.Char = DataTypeInfo.Stream(SqlType.Char, common | index, 4000, "nchar", "char");
types.VarChar = DataTypeInfo.Stream(SqlType.VarChar, common | index, 4000, "nvarchar", "varchar");
types.VarCharMax = DataTypeInfo.Regular(SqlType.VarCharMax, common, "nvarchar(max)", "ntext", "varchar(max)", "text", "xml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ namespace Xtensive.Sql.Drivers.SqlServer.v09
internal class Translator : SqlTranslator
{
public override string DateTimeFormatString => @"'cast ('\'yyyy\-MM\-ddTHH\:mm\:ss\.fff\'' as datetime)'";
public override string DateOnlyFormatString => @"'cast ('\'yyyy\-MM\-dd\'' as date)'";
public override string TimeOnlyFormatString => @"'cast ('\'HH\:mm\:ss\.fff\'' as time)'";
public override string TimeSpanFormatString => string.Empty;

public override void Initialize()
Expand Down Expand Up @@ -567,6 +569,13 @@ public override void Translate(SqlCompilerContext context, object literalValue)
case long v:
_ = output.Append($"CAST({v} as BIGINT)");
break;
#if DO_DATEONLY
case DateOnly dateOnly:
var dateOnlyRange = (ValueRange<DateTime>) Driver.ServerInfo.DataTypes.DateTime.ValueRange;
var newDateOnly = ValueRangeValidator.Correct(dateOnly.ToDateTime(TimeOnly.MinValue), dateOnlyRange).Date;
output.Append(newDateOnly.ToString(DateOnlyFormatString, DateTimeFormat));
break;
#endif
default:
base.Translate(context, literalValue);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,22 @@ public override DataTypeCollection GetDataTypesInfo()
var index = DataTypeFeatures.Indexing | DataTypeFeatures.Clustering |
DataTypeFeatures.FillFactor | DataTypeFeatures.KeyConstraint;

types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index,
new ValueRange<DateTime>(new DateTime(1, 1, 1), new DateTime(9999, 12, 31)),
"datetime2", "datetime", "date", "time", "smalldatetime");

types.DateTimeOffset = DataTypeInfo.Range(SqlType.DateTimeOffset, common | index,
new ValueRange<DateTimeOffset>(new DateTimeOffset(1, 1, 1, 0, 0, 0, 0, new TimeSpan(0)),
new DateTimeOffset(9999, 12, 31, 0, 0, 0, 0, new TimeSpan(0))),
"datetimeoffset");

#if DO_DATEONLY
types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index,
new ValueRange<DateTime>(new DateTime(1, 1, 1), new DateTime(9999, 12, 31)),
"datetime2", "datetime", "smalldatetime");
#else
types.DateTime = DataTypeInfo.Range(SqlType.DateTime, common | index,
new ValueRange<DateTime>(new DateTime(1, 1, 1), new DateTime(9999, 12, 31)),
"datetime2", "datetime", "smalldatetime", "date", "time");
#endif

types.VarBinaryMax = DataTypeInfo.Regular(SqlType.VarBinaryMax, common, "varbinary(max)", "image");

var geo = DataTypeFeatures.Default | DataTypeFeatures.Nullable | DataTypeFeatures.Multiple | DataTypeFeatures.Spatial;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ internal class Translator : v09.Translator
/// <inheritdoc/>
public override string DateTimeFormatString => @"'cast ('\'yyyy\-MM\-ddTHH\:mm\:ss\.fffffff\'' as datetime2)'";

/// <inheritdoc/>
public override string TimeOnlyFormatString => @"'cast ('\'HH\:mm\:ss\.fffffff\'' as time)'";

public string DateTimeOffsetFormatString => @"'cast ('\'yyyy\-MM\-dd HH\:mm\:ss\.fffffff\ zzz\'' as datetimeoffset)'";

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright (C) 2016-2021 Xtensive LLC.
// Copyright (C) 2016-2022 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Alex Groznov
// Created: 2016.08.01

using System;
using NUnit.Framework;
using Xtensive.Orm.Tests.Linq.DateTimeAndDateTimeOffset.Model;

Expand All @@ -23,6 +24,10 @@ public void EqualsTest()
RunWrongTest<SingleDateTimeEntity>(c => c.MillisecondDateTime==WrongMillisecondDateTime);
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateTime==WrongDateTime);
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateTime==null);
#if DO_DATEONLY
RunTest<SingleDateTimeEntity>(c => c.DateOnly == FirstDateOnly);
RunTest<SingleDateTimeEntity>(c => c.NullableDateOnly == NullableDateOnly);
#endif
});
}

Expand All @@ -33,6 +38,9 @@ public void NotEqualTest()
RunTest<SingleDateTimeEntity>(c=>c.DateTime!=FirstDateTime.AddYears(1));
RunTest<SingleDateTimeEntity>(c => c.MillisecondDateTime!=FirstMillisecondDateTime.AddYears(1));
RunTest<SingleDateTimeEntity>(c=>c.NullableDateTime!=NullableDateTime.AddYears(1));
#if DO_DATEONLY
RunTest<SingleDateTimeEntity>(c => c.DateOnly != FirstDateOnly.AddYears(1));
#endif
});
}

Expand All @@ -52,6 +60,9 @@ public void CompareTest()
RunWrongTest<SingleDateTimeEntity>(c => c.DateTime > FirstDateTime);
RunWrongTest<SingleDateTimeEntity>(c => c.MillisecondDateTime > FirstMillisecondDateTime);
RunWrongTest<SingleDateTimeEntity>(c => c.MillisecondDateTime < FirstMillisecondDateTime.Date);
#if DO_DATEONLY
RunTest<SingleDateTimeEntity>(c => c.DateOnly > FirstDateOnly.AddDays(-1));
#endif
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2016 Xtensive LLC.
// Copyright (C) 2016 Xtensive LLC.
// All rights reserved.
// For conditions of distribution and use, see license.
// Created by: Alex Groznov
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2016 Xtensive LLC.
// Copyright (C) 2016 Xtensive LLC.
// All rights reserved.
// For conditions of distribution and use, see license.
// Created by: Alex Groznov
Expand All @@ -18,6 +18,10 @@ public class DistinctTest : DateTimeBaseTest
public void DistinctByDateTimeTest()
{
ExecuteInsideSession(() => DistinctPrivate<DateTimeEntity, DateTime>(c => c.DateTime));
#if DO_DATEONLY
ExecuteInsideSession(() => DistinctPrivate<DateTimeEntity, DateOnly>(c => c.DateOnly));
ExecuteInsideSession(() => DistinctPrivate<DateTimeEntity, TimeOnly>(c => c.TimeOnly));
#endif
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ public void DateTimeJoinTest()
(left, right) => new JoinResult<DateTime> { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.DateTime, RightDateTime = right.DateTime },
c => c.LeftId,
c => c.RightId));
#if DO_DATEONLY
ExecuteInsideSession(() => JoinPrivate<DateTimeEntity, DateTimeEntity, JoinResult<DateOnly>, DateOnly, long>(
left => left.DateOnly,
right => right.DateOnly,
(left, right) => new JoinResult<DateOnly> { LeftId = left.Id, RightId = right.Id, LeftDateTime = left.DateOnly, RightDateTime = right.DateOnly },
c => c.LeftId,
c => c.RightId));
#endif
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public void AddYearsTest()
RunWrongTest<SingleDateTimeEntity>(c => c.DateTime.AddYears(1) == FirstDateTime.AddYears(2));
RunWrongTest<SingleDateTimeEntity>(c => c.MillisecondDateTime.AddYears(-1) == FirstMillisecondDateTime.AddYears(-2));
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateTime.Value.AddYears(33) == NullableDateTime.AddYears(44));
#if DO_DATEONLY
RunTest<SingleDateTimeEntity>(c => c.DateOnly.AddYears(1) == FirstDateOnly.AddYears(1));
RunTest<SingleDateTimeEntity>(c => c.NullableDateOnly.Value.AddYears(33) == NullableDateOnly.AddYears(33));

RunWrongTest<SingleDateTimeEntity>(c => c.DateOnly.AddYears(1) == FirstDateOnly.AddYears(2));
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateOnly.Value.AddYears(33) == NullableDateOnly.AddYears(44));
#endif
});
}

Expand All @@ -36,6 +43,13 @@ public void AddMonthsTest()
RunWrongTest<SingleDateTimeEntity>(c => c.DateTime.AddMonths(1) == FirstDateTime.AddMonths(2));
RunWrongTest<SingleDateTimeEntity>(c => c.MillisecondDateTime.AddMonths(-1) == FirstMillisecondDateTime.AddMonths(-2));
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateTime.Value.AddMonths(33) == NullableDateTime.AddMonths(44));
#if DO_DATEONLY
RunTest<SingleDateTimeEntity>(c => c.DateOnly.AddMonths(1) == FirstDateOnly.AddMonths(1));
RunTest<SingleDateTimeEntity>(c => c.NullableDateOnly.Value.AddMonths(33) == NullableDateOnly.AddMonths(33));

RunWrongTest<SingleDateTimeEntity>(c => c.DateOnly.AddMonths(1) == FirstDateOnly.AddMonths(2));
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateOnly.Value.AddMonths(33) == NullableDateOnly.AddMonths(44));
#endif
});
}

Expand All @@ -50,6 +64,13 @@ public void AddDaysTest()
RunWrongTest<SingleDateTimeEntity>(c => c.DateTime.AddDays(1) == FirstDateTime.AddDays(2));
RunWrongTest<SingleDateTimeEntity>(c => c.MillisecondDateTime.AddDays(-1) == FirstMillisecondDateTime.AddDays(-2));
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateTime.Value.AddDays(33) == NullableDateTime.AddDays(44));
#if DO_DATEONLY
RunTest<SingleDateTimeEntity>(c => c.DateOnly.AddDays(1) == FirstDateOnly.AddDays(1));
RunTest<SingleDateTimeEntity>(c => c.NullableDateOnly.Value.AddDays(33) == NullableDateOnly.AddDays(33));

RunWrongTest<SingleDateTimeEntity>(c => c.DateOnly.AddDays(1) == FirstDateOnly.AddDays(2));
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateOnly.Value.AddDays(33) == NullableDateOnly.AddDays(44));
#endif
});
}

Expand Down Expand Up @@ -78,6 +99,11 @@ public void AddMinutesTest()
RunWrongTest<SingleDateTimeEntity>(c => c.DateTime.AddMinutes(1) == FirstDateTime.AddMinutes(2));
RunWrongTest<SingleDateTimeEntity>(c => c.MillisecondDateTime.AddMinutes(-1) == FirstMillisecondDateTime.AddMinutes(-2));
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateTime.Value.AddMinutes(33) == NullableDateTime.AddMinutes(44));
#if DO_DATEONLY
RunTest<SingleDateTimeEntity>(c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(1));

RunWrongTest<SingleDateTimeEntity>(c => c.TimeOnly.AddMinutes(1) == FirstTimeOnly.AddMinutes(2));
#endif
});
}

Expand Down Expand Up @@ -188,6 +214,11 @@ public void MinusDateTimeTest()
RunWrongTest<SingleDateTimeEntity>(c => c.DateTime - SecondDateTime == FirstDateTime - WrongDateTime);
RunWrongTest<SingleDateTimeEntity>(c => c.MillisecondDateTime - SecondDateTime == FirstMillisecondDateTime - WrongDateTime);
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateTime - SecondDateTime == NullableDateTime - WrongDateTime);

#if DO_DATEONLY
RunTest<SingleDateTimeEntity>(c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - SecondTimeOnly);
RunWrongTest<SingleDateTimeEntity>(c => c.TimeOnly - SecondTimeOnly == FirstTimeOnly - WrongTimeOnly);
#endif
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2016 Xtensive LLC.
// Copyright (C) 2016 Xtensive LLC.
// All rights reserved.
// For conditions of distribution and use, see license.
// Created by: Alex Groznov
Expand All @@ -20,6 +20,11 @@ public void DateTimeOrderByTest()
ExecuteInsideSession(() => {
OrderByPrivate<DateTimeEntity, DateTime, long>(c => c.DateTime, c => c.Id);
OrderByPrivate<DateTimeEntity, DateTime, DateTime>(c => c.DateTime, c => c);
#if DO_DATEONLY
OrderByPrivate<DateTimeEntity, DateOnly, long>(c => c.DateOnly, c => c.Id);
OrderByPrivate<DateTimeEntity, DateOnly, DateOnly>(c => c.DateOnly, c => c);
OrderByPrivate<DateTimeEntity, TimeOnly, long>(c => c.TimeOnly, c => c.Id );
#endif
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ public void ExtractMonthTest()
RunWrongTest<SingleDateTimeEntity>(c => c.DateTime.Month == WrongDateTime.Month);
RunWrongTest<SingleDateTimeEntity>(c => c.MillisecondDateTime.Month == WrongMillisecondDateTime.Month);
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateTime.Value.Month == WrongDateTime.Month);
#if DO_DATEONLY
RunTest<SingleDateTimeEntity>(c => c.DateOnly.Month == FirstDateOnly.Month);
RunTest<SingleDateTimeEntity>(c => c.NullableDateOnly.Value.Month == NullableDateOnly.Month);

RunWrongTest<SingleDateTimeEntity>(c => c.DateOnly.Month == WrongDateOnly.Month);
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateOnly.Value.Month == WrongDateOnly.Month);
#endif
});
}

Expand Down Expand Up @@ -93,6 +100,11 @@ public void ExtractSecondTest()
RunWrongTest<SingleDateTimeEntity>(c => c.DateTime.Second == WrongDateTime.Second);
RunWrongTest<SingleDateTimeEntity>(c => c.MillisecondDateTime.Second == WrongMillisecondDateTime.Second);
RunWrongTest<SingleDateTimeEntity>(c => c.NullableDateTime.Value.Second == WrongDateTime.Second);
#if DO_DATEONLY
RunTest<SingleDateTimeEntity>(c => c.TimeOnly.Second == FirstTimeOnly.Second);

RunWrongTest<SingleDateTimeEntity>(c => c.TimeOnly.Second == WrongTimeOnly.Second);
#endif
});
}

Expand Down
Loading