Thanks Carl
I have added your suggestions/corrections and this time even ran it past the compiler and did a few tests and this new version seems to work fine (so far).
I got rid of the modulus (%) as basically you cannot trust it if there is a chance of negative numbers.
eg. we all know 100%3 = 1 but did you know that in Clarion, -100%3 = 2 ?
There is a compatible_modulus #pragma but you cannot rely on someone having that set on or off and besides, I suspect it just substitutes one “wrong” answer for another!
So safer to calculate it yourself - so I have done that now and so this should be safe for negative “periods”.
I share Carl’s unease about REALs in certain circumstances, but here where it is used only for integers (no fractions) it should be accurate and give the same result as Decimal(15), but REAL should be a bit quicker.
And talking of ‘quicker’ I wrapped the case variables in val() for a very minor performance boost.
anyway without further ado:
CalculateEndDateAndTime PROCEDURE (LONG pStartDate,LONG pStartTime,LONG pPeriod,STRING pTypeOfPeriod,*LONG pEndDate,*LONG pEndTime) ! Declare Procedure
! pTypeOfPeriod must be S (seconds), M (minutes), H (hours), D (days) or W (weeks).
timeIncrement REAL
daysIncrement LONG,auto
CODE
pEndDate = pStartDate
case val(upper(pTypeOfPeriod))
of val('W') ; pEndDate += pPeriod * 7
of val('D') ; pEndDate += pPeriod
of val('H') ; timeIncrement = pPeriod * TIME:Hour
of val('M') ; timeIncrement = pPeriod * TIME:Minute
of val('S') ; timeIncrement = pPeriod * TIME:Second
! else ; DebugOut('Unknown Period '& pTypeOfPeriod)
end
if timeIncrement
timeIncrement += pStartTime - 1
daysIncrement = timeIncrement / TIME:Day
if daysIncrement
pEndDate += daysIncrement
timeIncrement -= daysIncrement * TIME:Day
end
pEndTime = timeIncrement + 1
if pEndTime < 1
pEndTime += TIME:Day
pEndDate -= 1
end
else
pEndTime = pStartTime
end
edit: I mentioned I did a few basic tests. I will include 20 tests here so that if you change anything or do a different implementation you can run these as a check. The tests are in pairs basically subtracting an amount then adding it back - so you should get back to the original value. These tests are certainly not comprehensive so it would be good to add extras and add them here in a comment in this thread.
CalculateEndDateAndTime(deformat('20240101',@d12), 1, -1, 'D', endDate, endTime)
if format(endDate,@d12) <> '20231231' or endTime <> 1 then stop('failed test 1').
CalculateEndDateAndTime(endDate, endTime, 1, 'D', endDate, endTime)
if format(endDate,@d12) <> '20240101' or endTime <> 1 then stop('failed test 2').
CalculateEndDateAndTime(deformat('20240101',@d12), 1, -24, 'H', endDate, endTime)
if format(endDate,@d12) <> '20231231' or endTime <> 1 then stop('failed test 3: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(endDate, endTime, 24, 'H', endDate, endTime)
if format(endDate,@d12) <> '20240101' or endTime <> 1 then stop('failed test 4: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(deformat('20240101',@d12), 1, -(24*60), 'M', endDate, endTime)
if format(endDate,@d12) <> '20231231' or endTime <> 1 then stop('failed test 5: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(endDate, endTime, 24*60, 'M', endDate, endTime)
if format(endDate,@d12) <> '20240101' or endTime <> 1 then stop('failed test 6: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(deformat('20240101',@d12), 1, -(24*60*60), 'S', endDate, endTime)
if format(endDate,@d12) <> '20231231' or endTime <> 1 then stop('failed test 7: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(endDate, endTime, 24*60*60, 'S', endDate, endTime)
if format(endDate,@d12) <> '20240101' or endTime <> 1 then stop('failed test 8: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(deformat('20240101',@d12), 1, -36, 'H', endDate, endTime)
if format(endDate,@d12) <> '20231230' or endTime <> 1 + TIME:Day/2 then stop('failed test 9: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(endDate, endTime, 36, 'H', endDate, endTime)
if format(endDate,@d12) <> '20240101' or endTime <> 1 then stop('failed test 10: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(deformat('20240101',@d12), 1, -84, 'H', endDate, endTime)
if format(endDate,@d12) <> '20231228' or endTime <> 1 + TIME:Day/2 then stop('failed test 11: ' & format(endDate,@d12) & ' time: ' & endTime & ' expected: ' & 1 + TIME:Day/2).
CalculateEndDateAndTime(endDate, endTime, 84, 'H', endDate, endTime)
if format(endDate,@d12) <> '20240101' or endTime <> 1 then stop('failed test 12: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(deformat('20240101',@d12), 1, -1, 'H', endDate, endTime)
if format(endDate,@d12) <> '20231231' or endTime <> 1 + 23*TIME:Hour then stop('failed test 13: ' & format(endDate,@d12) & ' time: ' & endTime & ' expected: ' & 1 + 11*TIME:Hour).
CalculateEndDateAndTime(endDate, endTime, 1, 'H', endDate, endTime)
if format(endDate,@d12) <> '20240101' or endTime <> 1 then stop('failed test 14: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(deformat('20240101',@d12), 1, -1, 'M', endDate, endTime)
if format(endDate,@d12) <> '20231231' or endTime <> deformat('235900',@t5) then stop('failed test 15: ' & format(endDate,@d12) & ' time: ' & endTime & ' expected: ' & deformat('235900',@t5)).
CalculateEndDateAndTime(endDate, endTime, 1, 'M', endDate, endTime)
if format(endDate,@d12) <> '20240101' or endTime <> 1 then stop('failed test 16: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(deformat('20240101',@d12), 1, -1, 'S', endDate, endTime)
if format(endDate,@d12) <> '20231231' or endTime <> deformat('235959',@t5) then stop('failed test 17: ' & format(endDate,@d12) & ' time: ' & endTime & ' expected: ' & deformat('235959',@t5)).
CalculateEndDateAndTime(endDate, endTime, 1, 'S', endDate, endTime)
if format(endDate,@d12) <> '20240101' or endTime <> 1 then stop('failed test 18: ' & format(endDate,@d12) & ' time: ' & endTime).
CalculateEndDateAndTime(deformat('20240101',@d12), 100, -1, 'S', endDate, endTime)
if format(endDate,@d12) <> '20231231' or endTime <> deformat('235959',@t5)+99 then stop('failed test 19: ' & format(endDate,@d12) & ' time: ' & endTime & ' expected: ' & deformat('235959',@t5)+99).
CalculateEndDateAndTime(endDate, endTime, 1, 'S', endDate, endTime)
if format(endDate,@d12) <> '20240101' or endTime <> 100 then stop('failed test 20: ' & format(endDate,@d12) & ' time: ' & endTime).
stop('done date/time tests')
aside: Where I said
deformat('235959',@t5)+99
I could simply have said TIME:Day