VBA: interpeting English date format as string - vba

I have a spreadsheet containing dates in English format:
12 Feb 2015
13 Oct 2016
etc.
However, since my MS Excel is in French, it doesn't take them as dates but rather as strings.
I need to do some parsing with those values, so I run my procedure but something annoying happens. VBA, set in English no matter the OS/Microsoft language, gets the 12 Feb 2015 as a date and so, when I print it back, it returns me the French date (12-févr-2015, read as 12/02/2015). This stops me comparing that value with the previous 12 Feb 2015.
Is there anyway I can ask VBA not to take the initiative of interpreting my string as a date?
Here is a simplified version of my code + test case (but I guess you won't be able to see the issue if running it on an English Excel):
Put the string 12 Feb 2015 in the Range A1 of your spreadsheet
Run the following code:
Sub test()
myVal = Range("A1").Value
Range("A2").Value = myVal
End Sub
The value in Range("A2") will be a date in your Excel language, and not a string as it was before.
Note: I have thought of writing Range("A2").Value = "'" & myVal to let the value be a String instead of a date, but it doesn't make me possible to later compare 12 Feb 2015 with '12 Feb 2015 and, anyway, 12 Feb 2015 becomes a date already when it's read by myVal = Range("A1").Value.

If you want to retain the string, just use Text in place of Value, and define the storage variable as a string. Like so
Sub test()
Dim myVal as String
myVal = Range("A1").Text
Range("A2").Value = myVal
End Sub
Text is read only, so still using Value to write to the new cell. Docs which include an example for the difference between Value and Text: https://msdn.microsoft.com/en-us/library/office/ff840217.aspx

Related

If condition not met using Or keyword

This is my first time asking in Stack Overflow :)
I have a program that lets the user pick a date in a combobox and display it on the label. The other parts of the code for that is fine but I cant make exception for months with special days (like february which only has 29 days in leap years).
I tried using the If/Else statement so that when the users click the years 2016,2012,2008 and 2004 and february it will display on the combobox named "day" 1 to 29 and if the users click february but not the specified dates it will display 1 to 28:
If month.SelectedItem = "Feb" And year.SelectedItem = "2016" Or "2012" Or "2008" Or "2004" Then
Dim jkl As Integer
For jkl = 1 To 29
day.Items.Add(jkl)
Next
ElseIf month.SelectedItem = "Feb" Then
Dim poi As Integer
For poi = 1 To 28
day.Items.Add("poi")
Next
End If
But unfortunately when i debug it, when i select dates other than the specified in the first If statement the combo box named day only displays 29 instead of 28. I tried changing the order of the conditions, changing the separator of the numbers to "&" but it is still the same.
I hope someone gets to the bottom of this. I feel like its in my structure that is wrong but even if I change the order everytime and it is still displaying the same bug.
I tried searching here but I cant find one like my condition even remotely similar.
Turn Option Strict On, it will help you detect these errors right away. VB does not handle If statement like this, what you wrote doesn't do what you think it does. You need to specify both side every time.
If month.SelectedItem = "Feb" And (year.SelectedItem = "2016" Or year.SelectedItem = "2012" Or year.SelectedItem = "2008" Or year.SelectedItem = "2004") Then
As for your logic, there are formula to detect leap day. It might be better to use it instead.
Lastly, near the end, you add the string and not the variable value.
day.Items.Add(poi)
I think you should use here the DateTime functions:
DateTime.DaysInMonth(year, month)
Docs
Dim dateString As String = String.Format("{0} 1 2001", month.SelectedItem)
Dim dDate As Date = DateTime.Parse(dateString)
Dim numberOfMonth As Integer = DateAndTime.Month(dDate)
For jkl = 1 To DateTime.DaysInMonth(numberOfMonth, year.SelectedItem)
day.Items.Add(jkl)
Next
Personally, i would have made a nested If
if month.SelectedItem = "Feb" then
if year.SelectedItem = "2016" Or "2012" Or "2008" Or "2004" Then
Dim jkl As Integer
For jkl = 1 To 29
day.Items.Add(jkl)
Next
else
Dim poi As Integer
For poi = 1 To 28
day.Items.Add("poi")
Next
end if

Opening a new worksheet with the NEXT date

so I need a macro code for naming a worksheet with the next date on it, as in if the previous sheet is called Tue 27 then the next sheet (new one) should be Wed 28,
my current code only names it as TODAYS date, this is what I am using
Dim szTodayDate As String
szTodayDate = Format(Date, "ddd") & Format(Date, " dd")
On Error GoTo MakeSheet
Sheets(szTodayDate).Activate
Exit Sub
MakeSheet:
Dim Srt As Worksheet
Set Srt = ActiveSheet
Sheets.Add
ActiveSheet.Name = szTodayDate
Is it even possible to do this, and if so, can anyone please tell me how,
Thank you
additional note: so the macro creates a new sheet everytime I run it, and then names it with todays date, I need it instead to name it with the NEXT date, in relation to the previous sheet. so if the last sheet made (prior to macro run) is called "Sun 02" the macro should create a sheet and name it "Mon 03", assume for now that the month doesn't matter, I will not run this macro after the month ends, so on workbook Feb, "Wed 28" would be the last time I run this macro.
Reason Explained: so I need to create a new worksheet everyday for work, but I sometimes end up having to make the worksheet a day later, so lets say on sun 02 I make the sheet on time, so now I have worksheet sun 02, but then I miss it on monday, then on tuesday I make the sheet, it ends up making Sheet Tue 04, so now I'm missing Mon 03.
Possible Alternate: If I could somehow set an IF function that can check to see if worksheet with yesterdays name exists (maybe going back upto 2 days) and if not create it, that would work to. but not sure how to code said IF function either (It would also need to create it and name it as today, if today is 01).
Thank you again
An important note for the following code: in its default state it requires that the Sheet Names include the Month, so the format is "ddd dd mmm" ("Wed 28 Feb") not "ddd dd" ("Wed 28"). If, for example, the Month is stored in the FileName instead then you will need to modify the code.
We need a variable to store the most recent date found, and a Worksheet object to use in a For Each loop. We will check each Worksheet's Name and check if it IsDate. If it is, then we will see if it is later than our currently stored Date. This will fail at year end, unless you start including the Year in the Sheet Name (or File Name) too. (Since 1 Jan comes before 31 Dec)
Dim dMaxDate AS Date, wsForLoop AS Worksheet, sTestString AS String
dMaxDate = DateSerial(1900,1,0) 'Default to 0
For Each wsForLoop In ThisWorkbook.Worksheets 'Loop through every worksheet
sTestString = wsForLoop.Name 'Get the name of the sheet
If InStr(sTestString, " ") > 0 Then sTestString = Mid(sTestString, 1+InStr(sTestString, " ")) 'Remove the Weekday
'If you need to add the Month/Year from your filename, do that here
If IsDate(sTestString) Then 'Only check Worksheets with Dates for Names
If cDate(sTestString) > dMaxDate Then dMaxDate = cDate(sTestString) 'If this is a later date then store it
End If
Next wsForLoop 'Return to start of loop
If dMaxDate < DateSerial(1900, 1, 1) Then dMaxDate = Now-1 'If we have no Worksheets with Dates for Names then default to yesterday
With ThisWorkbook.Worksheets.Add 'Add a new sheet
'Change the Format if you are adding the Month and/or Year from Filename
.Name = Format(dMaxDate+1, "ddd d mmm") 'And Name it the day after our stored date
End With
try using below, you can use counter instead of 1.
SZTodayDate = Format(Date + 1, "ddd") & Format(Date + 1, " dd")

Filter for Dates which are in Caption in PivotTables which come from Olap cube

I want to Filter Dates which stands between two Dates within a Olap Cube.
My code so far:
Set pvtField = pt.CubeFields("[DimTime].[Year-Quarter-Month-Day]")
' Add item to the Report Filter
pvtField.CreatePivotFields
pvtField.Orientation = xlRowField
With ActiveSheet.PivotTables(1).PivotFields("[DimTime].[Year-Quarter-Month-Day].[Day]")
Debug.Print end_date 'return Date in the format 'dd.mm.yyyy', it is a valid Date
Debug.Print start_date 'return Date in the format 'dd.mm.yyyy', it is a valid Date
Debug.Print .PivotItems(1).Caption 'returns e.g. Monday, September 04 2006, this is just returned, if i click on the plus sign in the pivot table(see attached picture)
Debug.Print .PivotItems(1).Value 'returns e.g. [DimTime].[Year-Quarter-Month-Day].[Day].&[2006-09-04T00:00:00]
Debug.Print .Name 'returns e.g. [DimTime].[Year-Quarter-Month-Day].[Day]
.ClearAllFilters 'Clear All The Filters
.CubeField.IncludeNewItemsInFilter = True
.PivotFilters.Add2 Type:=xlCaptionIsBetween, Value1:=start_date, Value2:=end_date 'getting here the error
'.PivotFilters.Add2 Type:=xlDateBetween, Value1:=start_date, Value2:=end_date 'getting here the same error
End With
The error Message is:
Runtime Error 438: Object does not support property or method.
Manually opened plus signs (referenced in code):
Update
Ok, what i fount out so far:
With:
For Each i In .PivotItems
Debug.Print i.Caption 'it prints e.g. Monday, September 04 2017
Next i
I cant use .IsDate on this Field or the items, so it is not a date?
If I Format it my start_date to this Format i get Montag, September 04 2017 (the german version, because i am using a german pc). Is this relevant for my problem?
Update2:
i have tried:
.PivotFilters.Add Type:=xlCaptionIsGreaterThan, Value1:=Format(start_date, "dddd, mmmm dd yyyy") 'getting here the error
and the result is, that just Wednesdays are selected with start_date = Wednesday, August 23 2017 ( i have changed my System settings from German to English)
Resulting Question--> Can i convert my PivotField to a Date?
Update3
I converted the Olap Date with the following for loop and getting now a new error:
1004:The date you entered is not a valid date. Please try again.
For Each i In .PivotItems
Debug.Print i.Caption
'Debug.Print CDate(CStr(i.Caption))
p = i.Caption
u = Split(p, ",")(1)
Debug.Print CDate(u) 'e.g. 04/09/2017
i.Caption = CDate(u)
Next i
'Debug.Print .PivotItems(1).IsDate
.PivotFilters.Add Type:=xlDateBetween, Value1:=start_date, Value2:=end_date 'getting here the error
Also tried that with Cdbl in CDate(u) and start_date/end_date and getting this error message:
5: Invalid procedure call or argument.
Fire up the macro recorder, set the DateBetween filter, then take a look at the code it recorded. Then compare that code to what you are doing above, and you'll probably be able to find where you went wrong. When I did it, I got this:
ActiveSheet.PivotTables("PivotTable3").PivotFields("[Range].[Date].[Date]"). _
PivotFilters.Add2 Type:=xlDateBetween, Value1:="1/02/2017", Value2:= _
"1/08/2017"
Note that the macro recorder didn't care that the dates were formatted in dddd, mmmm dd yyyy format...it just used the d/mm/yyyy format appropriate for my location (New Zealand). That's probably where you are going wrong.

VBA - How to remove string of characters in a range cell

This is the value of my date Mar 30 2016 4:46:34:256PM i want to remove the Time to make it like this Mar 30 2016.
I've tried various ways to format this if value with the format like mm-dd-yyyy , mm-dd-yyyy , yyyy-mm-dd etc. it's working but if tried this Mar 30 2016 4:46:34:256PM as value it's not working. can someone please tell me why?
I tried a simple code to test all the formats but it's not working with this value Mar 30 2016 4:46:34:256PM so i decided to remove the 4:46:34:256PM and that's how i got stock....
Sub formateDate()
Dim lastrow As Long
lastrow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To lastrow
Cells(i, 2).NumberFormat = ("dd-mm-yyyy")
Next i
End Sub
from your example you seem to have two consecutive spaces after the year number
should it always be so you could go like follows
Cells(i, 2) = Left(Cells(i, 2), InStr(Cells(i, 2), " ") - 1)
and then you can also assign it date formats since IsDate WorksheetFunction returns True if called on the resulting values

Excel VBA - Extract the correct dates from badly formatted dates?

I am currently learning VBA programming by doing, and have encountered the below situation with which I would appreciate your help. Ideally not just in finding a solution, but also to understand how and why the solution works.
Say that there is a database from which one can export a spreadsheet of data. One of the columns has date values, but they are badly formatted from the export. The system sends the dates as mm/dd/yyyy hh:mm AM/PM, for example, 04/11/2014 09:24 AM, but the spreadsheet has this identified as dd/mm/..., meaning it enters 04 as the day and 11 as the month.
Within this column, if the day is before or including 12 of the month, the cell is formatted as date. If the day is past the 12th, the cell is formatted with a general format.
My question is, could I write a VBA macro that could reverse the values for day and month and create the correct dates in a new column? I would think that it would first have to identify if a cell is formatted as date, and then somehow extract the date and month in the correct positions, or if it's formatted as a general format, and then use a different code to extract the correct date.
If this is too basic an issue for this community and there's another community more suited, I will gladly repost my question there.
EDIT:
After my comment below I played around with functions and looked for other similar functions that may help do what I need, switch the day value with the month value, and so far I have:
'for dates with general format: 04/14/2014 11:20 AM
=DATE(MID(A1,7,4),LEFT(A1,2),MID(A1,4,2)) 'in a column for the date
=TIME(MID(A1,12,2),MID(A1,15,2),"00") 'in a column for time, since I may need this
'for dates with a date format: 4/11/2014 7:35:00 PM
=DATE(TEXT(A1,"yyyy"),TEXT(A1,"dd"),TEXT(A1,"mm")) 'in a column for the date
=TEXT(A1,"hh:mm AM/PM") 'in a column for time
Now I just need to figure out a conditional function to identify when to apply each set of formulas according to the values or formatting or column A.
But are there equivalent functions to achieve this through VBA? I need these date and time columns to only hold values, not formulas, so that I may export the data out of them directly. And somehow putting this in VBA code seems more "clean" to me, using formulas feels to me like a volatile solution. I'm not sure how to explain this properly, but I'm somehow more confortable with proper coding behind my data manipulation.
EDIT2:
I've resolved the worksheet functions solution as below. It took me a while to figure out how to go around the FIND error with date formatted cells, and only found the IFERROR function by chance in the list Excel suggests when writing =IF.
'to get the correct date
=IF(IFERROR(FIND("/",A1),0)>0,DATE(MID(A1,7,4),LEFT(A1,2),MID(A1,4,2)),DATE(TEXT(A1,"yyyy"),TEXT(A1,"dd"),TEXT(A1,"mm")))
'to get the correct time
=IF(IFERROR(FIND("/",A1),0)>0,TIME(MID(A1,12,2),MID(A1,15,2),"00"),TEXT(A1,"h:mm AM/PM"))
Now at least I have a working solution, but I'm still interested in a VBA translation for these formulas and will continue searching for these.
Check this out. Let's take for example your formula:
=IF(IFERROR(FIND("/",A1),0)>0,DATE(MID(A1,7,4),LEFT(A1,2),MID(A1,4,2)),DATE(TEXT(A1,"yyyy"),TEXT(A1,"dd"),TEXT(A1,"mm")))
VBA equivalent functions:
Find = Instr
Date = DateSerial
Text = Format (not exactly the same but the nearest)
Code equivalent:
Dim mydate As Date
Dim myformat As String
myformat = "mm/dd/yyyy hh:mm AM/PM"
If InStr(1, [A1], "/") > 0 Then
mydate = DateSerial(Mid(Format([A1], myformat), 7, 4), _
Left(Format([A1], myformat), 2), Mid(Format([A1], myformat), 4, 2))
Else
mydate = DateSerial(Year([A1]), Month([A1]), Day([A1]))
End If
[B1] = mydate
Take note that [A1] is a shortcut Evaluate function which can also be written as Evaluate("A1").
I used that to refer to Cell A1 as in your formula. You can use the conventional Range Object reference like this: Range("A1"). I used the shortcut because it looks cleaner. But it is not advisable in huge data sets.
For your time formula:
=IF(IFERROR(FIND("/",A1),0)>0,TIME(MID(A1,12,2),MID(A1,15,2),"00"),TEXT(A1,"h:mm AM/PM"))
Code Equivalent:
Dim mytime As Date
If InStr(1, [A1], "/") > 0 Then
mytime = TimeValue([A1])
Else
'~~> myformat is declared above
mytime = TimeValue(Format([A1], myformat))
End If
[C1] = mytime
You can also check the format of the cell like below:
Select Case True
Case [A1].NumberFormat = "General"
mydate = DateSerial(Year([A1]), Month([A1]), Day([A1]))
mytime = TimeValue(Format([A1], myformat))
Case [A1].NumberFormat = myformat '~~> again this is declared above
mydate = DateSerial(Mid(Format([A1], myformat), 7, 4), _
Left(Format([A1], myformat), 2), Mid(Format([A1], myformat), 4, 2))
mytime = TimeValue([A1])
Case Else
MsgBox "Invalid Format. Cannot be evaluated"
End Select
[B1] = mydate: [C1] = mytime
Not sure if above will really solve your problem.
There are just many possibilities when you extract datetime stamp from a database.
If the scenarios you mentioned are only the problems you encounter, then above solutions might work.
This is now an old thread but in case anyone else stumbles upon it (as I did) with a similar problem, I'm just offering this up.
My suggested VBA function for this is shown below. Its style doesn't strictly follow purist programming practice (declaration of variables, etc); it's written, rather, to be relatively easily comprehensible.
Function Date_Text_Convert( _
date_text As String, _
return_with_month_letters As Boolean, _
return_as_date_time_value As Boolean)
' Patrick S., June 2018
' Intention: to enable mm/dd/yyyy[etc] imported text-string dates
' to be switched to dd/mm/yyyy[etc]. Can be adapted for other cases.
' Usage examples: if cell A2 contains the text-string:
' 06/26/2018 09:24 AM
' then in, for example, cell B2, type:
' =Date_Text_Convert(A2,TRUE,FALSE) or =Date_Text_Convert(A2,FALSE,FALSE)
' which returns:
' 26-Jun-2018 09:24 am or 26/06/2018 09:24 am
' To return a date-and-time value instead of a string, use, for example:
' =Date_Text_Convert(A2,TRUE,TRUE)
' establish the positions where the day and month digits start
daypos = 4
mthpos = 1
rempos = 7 ' starting position of remaining part of the string
' establish the length of the relevant text sections: 2 characters each, in this case
daylen = 2
mthlen = 2
' so that,
daytext = Mid(date_text, daypos, daylen)
mthtext = Mid(date_text, mthpos, mthlen)
remtext = Mid(date_text, rempos, 999) ' the remainder of the text string
' format the output according to 'return_with_month_letters'
' there are 2 options available, each using a different separator
sep_stroke = "/"
sep_hyphen = "-"
If return_with_month_letters = True Then
mthnum = mthtext * 1
mthindex = ((mthnum - 1) * 3) + 1
mthname = Mid("JanFebMarAprMayJunJulAugSepOctNovDec", mthindex, 3)
newtext = daytext & sep_hyphen & mthname & sep_hyphen & LCase(remtext) ' LCase optional
Else
newtext = daytext & sep_stroke & mthtext & sep_stroke & UCase(remtext) ' UCase optional
End If
' finally, return the output through the function name: either as a date, or as the text equivalent
If return_as_date_time_value = True Then
newdate = DateValue(newtext) + TimeValue(newtext)
Date_Text_Convert = newdate
Else
Date_Text_Convert = newtext
End If
End Function