@@ -388,27 +388,46 @@ private static SqlExpression DateConstruct(SqlExpression years, SqlExpression mo
388388
389389 private static SqlExpression TimeConstruct ( IReadOnlyList < SqlExpression > arguments )
390390 {
391+ SqlExpression hour , minute , second , microsecond ;
391392 if ( arguments . Count == 4 ) {
392- var hours = arguments [ 0 ] ;
393- var minutes = arguments [ 1 ] ;
394- var seconds = arguments [ 2 ] ;
395- var milliseconds = arguments [ 3 ] ;
396-
397- return SqlDml . FunctionCall ( NumToDSIntervalFunctionName ,
398- seconds + ( minutes * 60 ) + ( hours * 3600 ) + ( milliseconds / 1000 ) ,
399- AnsiString ( SecondIntervalPart ) ) ;
393+ hour = arguments [ 0 ] ;
394+ minute = arguments [ 1 ] ;
395+ second = arguments [ 2 ] ;
396+ microsecond = arguments [ 3 ] * 10000 ;
400397 }
401398 else if ( arguments . Count == 1 ) {
402399 var ticks = arguments [ 0 ] ;
403- var zeroTime = SqlDml . Literal ( new TimeOnly ( 0 , 0 , 0 , 0 ) ) ;
404- if ( ! SqlHelper . IsTimeSpanTicks ( ticks , out var sourceInterval ) ) {
405- sourceInterval = SqlDml . FunctionCall ( NumToDSIntervalFunctionName , ticks / 10000000 , AnsiString ( SecondIntervalPart ) ) ;
400+ if ( SqlHelper . IsTimeSpanTicks ( ticks , out var sourceExpression ) ) {
401+ // try to optimize and reduce calculations when TimeSpan.Ticks where used for TimeOnly(ticks) ctor
402+ var days = SqlDml . Extract ( SqlIntervalPart . Day , sourceExpression ) ;
403+ var hours = days * 24 + SqlDml . Extract ( SqlIntervalPart . Hour , sourceExpression ) ;
404+
405+ var hourString1 = SqlDml . Cast ( hours , new SqlValueType ( SqlType . VarChar , 3 ) ) ;
406+ var sourceExpressionAsString = SqlDml . FunctionCall ( ToCharFunctionName , sourceExpression ) ;
407+ var minuteToSecondsSubstring = SqlDml . Substring ( sourceExpressionAsString , SqlDml . FunctionCall ( "INSTR" , sourceExpressionAsString , AnsiString ( ":" ) ) - 1 , 16 ) ;
408+ var composedTimeString1 = SqlDml . Concat ( AnsiString ( "0 " ) , hourString1 , minuteToSecondsSubstring ) ;
409+ return SqlDml . FunctionCall ( ToDSIntervalFunctionName , new [ ] { composedTimeString1 } ) ;
410+ }
411+ else {
412+ hour = SqlDml . Cast ( ticks / 36000000000 , new SqlValueType ( SqlType . Decimal , 10 , 0 ) ) ;
413+ minute = SqlDml . Cast ( ( ticks / 600000000 ) % 60 , new SqlValueType ( SqlType . Decimal , 10 , 0 ) ) ;
414+ second = SqlDml . Cast ( ( ticks / 10000000 ) % 60 , new SqlValueType ( SqlType . Decimal , 10 , 0 ) ) ;
415+ microsecond = SqlDml . Cast ( ticks % 10000000 , new SqlValueType ( SqlType . Decimal , 10 , 0 ) ) ;
406416 }
407- return SqlDml . Cast ( TimeAddInterval ( zeroTime , sourceInterval ) , SqlType . Time ) ;
408417 }
409418 else {
410419 throw new InvalidOperationException ( "Unsupported count of parameters" ) ;
411420 }
421+
422+ // using string version of time allows to control hours overflow
423+ // we cannot add hours, minutes and other parts to 00:00:00.000 time
424+ // because hours might step over 24 hours and start counting from 0.
425+ var hourString = SqlDml . Cast ( hour , new SqlValueType ( SqlType . VarChar , 3 ) ) ;
426+ var minuteString = SqlDml . Cast ( minute , new SqlValueType ( SqlType . VarChar , 2 ) ) ;
427+ var secondString = SqlDml . Cast ( second , new SqlValueType ( SqlType . VarChar , 2 ) ) ;
428+ var microsecondString = SqlDml . Cast ( microsecond , new SqlValueType ( SqlType . VarChar , 7 ) ) ;
429+ var composedTimeString = SqlDml . Concat ( AnsiString ( "0 " ) , hourString , SqlDml . Literal ( ":" ) , minuteString , SqlDml . Literal ( ":" ) , secondString , SqlDml . Literal ( "." ) , microsecondString ) ;
430+ return SqlDml . FunctionCall ( ToDSIntervalFunctionName , new [ ] { composedTimeString } ) ;
412431 }
413432
414433 private static SqlExpression TimeToNanoseconds ( SqlExpression time )
0 commit comments