I have problem in vba in excel.
I want the function to check whether the second day of the passed date has already been or not. I know that I'm comparison two string now, i dont know how change it to date format. Here is my code fragment:
Dim MyDate As Date
Set Passed = Range("D2")
MyDate = Format(DateTime.Now, "DD-DD.MM.YYYY")
If MyDate < Passed Then
Passed.Interior.ColorIndex = 45
ElseIf MyDate > Passed Then
Passed.Interior.ColorIndex = 43
The D2 is Short Date Format (eg. 02-05.02.2021)
You convert your String data type to a Date datatype using the CDate function.
For example CDate(Passed)
You will also need to remove the Format function as that converts your DatetTime.Now to a String
If CDate(Range("D2")) > DateTime.Now() Then
'Do Something
Else
'Do Something else
EndIf
Related
Microsoft Excel does not recognise pre-1900 dates and there is plenty of information online which documents this, including the question here.
The best work around (which many other posts link to) seems to be at ExcelUser
However, although the work around gets Excel to recognise a pre-1900 date as a date, it still does not allow it to be used in calculations e.g. when wanting to calculate the number of years since a pre-1900 date.
My question is whether the work around described at ExcelUser can be modified to allow the result to be used in a calculation.
To put things simply, for example, I want to calculate in Excel the number of years since 1/4/1756 - is this possible?
Or does another solution have to be adopted? Perhaps there are plug-ins which address this problem?
First of all I highly recommend to use the ISO 8601 format yyyy-mm-dd for dates because even if you only have strings and no real numeric dates this is properly sortable and the only date format that is defined clearly and cannot be misinterpret like 02/03/2021 where no one can ever say if it is mm/dd/yyyy or dd/mm/yyyy because both actually exist.
Since old dates cannot be real numeric dates but only entered as strings (looking like a date) that means misinterpretation needs to be avioded or you get wrong results. Therefore a date format that cannot be misinterpret is a clear advantage.
Second there is more than one way to calculate "how many years since the birth of Mr. X": For example lets take the birthday of Maryam Mirzakhani 1977-05-12 compared to the date today 2021-04-15. Today she would not have had birthday yet this year and therefore she would be 43 years old. But this year she would have turned 44 years (2021 - 1977 = 44). So the question needs to be asked more precisely. Either "how old would Mr. X be today?" or "how old would Mr. X be this year". The calculation for that would be different.
So let's start and assume the following data. We already know the fact that Excel cannot calculate with dates before 1900. You can see that if we enter pre-1900 dates that they are formatted as string (red dates) and post-1900 dates get formatted as numeric dates (green dates).
Image 1: #WERT! means #VALUE! (sorry for the German screenshot).
Also in column D where the formula =DATEDIF($B2,TODAY(),"y") was used the string dates cannot be calculated with. But since VBA can actually handle pre-1900 dates we can write our own UDF (user defined function) for that. Since as I explained above there is 2 different methods to calculate there is 2 different functions:
OldDateDiff(Date1, Date2, Interval) called like =OldDateDiff($B2,TODAY(),"yyyy")
OldDateAge(Date1, Date2) called like =OldDateAge($B2,TODAY())
Option Explicit
Public Function OldDateDiff(ByVal Date1 As Variant, ByVal Date2 As Variant, ByVal Interval As String) As Long
Dim RetVal As Long 'variable for the value we want to return
Dim localDate1 As Date
If VarType(Date1) = vbDate Or VarType(Date1) = vbDouble Then 'check if Date1 is numeric
localDate1 = CDate(Date1) 'if numeric take it
ElseIf VarType(Date1) = vbString Then 'check if Date1 is a string
localDate1 = ISO8601StringToDate(Date1) 'if it is a string convert it to numeric
Else 'neither string nor numeric throw an error
RetVal = CVErr(xlErrValue)
Exit Function
End If
Dim localDate2 As Date 'same as for Date1 but with Date2
If VarType(Date2) = vbDate Or VarType(Date2) = vbDouble Then
localDate2 = CDate(Date2)
ElseIf VarType(Date2) = vbString Then
localDate2 = ISO8601StringToDate(Date2)
Else
RetVal = CVErr(xlErrValue)
Exit Function
End If
If localDate1 <> 0 And localDate2 <> 0 Then 'make sure both dates were filled with values
RetVal = DateDiff(Interval, localDate1, localDate2) 'calculate the difference between dates with the desired interaval eg yyyy for years
End If
OldDateDiff = RetVal 'return the difference as result of the function
End Function
Public Function OldDateAge(ByVal Date1 As Variant, ByVal Date2 As Variant) As Long
Dim RetVal As Long 'variable for the value we want to return
Dim localDate1 As Date
If VarType(Date1) = vbDate Or VarType(Date1) = vbDouble Then 'check if Date1 is numeric
localDate1 = CDate(Date1) 'if numeric take it
ElseIf VarType(Date1) = vbString Then 'check if Date1 is a string
localDate1 = ISO8601StringToDate(Date1) 'if it is a string convert it to numeric
Else 'neither string nor numeric throw an error
RetVal = CVErr(xlErrValue)
Exit Function
End If
Dim localDate2 As Date 'same as for Date1 but with Date2
If VarType(Date2) = vbDate Or VarType(Date2) = vbDouble Then
localDate2 = CDate(Date2)
ElseIf VarType(Date2) = vbString Then
localDate2 = ISO8601StringToDate(Date2)
Else
RetVal = CVErr(xlErrValue)
Exit Function
End If
If localDate1 <> 0 And localDate2 <> 0 Then 'make sure both dates were filled with values
RetVal = WorksheetFunction.RoundDown((localDate2 - localDate1) / 365, 0)
'subtract date1 from date2 and divide by 365 to get years, then round down to full years to respect the birthday date.
End If
OldDateAge = RetVal 'return the age as result of the function
End Function
' convert yyyy-mm-dd string into numeric date
Private Function ISO8601StringToDate(ByVal ISO8601String As String) As Date
Dim ISO8601Split() As String
ISO8601Split = Split(ISO8601String, "-") 'split input yyyy-mm-dd by dashes into an array with 3 parts
ISO8601StringToDate = DateSerial(ISO8601Split(0), ISO8601Split(1), ISO8601Split(2)) 'DateSerial returns a real numeric date
' ≙yyyy ≙mm ≙dd
End Function
Note that here column B contains 2 different kind of data. Strings (that look like a date) and real numeric dates. If you sort them, all the numeric dates will sort before the string dates (which is probably not what you want). So if you want this to be sortable by birthday column make sure you turn all dates into strings. This can be done by adding an apostrophe ' infront of every date. This will not display but ensure the entered date is considered to be a string.
If your date is in an unambiguous format (eg ISO or a format corresponding to your Windows Regional Settings, or a real date if after 1900), you can use VBA which will recognize early dates.
Function Age(dt As Date)
Age = DateDiff("yyyy", dt, Date)
End Function
You should be aware that, because of how the function calculates years differences, depending on what you want exactly for a result, you may need to adjust the answer if the birthdate is before/after today's date.
In other words, if the day of the year of the birthdate is after the day of the year of Today, you may need to subtract 1 from the result.
But this should get you started.
There's a much easier way than the accepted answer. Simply convert your dates to Unix time:
Function nUnixTime(dTimestamp As Date) As LongLong
' Return given VB date converted to a Unix timestamp.
Const nSecondsPerDay As Long = 86400 ' 24 * 60 * 60
nUnixTime = Int(CDbl(CDate(dTimestamp) - CDate("1/1/1970"))) * nSecondsPerDay
End Function
Unix time is the number of seconds since Jan. 1, 1970, with times before that date being negative. So if you convert your dates to Unix time, you can just subtract them and divide the result by 86,400 to have the difference in days, or by 31,557,600 for years (31,557,600 = 60 * 60 * 24 * 365.25).
Example results of the above VB function called from Excel:
Column A
Column B formula
Column B value
2/2/2022
=nUnixTime(A1)
1,643,760,000
1/1/1970
=nUnixTime(A2)
0
12/14/1901
=nUnixTime(A3)
-2,147,472,000
12/13/1901
=nUnixTime(A4)
-2,147,558,400
1/1/1900
=nUnixTime(A5)
-2,208,988,800
1/1/1800
=nUnixTime(A6)
-5,364,662,400
1/1/100
=nUnixTime(A7)
-59,011,459,200
The reason I included the two dates in 1901 is because their magnitudes in Unix time are just smaller than and just larger than the largest magnitude of a signed 32-bit integer, i.e., a Long in VBA. If the output of the above function were a Long, then values for dates before Dec. 14, 1901 would be the error #Value!. That is the reason the output of the function is defined as LongLong, which is VBA's signed 64-bit integer.
In vba,I am trying to convert datetime stamp(1899-12-30T00:00:00.000+0000) from oracle server into dd-mm-yyyy. I need to have a date format which can be recognised by excel and display it in a specific manner using cell formatting (like excel is parsing the date from sql server into dd-mm-yyyy type on its own using cell formatting but unable to recognise the oracle format for the same).
For j = 1 To colCount
mainWorkBook.Sheets("sheet1").Range(colToLetter(j) & 1).Value = node(0).ChildNodes(j - 1).tagName
Next j
For i = 0 To rowCount - 1
For j = 1 To colCount
If IsDate(node(i).ChildNodes(j - 1).Text) Then
mainWorkBook.Sheets("sheet1").Range(colToLetter(j) & i + 2).Value = CDate(node(i).ChildNodes(j - 1).Text)
Else
mainWorkBook.Sheets("sheet1").Range(colToLetter(j) & i + 2).Value = node(i).ChildNodes(j - 1).Text
End If
Next j
Next i
Date values are handled differently in vba, excel and oracle.
Do you really need pre 1900 dates?
Value ranges:
vba: 1 January 100, to 31 December 9999
Date variables are stored as IEEE 64-bit (8-byte) floating-point numbers
see https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/date-data-type
oracle: year -4712 to 9999 (excluding year 0)
different datatypes available depending on your needs:
DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE, and TIMESTAMP WITH LOCAL TIME ZONE
see https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements001.htm#SQLRF00204
Excel: 01.01.1900 - 31.12.9999
converting 0 to a date => 00.01.1900
converting 1 to a date => 01.01.1900
...
converting 2958465 to a date => 31.12.9999
I you only want to display the contents of the oracle database you can easily just display the textual content. If you want to calculate with the dates you might want to add a shift to do it in excel or you could do the calculation in vba and only display the results as strings.
If you need "historical" dates in your solution and want to calculate with it search for the different available options: "excel date pre 1900"
see also http://www.creativedeletion.com/2016/04/29/programmers-know-dates.html
see also https://en.wikipedia.org/wiki/Time_formatting_and_storage_bugs
If I understand correctly you simply want to convert a string to a valid date; the DateSerial functions offers a reliable way:
Function timeStampOracle2VBA(myDate) As Date
Dim Tokens ' i.e. As Variant to allow split
tokens = Split(Split(myDate, "T")(0), "-", 3) ' limit split results to 3 tokens
Dim dat
dat = DateSerial(Val(tokens(0)), Val(tokens(1)), Val(tokens(2))) ' arguments year, month, date
timeStampOracle2VBA = dat ' return function value
End Function
Example call
Sub test()
Debug.Print Format(timeStampOracle2VBA("1999-12-30T00:00:00.000+0000"), "dd-mm-yyyy")
End Sub
I'm trying to extract a date from a spreadsheet that falls between 01/07/2019 and 31/07/2019 and save that in the variable chantalJulyTotalDemanded.
Dim julyStart As Date
Dim julyFinish As Date
julyStart = CDate("01/07/2019")
julyFinish = CDate("31/07/2019")
If CDate(dataSheet.Cells(x, 12)) >= CDate(julyStart) And _
CDate(dataSheet.Cells(x, 12)) <= CDate(JulyFinish) Then
chantalJulyTotalDemanded = chantalJulyTotalDemanded + dataSheet.Cells(x, 10)
The above also grabs information from dates in August.
The date from the spreadsheet is in the format 1/07/2019.
Less of an answer and more of suggestions:
For date constants in VBA code, use #
julyStart = #1/7/2019#
I'm not sure sure about how VBA determines if this is Jan 7 or Jul 1. But what I suggest you do is break in VBA and move mouse over vars to see what it shows you.
In your loop create a variable for the cell value:
Dim cellDate As Date: cellDate = dataSheet.Cells(x, 12)
Now in break mode, you can inspect the variables to see if they are as you expect.
No need to run CDate on CDate(julyStart) since julyStart is already a date.
I'm assuming your date value in the cell is an actual date number value. If so, you shouldn't have any problem. But if it's a string value, then you'll need to figure out if CDate is performing the proper conversion of say 1/7/2019 vs 7/1/2019.
Update 05/03/2019
I used the following from the URL given below by Tom.
Function ConvertDate(InputStr As String) As Date
Dim InputYear As Integer
Dim InputMonth As Integer
Dim InputDay As Integer
'extract year, month and day
InputYear = Right(InputStr, 4)
InputMonth = Mid(InputStr, 4, 2)
InputDay = Left(InputStr, 2)
'put it together to a real date
ConvertDate = DateSerial(InputYear, InputMonth, InputDay)
End Function
It gives me the date in the way I want. But when I write the same date
to a text file, the date changes back to the PC format which is
MM/DD/YYYY. Is there a way to get this solved?.
****I have this excel VBA code which captures the date from another mainframe application which has the format of DD.MM.YYYY Once its captured I'm trying to change it back to the following format YYYY/MM/DD. I have applied the following format to the date_value column of my excel. Sheet1.Range("A2", "A50000").NumberFormat = "yyyy/mm/dd" This is how I captured the date value from the mainframe program. Cells(noRow, 9).Value = Trim(MyHost.TextRC(5, 68, 12)) When I run the program it does not change the date format the way I want. My PC date format is set to the following, MM/DD/YYYY. Any hint or guidance is highly appreciated.****
I am trying to find out the difference between the system date and the date stored in the worksheet. If the difference between them is > 30 days, the result is true, else the result is false
Dim result as boolean
Dim sDate as string
sDate = Date
if Worksheets("dates").Cells(1,1) - sDate > 30 then 'how do I do this?
result = true
else
result = false
end if
How do I find out the difference in days between the system date and the date stored in the worksheet? The date in the worksheet can be a past date, too.
I wonder why I rarely see people using the date functions.
You can also use this:
if DateDiff("d", date1, date2) > 30 then
in this case, date1 would be CDate(Worksheets("dates").Cells(1,1))
and date2 would be sdate (either cast with CDate or dim'd as a date as Jeff said.
"d" means we are getting the difference in days. Here are the intervals for years, months, etc. in VBA:
yyyy - Year
q - Quarter
m - Month
y - Day of year
d - Day
w - Weekday
ww - Week
h - Hour
n - Minute
s - Second
Try this:
if CDate(Worksheets("dates").Cells(1,1)) - sDate > 30 then
sDate is a STRING, which is NOT a Real Date!
Convert your string to a date, using either the CDate() function or the DateValue() function.
However, there is a caveat in this kind of conversion. These conversion will handle the following structures:
yyyy/mm/dd
yyyy/m/d
mm/dd/yyyy
m/d/yyyy
These will not be correctly converted
dd/mm/yyyy
d/m/yyyy
And avoid using any 2-digit year.
I would advise using the DateSerial() function for date conversion.
So regarding your code, assuming that the values on yor sheet are actually dates (to be certain, simply select the column and change the Number Format to GENERAL. If they are real dates, each will display a PURE NUMBER. Remember to hit UNDO to get your Date Format back)
Dim result As Boolean
If Worksheets("dates").Cells(1, 1).Value - Date > 30 Then
result = True
Else
result = False
End If