Skip to content

Commit 519a8c1

Browse files
committed
Conversions of date/time/datetime/datetimeoffset values
1 parent 753d18a commit 519a8c1

9 files changed

Lines changed: 375 additions & 42 deletions

File tree

Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/v2_5/Compiler.cs

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -205,47 +205,49 @@ public override void Visit(SqlBinary node)
205205
/// <inheritdoc/>
206206
public override void Visit(SqlFunctionCall node)
207207
{
208+
var arguments = node.Arguments;
209+
208210
switch (node.FunctionType) {
209211
case SqlFunctionType.Concat:
210-
Visit(SqlDml.Concat(node.Arguments.ToArray(node.Arguments.Count)));
212+
Visit(SqlDml.Concat(arguments.ToArray(node.Arguments.Count)));
211213
return;
212214
case SqlFunctionType.DateTimeTruncate:
213-
Visit(SqlDml.Cast(node.Arguments[0], new SqlValueType("Date")));
215+
Visit(SqlDml.Cast(arguments[0], new SqlValueType("Date")));
214216
return;
215217
case SqlFunctionType.IntervalToMilliseconds:
216-
Visit(CastToLong(node.Arguments[0]) / NanosecondsPerMillisecond);
218+
Visit(CastToLong(arguments[0]) / NanosecondsPerMillisecond);
217219
return;
218220
case SqlFunctionType.IntervalConstruct:
219221
case SqlFunctionType.IntervalToNanoseconds:
220-
Visit(CastToLong(node.Arguments[0]));
222+
Visit(CastToLong(arguments[0]));
221223
return;
222224
case SqlFunctionType.DateTimeAddMonths:
223-
Visit(DateAddMonth(node.Arguments[0], node.Arguments[1]));
225+
Visit(DateAddMonth(arguments[0], arguments[1]));
224226
return;
225227
case SqlFunctionType.DateTimeAddYears:
226-
Visit(DateAddYear(node.Arguments[0], node.Arguments[1]));
228+
Visit(DateAddYear(arguments[0], arguments[1]));
227229
return;
228230
case SqlFunctionType.DateTimeConstruct:
229231
Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateTime(2001, 1, 1)), SqlType.DateTime),
230-
node.Arguments[0] - 2001),
231-
node.Arguments[1] - 1),
232-
node.Arguments[2] - 1));
232+
arguments[0] - 2001),
233+
arguments[1] - 1),
234+
arguments[2] - 1));
233235
return;
234236
#if NET6_0_OR_GREATER //DO_DATEONLY
235237
case SqlFunctionType.DateAddYears:
236-
Visit(DateAddYear(node.Arguments[0], node.Arguments[1]));
238+
Visit(DateAddYear(arguments[0], arguments[1]));
237239
return;
238240
case SqlFunctionType.DateAddMonths:
239-
Visit(DateAddMonth(node.Arguments[0], node.Arguments[1]));
241+
Visit(DateAddMonth(arguments[0], arguments[1]));
240242
return;
241243
case SqlFunctionType.DateAddDays:
242-
Visit(DateAddDay(node.Arguments[0], node.Arguments[1]));
244+
Visit(DateAddDay(arguments[0], arguments[1]));
243245
return;
244246
case SqlFunctionType.DateConstruct:
245247
Visit(DateAddDay(DateAddMonth(DateAddYear(SqlDml.Cast(SqlDml.Literal(new DateOnly(2001, 1, 1)), SqlType.Date),
246-
node.Arguments[0] - 2001),
247-
node.Arguments[1] - 1),
248-
node.Arguments[2] - 1));
248+
arguments[0] - 2001),
249+
arguments[1] - 1),
250+
arguments[2] - 1));
249251
return;
250252
case SqlFunctionType.TimeAddHours:
251253
Visit(DateAddHour(node.Arguments[0], node.Arguments[1]));
@@ -255,20 +257,32 @@ public override void Visit(SqlFunctionCall node)
255257
return;
256258
case SqlFunctionType.TimeConstruct:
257259
Visit(DateAddMillisecond(DateAddSecond(DateAddMinute(DateAddHour(SqlDml.Cast(SqlDml.Literal(new TimeOnly(0, 0, 0)), SqlType.Time),
258-
node.Arguments[0]),
259-
node.Arguments[1]),
260-
node.Arguments[2]),
261-
node.Arguments[3]));
260+
arguments[0]),
261+
arguments[1]),
262+
arguments[2]),
263+
arguments[3]));
262264
return;
263265
case SqlFunctionType.DateToString:
264-
Visit(DateToString(node.Arguments[0]));
266+
Visit(DateToString(arguments[0]));
265267
return;
266268
case SqlFunctionType.TimeToString:
267-
Visit(TimeToString(node.Arguments[0]));
269+
Visit(TimeToString(arguments[0]));
270+
return;
271+
case SqlFunctionType.DateToDateTime:
272+
DateToDateTime(arguments[0]).AcceptVisitor(this);
273+
return;
274+
case SqlFunctionType.DateTimeToDate:
275+
DateTimeToDate(arguments[0]).AcceptVisitor(this);
276+
return;
277+
case SqlFunctionType.DateTimeToTime:
278+
DateTimeToTime(arguments[0]).AcceptVisitor(this);
279+
return;
280+
case SqlFunctionType.TimeToDateTime:
281+
TimeToDateTime(arguments[0]).AcceptVisitor(this);
268282
return;
269283
#endif
270284
case SqlFunctionType.DateTimeToStringIso:
271-
Visit(DateTimeToStringIso(node.Arguments[0]));
285+
Visit(DateTimeToStringIso(arguments[0]));
272286
return;
273287
}
274288

@@ -356,6 +370,18 @@ protected static SqlUserFunctionCall BitNot(SqlExpression operand) =>
356370
SqlDml.FunctionCall("BIN_NOT", operand);
357371

358372
#if NET6_0_OR_GREATER //DO_DATEONLY
373+
protected static SqlExpression TimeToDateTime(SqlExpression time) =>
374+
SqlDml.Cast(time, SqlType.DateTime);
375+
376+
protected static SqlExpression DateTimeToTime(SqlExpression dateTime) =>
377+
SqlDml.Cast(dateTime, SqlType.Time);
378+
379+
protected static SqlExpression DateToDateTime(SqlExpression date) =>
380+
SqlDml.Cast(date, SqlType.DateTime);
381+
382+
protected static SqlExpression DateTimeToDate(SqlExpression dateTime) =>
383+
SqlDml.Cast(dateTime, SqlType.Date);
384+
359385
protected static SqlFunctionCall DateToString(SqlExpression date)
360386
{
361387
return SqlDml.Substring(date, 0, 10);;

Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_0/Compiler.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,18 @@ public override void Visit(SqlFunctionCall node)
232232
case SqlFunctionType.TimeToString:
233233
Visit(TimeToString(arguments[0]));
234234
return;
235+
case SqlFunctionType.DateTimeToDate:
236+
DateTimeToDate(node.Arguments[0]).AcceptVisitor(this);
237+
return;
238+
case SqlFunctionType.DateToDateTime:
239+
DateToDateTime(node.Arguments[0]).AcceptVisitor(this);
240+
return;
241+
case SqlFunctionType.DateTimeToTime:
242+
DateTimeToTime(node.Arguments[0]).AcceptVisitor(this);
243+
return;
244+
case SqlFunctionType.TimeToDateTime:
245+
TimeToDateTime(node.Arguments[0]).AcceptVisitor(this);
246+
return;
235247
#endif
236248
case SqlFunctionType.DateTimeToStringIso:
237249
Visit(DateTimeToStringIso(arguments[0]));
@@ -244,7 +256,9 @@ public override void Visit(SqlFunctionCall node)
244256
#if NET6_0_OR_GREATER //DO_DATEONLY
245257
public override void Visit(SqlPlaceholder node)
246258
{
247-
if (node.Id is Xtensive.Orm.Providers.QueryParameterBinding qpb && qpb.TypeMapping.Type == typeof(TimeOnly)) {
259+
if (node.Id is Orm.Providers.QueryParameterBinding qpb
260+
&& qpb.BindingType == Orm.Providers.QueryParameterBindingType.Regular
261+
&& qpb.TypeMapping.Type == typeof(TimeOnly)) {
248262
_ = context.Output.Append("TIME(");
249263
base.Visit(node);
250264
_ = context.Output.Append(")");
@@ -310,7 +324,6 @@ protected virtual SqlExpression TimeSubtractTime(SqlExpression time1, SqlExpress
310324
NanosecondsPerDay);
311325

312326
protected virtual SqlExpression TimeAddInterval(SqlExpression time, SqlExpression interval) =>
313-
314327
SqlDml.FunctionCall("TIME",
315328
SqlDml.FunctionCall(
316329
"DATE_ADD",
@@ -358,6 +371,7 @@ protected static SqlUserFunctionCall DateAddYear(SqlExpression date, SqlExpressi
358371

359372
protected static SqlUserFunctionCall DateAddMonth(SqlExpression date, SqlExpression months) =>
360373
SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(months, SqlDml.Native("MONTH"))));
374+
361375
protected static SqlUserFunctionCall DateAddDay(SqlExpression date, SqlExpression days) =>
362376
SqlDml.FunctionCall("DATE_ADD", date, SqlDml.RawConcat(SqlDml.Native("INTERVAL "), SqlDml.RawConcat(days, SqlDml.Native("DAY"))));
363377

@@ -378,6 +392,27 @@ protected static SqlUserFunctionCall DateToString(SqlExpression dateTime) =>
378392

379393
protected static SqlUserFunctionCall TimeToString(SqlExpression dateTime) =>
380394
SqlDml.FunctionCall("DATE_FORMAT", dateTime, "%H:%i:%s.%f0");
395+
396+
private static SqlExpression DateTimeToDate(SqlExpression dateTime) =>
397+
SqlDml.Cast(dateTime, SqlType.Date);
398+
399+
private static SqlExpression DateToDateTime(SqlExpression date) =>
400+
SqlDml.Cast(date, SqlType.DateTime);
401+
402+
private static SqlExpression DateTimeToTime(SqlExpression dateTime) =>
403+
SqlDml.Cast(dateTime, SqlType.Time);
404+
405+
// can't convert via cast, because mysql shots to its head and creates
406+
// value that it can't read later. This mimics conversion that occurs
407+
// in newer versions (5.6+) and use current date as a source of year,
408+
// month and day values :-)
409+
private static SqlExpression TimeToDateTime(SqlExpression time) =>
410+
SqlDml.FunctionCall("DATE_ADD",
411+
SqlDml.Literal(DateTime.Now.Date),
412+
SqlDml.RawConcat(
413+
SqlDml.RawConcat(SqlDml.Native("INTERVAL "),
414+
SqlDml.FunctionCall("TIME_TO_SEC", time)),
415+
SqlDml.Native("SECOND")));
381416
#endif
382417

383418
protected static SqlUserFunctionCall DateTimeToStringIso(SqlExpression dateTime) =>

Orm/Xtensive.Orm.MySql/Sql.Drivers.MySql/v5_6/Compiler.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public override void Visit(SqlFunctionCall node)
4444
Visit(MakeTime(arguments[0], arguments[1], arguments[2], arguments[3]));
4545
return;
4646
}
47+
case SqlFunctionType.TimeToDateTime:
48+
Visit(SqlDml.Cast(arguments[0], SqlType.DateTime));
49+
return;
4750
default:
4851
base.Visit(node);
4952
return;

Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/Compiler.cs

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ internal class Compiler : SqlCompiler
3737
protected const string SecondIntervalPart = "SECOND";
3838

3939
protected const string ToCharFunctionName = "TO_CHAR";
40-
private const string NumToDSIntervalFunctionName = "NUMTODSINTERVAL";
40+
protected const string NumToDSIntervalFunctionName = "NUMTODSINTERVAL";
41+
protected const string ToDSIntervalFunctionName = "TO_DSINTERVAL";
42+
protected const string TimeFormat = "HH24:MI:SS.FF6";
43+
4144
private static readonly SqlExpression SundayNumber = SqlDml.Native(
4245
"TO_NUMBER(TO_CHAR(TIMESTAMP '2009-07-26 00:00:00.000', 'D'))");
4346
private static readonly SqlNative RefTimestamp = SqlDml.Native("timestamp '2009-01-01 00:00:00.000000'");
@@ -129,9 +132,38 @@ public override void Visit(SqlFunctionCall node)
129132
case SqlFunctionType.DateTimeToDateTimeOffset:
130133
DateTimeToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this);
131134
return;
135+
case SqlFunctionType.DateTimeOffsetToDateTime:
136+
DateTimeOffsetToDateTime(node.Arguments[0]).AcceptVisitor(this);
137+
return;
132138
case SqlFunctionType.DateTimeOffsetToUtcTime:
133139
DateTimeOffsetToUtcTime(node.Arguments[0]).AcceptVisitor(this);
134140
return;
141+
#if NET6_0_OR_GREATER
142+
case SqlFunctionType.DateTimeToDate:
143+
DateTimeToDate(node.Arguments[0]).AcceptVisitor(this);
144+
return;
145+
case SqlFunctionType.DateToDateTime:
146+
DateToDateTime(node.Arguments[0]).AcceptVisitor(this);
147+
return;
148+
case SqlFunctionType.DateTimeToTime:
149+
DateTimeToTime(node.Arguments[0]).AcceptVisitor(this);
150+
return;
151+
case SqlFunctionType.TimeToDateTime:
152+
TimeToDateTime(node.Arguments[0]).AcceptVisitor(this);
153+
return;
154+
case SqlFunctionType.DateTimeOffsetToDate:
155+
DateTimeOffsetToDate(node.Arguments[0]).AcceptVisitor(this);
156+
return;
157+
case SqlFunctionType.DateTimeOffsetToTime:
158+
DateTimeOffsetToTime(node.Arguments[0]).AcceptVisitor(this);
159+
return;
160+
case SqlFunctionType.DateToDateTimeOffset:
161+
DateToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this);
162+
return;
163+
case SqlFunctionType.TimeToDateTimeOffset:
164+
TimeToDateTimeOffset(node.Arguments[0]).AcceptVisitor(this);
165+
return;
166+
#endif
135167
default:
136168
base.Visit(node);
137169
return;
@@ -358,9 +390,9 @@ private static SqlExpression TimeAddInterval(SqlExpression time, SqlExpression i
358390
{
359391
var baseOp = (negateInterval) ? RefTimestamp + time - intervalToAdd
360392
: RefTimestamp + time + intervalToAdd;
361-
var getTimeOnly = SqlDml.FunctionCall(ToCharFunctionName, baseOp, AnsiString("HH24:MI:SS.FF6"));
393+
var getTimeOnly = SqlDml.FunctionCall(ToCharFunctionName, baseOp, AnsiString(TimeFormat));
362394
var pretendZeroDays = (SqlExpression) SqlDml.Concat(AnsiString("0 "), getTimeOnly);
363-
var dsInterval = SqlDml.FunctionCall("TO_DSINTERVAL", pretendZeroDays);
395+
var dsInterval = SqlDml.FunctionCall(ToDSIntervalFunctionName, pretendZeroDays);
364396
var castToCorrectInterval = SqlDml.Cast(dsInterval, SqlType.Time);
365397
return castToCorrectInterval;
366398
}
@@ -449,9 +481,59 @@ private static SqlExpression DateTimeOffsetToLocalTime(SqlExpression dateTimeOff
449481
private static SqlExpression DateTimeToDateTimeOffset(SqlExpression dateTime) =>
450482
SqlDml.Cast(dateTime, SqlType.DateTimeOffset);
451483

484+
private static SqlExpression DateTimeOffsetToDateTime(SqlExpression dateTimeOffset) =>
485+
SqlDml.Cast(dateTimeOffset, SqlType.DateTime);
486+
452487
private static SqlExpression DateTimeOffsetToUtcTime(SqlExpression dateTimeOffset) =>
453488
SqlDml.RawConcat(dateTimeOffset, SqlDml.Native(" at time zone 'UTC'"));
454489

490+
private static SqlExpression DateToDateTimeOffset(SqlExpression date) =>
491+
SqlDml.Cast(date, SqlType.DateTimeOffset);
492+
493+
#if NET6_0_OR_GREATER //DO_DATEONLY
494+
private static SqlExpression DateTimeToDate(SqlExpression dateTime) =>
495+
SqlDml.Cast(dateTime, SqlType.Date);
496+
497+
private static SqlExpression DateToDateTime(SqlExpression date) =>
498+
SqlDml.Cast(date, SqlType.DateTime);
499+
500+
private static SqlExpression DateTimeToTime(SqlExpression dateTime) =>
501+
SqlDml.Cast(SqlDml.FunctionCall(ToDSIntervalFunctionName,
502+
(SqlExpression) SqlDml.Concat(
503+
AnsiString("0 "),
504+
SqlDml.FunctionCall(ToCharFunctionName,
505+
dateTime,
506+
AnsiString(TimeFormat)
507+
)
508+
)
509+
), SqlType.Time);
510+
511+
private static SqlExpression TimeToDateTime(SqlExpression time) =>
512+
SqlDml.Cast(
513+
SqlDml.Literal(DateTime.MinValue) + time,
514+
SqlType.DateTime);
515+
516+
private static SqlExpression DateTimeOffsetToDate(SqlExpression dateTimeOffset) =>
517+
SqlDml.Cast(dateTimeOffset, SqlType.Date);
518+
519+
private static SqlExpression DateTimeOffsetToTime(SqlExpression dateTimeOffset) =>
520+
SqlDml.Cast(SqlDml.FunctionCall(ToDSIntervalFunctionName,
521+
(SqlExpression)SqlDml.Concat(
522+
AnsiString("0 "),
523+
SqlDml.FunctionCall(ToCharFunctionName,
524+
dateTimeOffset,
525+
AnsiString(TimeFormat)
526+
)
527+
)
528+
), SqlType.Time);
529+
//SqlDml.Cast(dateTimeOffset - SqlDml.Cast(dateTimeOffset, SqlType.Date), SqlType.Time);
530+
531+
private static SqlExpression TimeToDateTimeOffset(SqlExpression time) =>
532+
SqlDml.Cast(
533+
SqlDml.Literal(DateTime.MinValue) + time,
534+
SqlType.DateTimeOffset);
535+
#endif
536+
455537
private static SqlExpression AnsiString(string value) => SqlDml.Native("'" + value + "'");
456538

457539
// Constructors

Orm/Xtensive.Orm.Oracle/Sql.Drivers.Oracle/v09/TypeMapper.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ public override object ReadDouble(DbDataReader reader, int index)
212212
public override object ReadDateTimeOffset(DbDataReader reader, int index)
213213
{
214214
var nativeReader = (OracleDataReader) reader;
215-
return new DateTimeOffset(nativeReader.GetOracleTimeStampTZ(index).Value, nativeReader.GetOracleTimeStampTZ(index).GetTimeZoneOffset());
215+
var timeStampTZ = nativeReader.GetOracleTimeStampTZ(index);
216+
return new DateTimeOffset(timeStampTZ.Value, timeStampTZ.GetTimeZoneOffset());
216217
}
217218

218219
public override object ReadTimeSpan(DbDataReader reader, int index)

0 commit comments

Comments
 (0)