Visual basic date object parsing from string with indexing - vba

I would like to ask if in VBA there is a built in function which will parse a date object from a string based on a specified format.
For example:
dateString = "24-4-12"
VBADateFunc(dateString, "dd-m-yy")
to return a date object interpreting the dateString string by the provided format.
I will appretiate your ideas on this.
Thank you

Here you go:
Public Sub TestMe()
Dim dtMyDate As Date
dtMyDate = Format("24-4-12", "dd-mm-yy")
Debug.Print dtMyDate
Debug.Print Format(dtMyDate, "yyyy")
Debug.Print Format(dtMyDate, "dd-mmm-yy")
'For Non-Europeans:
dtMyDate = Format(DateSerial(2012, 4, 24), "dd-mm-yy")
Debug.Print dtMyDate
Debug.Print Format(dtMyDate, "yyyy")
Debug.Print Format(dtMyDate, "dd-mmm-yy")
End Sub
From the comments - in general, the date is a long value in MS Excel and VBA. Today's date can be seen like this in the immediate window:
?clng(now)
42935
If you want to do further something with the 42935 value, you may go like this:
?Format(42934,"dd-mm-yyyy")
Note: Today is 42934 for all those, who have ActiveWorkbook.Date1904 = False. For those, who are starting the calendar with 1904, today is 42935-4*365-1

I ended up writing my own function to scan a date. Leaving out error handling:
Function ScanDate(s As String, Optional order As String = "DMY", Optional separator As String = "-") As Date
Dim parts() As String
parts = Split(s, separator)
Dim day As Long, month As Long, year As Long
day = parts(InStr(order, "D") - 1)
month = parts(InStr(order, "M") - 1)
year = parts(InStr(order, "Y") - 1)
ScanDate = DateSerial(year, month, day)
End Function

Related

What datetime format does Cdate() return in VBA?

I am converting strings into a date format in VBA. The string must first be parsed into date format (whilst still a string), before being cast to a date. E.g I must first transform "091220" to "09/12/20" and then call Cdate()
However, I cannot verify what datetime format is the default for Cdate(). The official documentation here and other sources such as here don't state the format that is returned. Primarily, does Cdate() return a date as dd/mm/yy or mm/dd/yy?
Other questions regarding Cdate() such as here mention the format depends upon the region you are in. I am in the UK.
Through the use of a MWE, I have shown that Cdate() returns it as dd/mm/yy, however I would like to know for sure before I apply this function to the rest of my project.
I attach this MWE below.
public sub date_test()
dim early_date as string '9th June 2020
dim late_date as string '4th July 2020
early_date = "090620"
late_date = "040720"
early_date = format(left(early_date,2) & "/" & mid(early_date, 3, 2) & "/" & right(early_date,2), "dd/mm/yy")
late_date = format(left(late_date,2) & "/" & mid(late,3,2) & "/" & right(late_date,2)
msgbox Cdate(late_date) > Cdate(early_date) 'I would like this to return True, which it does
end sub
You should always handel dates as Date. Try:
public sub date_test()
dim early_text as string '9th June 2020
dim late_text as string '4th July 2020
dim early_date as date
dim late_date as date
early_text = "090620"
late_text = "040720"
early_date = DateSerial(right(early_text, 2), mid(early_text, 3, 2), left(early_text, 2))
late_date = DateSerial(right(late_text, 2), mid(late_text, 3, 2), left(late_text, 2))
msgbox DateDiff("d", early_date, late_date) > 0
end sub

How to specify a format when converting String to Date

I know I can use Format() when converting a date into a string. And Cells.NumberFormat to change the display text of a date.
I'm trying to go the opposite direction. I have a string like "231211" or "08AUG11" and a corresponding date format like "YYMMDD" or "DDMMMYY" and I just need to convert the string based on the format. I already have a way to find the correct format of each string, I just need to do the conversion.
I can't seem to find the right function/method to do what I'm asking. DateTime.ParseExact doesn't seem to exist in Excel 2007, even after I added mscorlib.dll as a reference.
Is there an alternative I can use? Or how do I get DateTime.ParseExact to work properly in Excel 2007?
Since the string is not always the same length and there are about 30 different formats, it's quite annoying to conditionally split the string and interpret the substrings individually. I would love to be able to just parse the date based on the format.
here is a start to create your own function:
Function dateParser(str As String, fmt As String) As Date
If Len(str) <> Len(fmt) Then Exit Function
Dim dy As Long
Dim mnth As String
Dim yr As Long
Dim mnthnm As Long
Dim i As Long
For i = 1 To Len(str)
If UCase(Mid(fmt, i, 1)) = "D" Then
dy = dy * 10 + CLng(Mid(str, i, 1))
ElseIf UCase(Mid(fmt, i, 1)) = "Y" Then
yr = yr * 10 + CLng(Mid(str, i, 1))
ElseIf UCase(Mid(fmt, i, 1)) = "M" Then
mnth = mnth & Mid(str, i, 1)
End If
Next i
If IsNumeric(mnth) Then
mnthnm = CLng(mnth)
Else
mnthnm = Month(CDate("01 " & mnth & " 2020"))
End If
dateParser = DateSerial(yr, mnthnm, dy)
End Function
Used like:
Sub test()
Dim str As String
str = "08AUG11"
Dim fmt As String
fmt = "DDMMMYY"
Dim x As Date
x = dateParser(str, fmt)
debug.print x
End Sub

Getting Today and Yesterday in Date and String Format via VBA

I'm struggeling to format my date strings correctly as it appears to be minusing the wrong parts of my date using DateAdd and I'm not sure how to resolve.
Eg:
Sub DateTest()
DateStr = Format(Date, "DD-MM-YY")
Yesterday = Format(DateAdd("d", -1, CDate(DateStr)), "DD-MM-YY")
YtdStr = Format(Yesterday, "DD-MM-YY")
Debug.Print DateStr
Debug.Print Yesterday
Debug.Print YtdStr
End Sub
Result:
13-09-20
19-09-13
13-09-19
Expected Result:
13-09-20
12-09-20
12-09-20
I even tried using just "day of year" as this is for an hidden report, but I like using regular date strings I think or at least I'd like to get both figured out, but using day of year showed me some interesting results. Yesterday had to be d 0 which made no sense, but if I used -1 it removed two dates. As well, turning that into a string seems to remove another day?
Eg:
Sub DateTest()
DateStr = Format(Date, "Y")
YtdDate = Format(DateAdd("d", 0, CDate(DateStr)), "Y")
YtdStr = Format(YtdDate, "Y")
Debug.Print DateStr
Debug.Print YtdDate
Debug.Print YtdStr
End Sub
Resut:
257
256
255 'This was expected to be a string 256?
Can anybody point out how to format this correctly?
You are formatting the source date as string using DD-MM-YY, parsing it back to date using your current system default date format (apparently MM-DD-YY), adding days to the result of that, and formatting it back to string. You don't want these intermediate formatting to strings.
Sub DateTest()
Dim DateNotStr As Date
Dim YesterdayNotStrEither As Date
DateNotStr = Date
YesterdayNotStrEither = DateAdd("d", -1, DateNotStr)
Debug.Print Format$(DateNotStr, "dd-mm-yy")
Debug.Print Format$(YesterdayNotStrEither , "dd-mm-yy")
End Sub
I think the issue was CDate wasn't transforming my date correctly based on my systems default date format. I created a variable for today in date format and used that to generate yesterdays variable.
Eg:
Public Sub SetDateVars()
TdDate = Date
TdStr = Format(Date, "DD-MM-YY")
YtdDate = Format(DateAdd("d", -1, TdDate))
YtdStr = Format(YtdDate, "DD-MM-YY")
Debug.Print TdDate
Debug.Print TdStr
Debug.Print YtdDate
Debug.Print YtdStr
End Sub
Result:
9/13/2020
13-09-20
9/12/2020
12-09-20

Read the correct week number from a calendar date

I have below dates in an Excel column, as you can see.
Sprint 1 takes from 10.04 to 21.04 this means 2 weeks and between brackets they are specified week 15 and 16 which is correct but for Sprint 2, who also starts in 10.04 but takes until 05.05 it means 7 weeks, but are displayed also the weeks from the Sprint1.
"Sprint1 (CW15-16/2017)
[10.04.2017 - 21.04.2017]
Sprint2 (CW15-16/2017)
[10.04.2017 - 05.05.2017]"
What I have until now is:
'reading the first CW of the sprint based on the date
SprintFristCW = Left(planning_wb.Worksheets(SprintPlanningTable).Cells(2, i + 1).Value, 9)
'reading the last CW of the Sprint based on the date
SprintEndCW = Right(planning_wb.Worksheets(SprintPlanningTable).Cells(2, i + Sprintlength).Value, 9)
SprintCW = Left(SprintFirstCW, 4) & "-" & Right(SprintEndCW, 7)
But SprintEndCW is not reading correct the week number.
So I need to read the correct week number in which each sprint ends and print it.
Don't create huge procedures. Small is beautiful. Create functions that feed into your Main procedure. Here is an example. The procedure TestExtraction calls the function ExtractWeeks. Therefore ExtractWeeks needs not be part of the procedure that calls it, making the code easier to understand and maintain.
Private Sub TestExtraction()
Dim Fun As Long
Dim DateString As String
Dim StartDate As Date, EndDate As Date
DateString = ActiveCell.Value
' the DateString is re-defined here for testing purposes
DateString = "[10.04.2017 - 05.05.2017]"
Fun = ExtractWeeks(DateString, StartDate, EndDate)
If Fun < 0 Then
Debug.Print "Invalid date"
Else
With Application
DateString = "(CW" & .WeekNum(StartDate)
If Year(StartDate) <> Year(EndDate) Then _
DateString = DateString & "/" & Year(StartDate)
DateString = DateString & " - " & .WeekNum(EndDate) & "/" & Year(EndDate) & ")"
End With
Debug.Print DateString
Debug.Print Fun & " weeks"
End If
End Sub
Private Function ExtractWeeks(ByVal DateString As String, _
StartDate As Date, _
EndDate As Date) As Long
' 24 Oct 2017
' return the number of weeks between dates (rounded up)
' return -1 if one of the dates is unreadable
Dim Dates() As String
Dim i As Integer
Dates = Split(Mid(DateString, 2, Len(DateString) - 2), "-")
On Error Resume Next
For i = 0 To 1
Dates(i) = Replace(Trim(Dates(i)), ".", Application.International(xlDateSeparator))
Next i
StartDate = DateValue(Dates(0))
EndDate = DateValue(Dates(1))
If Err Then
ExtractWeeks = -1
Else
ExtractWeeks = Int((StartDate - EndDate) / 7) * -1
End If
End Function
The point is that not everything that looks like a date is a date Excel can understand. The Function ExtractWeeks converts the "dates' from your worksheet into real dates and returns these dates to the calling procedure. It also returns -1 in case of error which you can use to trap such errors. In my example, the function returns the number of weeks (or -1). You might let it return the CW string my calling procedure constructs. You will find it easy to move the process of constructing that string to the function and let the function return "" in case of error instead of -1. Perhaps you can exclude the possibility of errors in the dates. This is a question of how you integrate the function into your Main.

How can I convert a long date with time to a different format in VBA?

I have data for a date that looks like this: "2015-02-11T19:41:50-08:00"
I would like to know if there is already a function that exists in VBA which can convert the above data to the format of something like "02/11/2015 11:41 AM PST"
I attempted the following code playing around with the format function but was unable to get VBA to recognize the format as a date:
testdate = "2015-02-12T22:57:05-08:00"
newdate = Format(testdate, "mm/dd/yyyy hh/nn/ss AM/PM")
Debug.Print newdate
The output was still "2015-02-12T22:57:05-08:00"
Thanks for the help.
Edit:
I was able to resolve the problem by taking your suggestions to use the mid() function since the dates are in fixed format. I decided to keep the military time in the final version.
Here is my code for anyone curious:
Function convertDate(orderdate)
'takes the date formatted as 2015-02-06T08:26:00-08:00
'and converts it to mm/dd/yyyy hh/nn/ss UTC format
'2015-02-06T08:26:00-08:00
orderyear = Mid(orderdate, 1, 4)
ordermonth = Mid(orderdate, 6, 2)
orderday = Mid(orderdate, 9, 2)
orderhour = Mid(orderdate, 12, 2)
orderminute = Mid(orderdate, 15, 2)
ordersecond = Mid(orderdate, 18, 2)
newdate = ordermonth & "/" & orderday & "/" & orderyear
newtime = orderhour & ":" & orderminute & ":" & ordersecond
'Debug.Print newdate
convertDate = newdate & " " & newtime & " UTC"
End Function
Because your input isn't a true date none of Excel or VBA's date methods will work with it. Your best bet is to break the string down into parts, work with them individually, and then join it all back up again - for example:
testdate = "2015-02-12T22:57:05-08:00"
'// The letter T is redundant, so let's split the string here into an array:
dateArr = Split(testdate, "T")
'// Part 1 of the array can be easily converted with CDate() and Format()
dateArr(0) = Format(CDate(dateArr(0)), "mm/dd/yyyy")
'// Part 2 of the array will need to be broken down further:
dateArr(1) = Format(TimeValue(Split(dateArr(1), "-")(0)) - _
TimeSerial(Left(Split(dateArr(1), "-")(1), 2), _
Right(Split(dateArr(1), "-")(1), 2), 0), "hh:mm:ss")
'// The above line does the following:
'// 1) Split the second part of the array again, using the "-" as the delimiter
'// 2) Convert the first part of this (22:57:05) to a time using TimeValue()
'// 3) Convert the second part (08:00) to hours & minutes using TimeSerial()
'// 4) Minus the latter from the former (which can only be done if both are a valid time)
'// 5) Wrap all that into a Format() method to show "hh:mm:ss" instead of a Double.
'// Join the two parts back together and add "PST" on the end.
newdate = Join(dateArr, " ") & " PST"
Debug.Print newdate
'// Output will display "02/12/2015 14:57:05 PST"
N.B. I have chosen not to include "AM" or "PM" because your time is in 24hr format anyway so I don't see the relevance...
It's not converting because of the "T" and because of the tacked on time range at the end. You can ditch the "T" and truncate off the trailing range and it will convert.
Public Sub Example()
Const testValue As String = "2015-02-12T22:57:05-08:00"
Dim dateValue As Date
Dim stringValue As String
Dim subVal As Date
Dim hyphenPos As Long
stringValue = testValue
Mid(stringValue, 11&, 1&) = " "
hyphenPos = InStrRev(stringValue, "-")
subVal = Mid$(stringValue, hyphenPos + 1&)
dateValue = CDate(Left$(stringValue, hyphenPos - 1&)) - subVal
End Sub
Couple of ideas:
The sample date you have 2015-02-12T22:57:05-08:00 is not a real date (I think)
I think the following will give you the closest format to what you are looking for (you will need to define the range.Range.NumberFormat = "[$-409]h:mm:ss AM/PM"
Your best bet is concating "PST" to a date datatype formatted to your needs.
Sub DebugPrintDate()
Dim testdate As Date: testdate = Now
newdate = Format(testdate, "mmm/dd/yyyy hh:mm AM/PM") & " PST"
Debug.Print newdate
End Sub
Ouput:
Never mind the "févr". My system locale is France.
If you want to define a particular date, make sure to wrap the date in two #s.
Example:
Dim someDateAndTime As Date = #8/13/2002 12:14 PM#