Modify

Opened 4 years ago

Closed 4 years ago

Last modified 4 years ago

#3002 closed Bug (No Bug)

Bug - _DateAdd('s', 1, '1970/01/01') Returns '1970/01/01'

Reported by: mLipok Owned by:
Milestone: Component: Standard UDFs
Version: 3.3.13.19 Severity: None
Keywords: Cc:

Description

Repro:

#include <Date.au3>

ConsoleWrite(@CRLF)
; positive results
_DateAdd_TEST('1970/01/01 00:00:00')
; negative results
_DateAdd_TEST('1970/01/01')
; positive results
_DateAdd_TEST_2('1970/01/01')

Func _DateAdd_TEST($sDate)
	$sDate = _DateAdd('s', 1, $sDate)
	ConsoleWrite('@error  = ' & @error & @CRLF)
	ConsoleWrite($sDate & @CRLF)
	ConsoleWrite(@CRLF)
EndFunc   ;==>_DateAdd_TEST

Func _DateAdd_TEST_2($sDate)
	If StringRegExpReplace($sDate, '(\d{4}\/\d{2}\/\d{2})', '') = '' Then $sDate &= ' 00:00:00'
	$sDate = _DateAdd('s', 1, $sDate)
	ConsoleWrite('@error  = ' & @error & @CRLF)
	ConsoleWrite($sDate & @CRLF)
	ConsoleWrite(@CRLF)
EndFunc   ;==>_DateAdd_TEST_2

solusion proposal:

new internal function:

Func __DateExpand(Byref $sDate)
	If StringRegExpReplace($sDate, '(\d{4}\/\d{2}\/\d{2})', '') = '' Then $sDate &= ' 00:00:00'
EndFunc   ;==>__DateExpand

and modyfication:

Func _DateAdd($sType, $iNumber, $sDate)
	Local $asTimePart[4]
	Local $asDatePart[4]
	Local $iJulianDate
	; Verify that $sType is Valid
	$sType = StringLeft($sType, 1)
	If StringInStr("D,M,Y,w,h,n,s", $sType) = 0 Or $sType = "" Then
		Return SetError(1, 0, 0)
	EndIf
	; Verify that Value to Add  is Valid
	If Not StringIsInt($iNumber) Then
		Return SetError(2, 0, 0)
	EndIf

	__DateExpand($sDate)

	; Verify If InputDate is valid
	If Not _DateIsValid($sDate) Then
		Return SetError(3, 0, 0)
	EndIf

	; split the date and time into arrays
	_DateTimeSplit($sDate, $asDatePart, $asTimePart)

	; ====================================================
	; adding days then get the julian date
	; add the number of day
	; and convert back to Gregorian
	If $sType = "d" Or $sType = "w" Then
		If $sType = "w" Then $iNumber = $iNumber * 7
		$iJulianDate = _DateToDayValue($asDatePart[1], $asDatePart[2], $asDatePart[3]) + $iNumber
		_DayValueToDate($iJulianDate, $asDatePart[1], $asDatePart[2], $asDatePart[3])
	EndIf
	; ====================================================
	; adding Months
	If $sType = "m" Then
		$asDatePart[2] = $asDatePart[2] + $iNumber
		; pos number of months
		While $asDatePart[2] > 12
			$asDatePart[2] = $asDatePart[2] - 12
			$asDatePart[1] = $asDatePart[1] + 1
		WEnd
		; Neg number of months
		While $asDatePart[2] < 1
			$asDatePart[2] = $asDatePart[2] + 12
			$asDatePart[1] = $asDatePart[1] - 1
		WEnd
	EndIf
	; ====================================================
	; adding Years
	If $sType = "y" Then
		$asDatePart[1] = $asDatePart[1] + $iNumber
	EndIf
	; ====================================================
	; adding Time value
	If $sType = "h" Or $sType = "n" Or $sType = "s" Then
		Local $iTimeVal = _TimeToTicks($asTimePart[1], $asTimePart[2], $asTimePart[3]) / 1000
		If $sType = "h" Then $iTimeVal = $iTimeVal + $iNumber * 3600
		If $sType = "n" Then $iTimeVal = $iTimeVal + $iNumber * 60
		If $sType = "s" Then $iTimeVal = $iTimeVal + $iNumber
		; calculated days to add
		Local $iDay2Add = Int($iTimeVal / (24 * 60 * 60))
		$iTimeVal = $iTimeVal - $iDay2Add * 24 * 60 * 60
		If $iTimeVal < 0 Then
			$iDay2Add = $iDay2Add - 1
			$iTimeVal = $iTimeVal + 24 * 60 * 60
		EndIf
		$iJulianDate = _DateToDayValue($asDatePart[1], $asDatePart[2], $asDatePart[3]) + $iDay2Add
		; calculate the julian back to date
		_DayValueToDate($iJulianDate, $asDatePart[1], $asDatePart[2], $asDatePart[3])
		; caluculate the new time
		_TicksToTime($iTimeVal * 1000, $asTimePart[1], $asTimePart[2], $asTimePart[3])
	EndIf
	; ====================================================
	; check if the Input day is Greater then the new month last day.
	; if so then change it to the last possible day in the month
	Local $iNumDays = _DaysInMonth($asDatePart[1])
	;
	If $iNumDays[$asDatePart[2]] < $asDatePart[3] Then $asDatePart[3] = $iNumDays[$asDatePart[2]]
	; ========================
	; Format the return date
	$sDate = $asDatePart[1] & '/' & StringRight("0" & $asDatePart[2], 2) & '/' & StringRight("0" & $asDatePart[3], 2)
	; add the time when specified in the input
	If $asTimePart[0] > 0 Then
		If $asTimePart[0] > 2 Then
			$sDate = $sDate & " " & StringRight("0" & $asTimePart[1], 2) & ':' & StringRight("0" & $asTimePart[2], 2) & ':' & StringRight("0" & $asTimePart[3], 2)
		Else
			$sDate = $sDate & " " & StringRight("0" & $asTimePart[1], 2) & ':' & StringRight("0" & $asTimePart[2], 2)
		EndIf
	EndIf
	;
	Return $sDate
EndFunc   ;==>_DateAdd

Attachments (0)

Change History (6)

comment:1 Changed 4 years ago by mLipok

Modyfication I mean only by appropriate use this new internal function.

comment:2 Changed 4 years ago by jchd

I'm not sure this qualifies as 'bug' and I'd rather see this as a feature request, for when you supply a date only (without time), that doesn't always imply that you mean time = '00:00:00'. Yet commonly used library functions work the way to want and consider a default time part to be '00:00:00'.

BTW if ever this is accepted, I'd like other would also consider another modification. Currently the function only works when the date separator is a slash (/), but the standard separator specified by ISO 8601 is a dash (-). It would be pretty beneficial to simply mask off the separator in the regexp, yet take care to use the same separator in output.

; this should definitely work
_DateAdd('s', 1, '2015-03-21')

The same change should be made to related _Date* functions, like _DateDiff and some others. The best witness that this change is indeed needed is that _DateIsValid accepts both separators!

Note: I don't advocate to accept any more format variant described in ISO 8601.

comment:3 Changed 4 years ago by guinness

jchd,

It does accept - (see the StringSplit() in _DateTimeSplit()

#include <Date.au3>

ConsoleWrite(_DateAdd('s', 1, '2015-03-21 00:00:00') & @CRLF)

Unless you meant the output should be 2015-03-21 00:00:01 and not 2015/03/21 00:00:00

comment:4 Changed 4 years ago by jchd18

Oops, I never read the help file down to the last Remark line.

BTW, this important remark about allowable date formats would be better placed in the $Date parameter description: "Input date in the format YYYY/MM/DD[ HH:MM:SS]" --> "Input date in any format described in _DateTimeSplit()".

Sorry for the noise, but yes I still find it would be natural to expect the function to return dates under the same format used for input.

comment:5 Changed 4 years ago by Melba23

  • Resolution set to No Bug
  • Status changed from new to closed

I am going to close this. It seems illogical to me that the function should deal with adding/subtracting an interval smaller than the lowest interval defined in the DTG passed to it. If the user wants to add/subtract a time interval they need to define the start time, not expect the function to default to some arbitrary value.

M23

comment:6 Changed 4 years ago by mLipok

After deliberation, I have to agree with your opinion, and also to ask you to consider, adding an appropriate description for the Help documentation, because this was not clear for me so can be not clear for others, especialy non native english coders.

Guidelines for posting comments:

  • You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
  • In-depth discussions should take place on the forum.

For more information see the full version of the ticket guidelines here.

Add Comment

Modify Ticket

Action
as closed The ticket will remain with no owner.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.