Checking that cell value is date time (ISO) - vba

Want to check if the cell is a date time value using the ISO format i.e.
2012-04-12T00:00:00
Current try:
If mainsht.Cells(r, 6).Value = "" Or mainsht.Cells(r, 6).Value = "####-##-##T##:##:## Then
GoTo next6
Still it does not seem to match the format in vba and cell value, as I have many cells with this correct format and still activating the else statement i.e. not recognized by the "####-##-##T##:##:##".
Maybe yyyy-mm-ddThh-MM-ss?

ISO date come in several formats, adding an asterisk "####-##-##T##:##:##*" would be more versatile.
2011-01-01T12:00:00Z
2011-01-01T12:00:00+05:00
2011-01-01T12:00:00-05:00
2011-01-01T12:00:00.05381+05:00
Example:
If mainsht.Cells(r, 6).Value = "" Or mainsht.Cells(r, 6).Value Like "####-##-##T##:##:##*" Then
You might want to look at this post: Parsing an ISO8601 date/time (including TimeZone) in Excel

The following UDF¹ can be used as a worksheet function or a helper function in a VBA project.
Option Explicit
Function IsISODateTime(str As String)
Dim n As Long, nums() As Variant
Static rgx As Object, cmat As Object
'with rgx as static, it only has to be created once; beneficial with repeated calls to the UDF
If rgx Is Nothing Then
Set rgx = CreateObject("VBScript.RegExp")
End If
IsISODateTime = vbNullString
With rgx
.Global = False
.MultiLine = False
.Pattern = "[0-9]{4}\-[0-9]{2}\-[0-9]{2}[A-Z]{1}[0-9]{2}\:[0-9]{2}\:[0-9]{2}"
IsISODateTime = .Test(str)
End With
End Function
The UDF returns a true boolean True/False.
The pattern I've provided is very brick-by-brick literal; it could be shortened using the methods detailed in How to use Regular Expressions (Regex) in Microsoft Excel both in-cell and loops.
¹ A User Defined Function (aka UDF) is placed into a standard module code sheet. Tap Alt+F11 and when the VBE opens, immediately use the pull-down menus to Insert ► Module (Alt+I,M). Paste the function code into the new module code sheet titled something like Book1 - Module1 (Code). Tap Alt+Q to return to your worksheet(s).

As already pointed out by Tim Williams do it like so
If mainsht.Cells(r, 6).Value = "" Or mainsht.Cells(r, 6).Value Like "####-##-##T##:##:##" ...

To test the cell for an ISO formatted date you should check the cells NumberFormat property, so your If statement should be:
If mainsht.Cells(r, 6).Value = "" Or mainsht.Cells(r, 6).NumberFormat Like "yyyy-mm-ddThh:mm:ss*" Then
Note: If the currently accepted solution is working, your cell only contains a string value (which looks like an ISO formatted date) rather than an actual date displayed using an ISO date format.

Related

VBA Excel - run string variable as a line of code

In the aim to allow users from different countries to use my application, I would like to initialize a translation of each object in each existing userform (labels,commandbuttons,msgbox,frames, etc...) at the start of the application.
I'll write all the translation in my Languages sheet:
I've already made a first userform where the user types his login, password and selects his language.
After this step, the main userform called "Menu" will be launched.
I've already tried to type a piece of code (here below) to find the line of code, in a msgbox that I want to run (example : menu.commandbutton1.caption="Envoyer email")
Private Sub UserForm_Initialize()
' Definition of language selected during login
Set langue = Sheets("Languages").Cells.Find("chosen",
lookat:=xlWhole).Offset(-1, 0)
' Initialisation of the texts in the selected language
Dim cel As Range
Dim action As String
For Each cel In Sheets("Languages").Range("d3:d999")
If cel <> "" Then
action = cel & "=" & """" & cel.Offset(0, -2) & """"
MsgBox (action)
End If
Next cel
End Sub
I've already read some topics about this subject but those does not correspond exactly to what i would like to do.
If you have a solution, or a work around, it would be very helpful.
If you simply want different MsgBox, based on a coutry, this is probably the easiest way to achieve it. Imagine your file is like this:
Then something as easy as this would allow you to use different strings, based on the country:
Public Sub TestMe()
Dim country As String
Dim language As Long
country = "Bulgaria" 'or write "England" to see the difference
language = WorksheetFunction.Match(country, Range("A1:B1"), 0)
MsgBox (Cells(2, language))
MsgBox "The capital of " & country & " is " & (Cells(3, language))
End Sub
The idea of the whole trick is simply to pass the correct column, which is done through WorksheetFunction.Match.
Taken from an old CR post I have here, this solution pretty much mimicks .NET .resx resource files, and you can easily see how to extend it to other languages, and if I were to write it today I'd probably use Index+Match lookups instead of that rather inefficient loop - but anyway it works nicely:
Resources standard module
Option Explicit
Public Enum Culture
EN_US = 1033
EN_UK = 2057
EN_CA = 4105
FR_FR = 1036
FR_CA = 3084
End Enum
Private resourceSheet As Worksheet
Public Sub Initialize()
Dim languageCode As String
Select Case Application.LanguageSettings.LanguageID(msoLanguageIDUI)
Case Culture.EN_CA, Culture.EN_UK, Culture.EN_US:
languageCode = "EN"
Case Culture.FR_CA, Culture.FR_FR:
languageCode = "FR"
Case Else:
languageCode = "EN"
End Select
Set resourceSheet = Worksheets("Resources." & languageCode)
End Sub
Public Function GetResourceString(ByVal resourceName As String) As String
Dim resxTable As ListObject
If resourceSheet Is Nothing Then Initialize
Set resxTable = resourceSheet.ListObjects(1)
Dim i As Long
For i = 1 To resxTable.ListRows.Count
Dim lookup As String
lookup = resxTable.Range(i + 1, 1)
If lookup = resourceName Then
GetResourceString = resxTable.Range(i + 1, 2)
Exit Function
End If
Next
End Function
The idea is, similar to .NET .resx files, to have one worksheet per language, named e.g. Resources.EN and Resources.FR.
Each sheet contains a single ListObject / "table", and can (should) be hidden. The columns are basically Key and Value, so your data would look like this on sheet Resources.EN:
Key Value
menu.caption Menu
menu.commandbutton1.caption Send email
menu.commandbutton1.controltiptext Click to send the document
And the Resources.FR sheet would have a similar table, with identical keys and language-specific values.
I'd warmly recommend to use more descriptive names though; e.g. instead of menu.commandbutton1.caption, I'd call it SendMailButtonText, and instead of menu.commandbutton1.controltiptext, I'd call it SendMailButtonTooltip. And if your button is actually named CommandButton1, go ahead and name it SendMailButton - and thank yourself later.
Your code can then "localize" your UI like this:
SendMailButton.Caption = GetResourceString("SendMailButtonText")
The Resources.Initialize procedure takes care of knowing which resource sheet to use, based on Application.LanguageSettings.LanguageID(msoLanguageIDUI) - and falls back to EN, so if a user has an unsupported language, you're still showing something.

Excel VBA Extract Text from a Cell

I have tried looking through Stack Overflow for previous suggestions but haven't found any that have worked.
Here is my situation: I am trying to look at a simple Excel sheet which shows someone's name, position, and then their "Role" which is a custom field I am creating. Right now, I am looking to just do "Engineers" but will also expand to things like "Admin Assistant" and "Manager". (The real spreadsheet is about 8100 lines long).
Here is an example of some test data:
All I need is to scan through the "Title" column, see if it matches a String (in this case, my test string is engineer), and then to copy the String and the remaining I or II or III or in some cases, IV after it.
I have heard about using a regular expression and have used them in SQL before, but am struggling coming up with what I need. Here is my current code where I tried using the MID function:
Sub GetRole()
' Custom function written to take role out of official title
strRole = "Engineer" ' String to check for
Dim lrow As Integer ' Number of Rows
Dim Role As String ' Role to write into adjacent cell
lrow = Cells(Rows.Count, "B").End(xlUp).Row
For i = lrow To 2 Step -1
If InStr(1, Cells(i, 2), "Engineer") > 0 Then
Role = Mid(Cells(i,3)), 1, 5)
Cells.(i, 3).Value = Role
End If
Next i
End Sub
But that didn't quite work. Any help or advice would be greatly appreciated. I am willing to provide any extra information necessary.
You can solve this using Regular Expressions. First you need to enable the reference which you do so by going to Tools > References... and enable Microsoft VBScript Regular Expressions 5.5
Then use the following code to generate your answers
Sub GetRole()
' Custom function written to take role out of official title
' Uncomment the below if using Early Binding i.e. you enable the reference
' Dim ReGex As New RegExp
' Comment below line if decide to use Late Binding (i.e. you enable the reference)
Dim ReGex As Object
Dim i As Long, lrow As Long ' Number of Rows
' Comment the below line if decide to use Late Binding (i.e. you enable the reference)
Set ReGex = CreateObject("VBScript.RegExp")
With ReGex
.Global = True
.IgnoreCase = True
.Pattern = "(Engineer\sI*\b)"
End With
With ActiveSheet
lrow = .Cells(.Rows.Count, "B").End(xlUp).Row
For i = lrow To 2 Step -1
If ReGex.test(.Cells(i, 2).Value2) Then .Cells(i, 3).Value2 = Trim(ReGex.Execute(Cells(i, 2).Value2)(0))
Next i
End With
End Sub
Generates output:
I think Excel formula will be easier to extend compared to debugging VBA and Regex:
=IF(ISNUMBER( FIND("Engineer III", E4)), "Engineer III",
IF(ISNUMBER( FIND("Engineer II" , E4)), "Engineer II",
IF(ISNUMBER(SEARCH("Engineer *I" , E4)), "Engineer I", "")))

FormulaR1C1 doesn't work with SUMME

The FormulaR1C1 Method doesn't work as it's supposed to.
objExcel = CreateObject("Excel.Application")
objWorkbook = objExcel.Workbooks.Add
Dim Formula As String
Formula = "=SUMME(Z1S1;Z2S1)"
For i = 1 To 5 Step 1
objWorkbook.Worksheets("Tabelle1").Cells(i, 1).FormulaR1C1 = 5
objWorkbook.Worksheets("Tabelle1").Cells(i, 5).FormulaR1C1 = "Text"
objWorkbook.Worksheets("Tabelle1").Cells(i, 3).FormulaR1C1 = Formula
Next
This should give me 10 in the third column 5 times, but it doesn't. I even tried it with:
Formula = "=SUMME(Z[0]S[-2];Z[1]S[-2])"
but that doesn't work either. The Loop just breaks when coming to the assigment line :( . If I try it with
Formula = "=SUMME(Z1S1,Z2S1)"
It executes completely but it doesn't work for excel because then it says =SUMME('Z1S1';'Z2S1') in the Excel Field
You have to use the Range.FormulaLocal property or Range.FormulaR1C1Local property if you plan to use a string that contains non-EN-US function. VB.Net expects a EN-US formula and will translate that to the regional language of the worksheet.
Formula = "=SUM(A1, B2)"
'or depending on your requiements,
Formula = "=SUM(A1:B2)"
.Formula = Formula
Formula = "=SUM(R1C1, R2C2)"
'or depending on your requiements,
Formula = "=SUM(R1C1:R2C2)"
.FormulaR1C1 = Formula
Formula = "=SUMME(Z1S1;Z2S1)"
'or depending on your requirements,
Formula = "=SUMME(Z1S1:Z2S1)"
.FormulaLocalR1C1 = Formula
Don't confuse xlA1 cell references with xlR1C1 references. R23 would mean Cells(23, 18) in xlA1 and 23:23 in xlR1C1.
Note that using the Range.Formula property or Range.FormulaR1C1 property enforces the use of a comma (EN-US standard) instead of a semicolon as a system list separator.
Range.Formula property
Range.FormulaR1C1 property
Range.FormulaLocal property
Range.FormulaR1C1Local property

Confusing dd/mm/yyyy and mm/dd/yyyy

I have a VBA script that opens up a bunch of CSV files, and compiles them into one summary report.
However, I'm having a problem where it reads in UK style dates (dd/mm/yyyy), then interprets them as US-style dates when it makes the copy, before display them as UK-style dates again!
So 4th of July in original sheet becomes 7th of April in the summary sheet - verified by changing cell format to display month name.
This is odd, as when you open up the CSV file in Excel, it correctly interprets the UK style date.
Copy is made using code like this
SummarySheet.Cells(Y,X).value = CSVSheet.Cells(W,Z).value
What is going on here?
You did not post the code as to how you are opening your CSV files -- that is the critical area. The dates need to be parsed properly BEFORE being entered on the worksheet. The following code will selects and then opens a file that has UK style dates in a single column, and properly parse them. You will need to adapt it to your particular requirements.
The FieldInfo argument is what does the work. The formatting of the Excel worksheet is "for show" so you can see an unambiguous date.
Option Explicit
Sub OpenUKcsv()
Dim sFile As String
Dim WB As Workbook
Dim WS As Worksheet
sFile = Application.GetOpenFilename()
Workbooks.OpenText Filename:=sFile, DataType:=xlDelimited, comma:=True, other:=False, _
fieldinfo:=Array(1, 4)
Set WB = ActiveWorkbook
Set WS = ActiveSheet
With WS.Columns(1)
.NumberFormat = "dd-mmm-yyyy"
.EntireColumn.AutoFit
End With
End Sub
You could use .Text (text displayed in Excel cell) or .Value2 (value without formatting) instead of .Value (value with formatting).
But I strongly suggest that you set the format of the cells that you use to what you expect to have at the end with .NumberFormat = "mm/dd/yyyy"
Or you could use CDate function :
SummarySheet.Cells(Y,X).value = CDate(CSVSheet.Cells(W,Z).value)
Or use an UDF with DateSerial :
Sub test_CMaster()
MsgBox ParseDate("4/7/15") & vbCrLf & CDate("4/7/15")
End Sub
Function ParseDate(ByVal DateInCell As String, Optional Separator As String = "/") As Date
Dim D() As String
D = Split(DateInCell, Separator)
ParseDate = DateSerial(D(UBound(D)), D(1), D(0))
End Function
Try using the Workbooks.OpenText() method instead and set the Local flag to True
Set csvWB = Workbooks.OpenText(Filename:=myCSVfile, Local:=True)
Here is the MSDN article on this method which says for the Local setting:
Specify True if regional settings of the machine should be used for separators, numbers and data formatting.
Maybe you can convert the CSV files to show dates as numbers, ie. 10th Nov 15 will show as 42318. Or add a separate column where B1 is =DATEVALUE(A1) and work with that.
When you create the summary report, import the numbers and convert them to date using CDate and Format. Something like this:
Sub test()
Range("A2:A4").NumberFormat = "m/d/yyyy"
Range("A2").Value = Format(CDate(Range("A1").Value), "dd.mm.yyyy")
Range("A3").Value = Format(CDate(Range("A1").Value), "mm.dd.yyyy")
Range("A4").Value = Format(CDate(Range("A1").Value), "Long Date")
End Sub
EDIT:
For better formatting (no need for NumberFormat, I think it will use your regional settings right away) and auto-setting the cell format to date-type, use this:
Sub test()
Dim sDate As Date
sDate = CDate(Range("A1").Value)
Range("A2").Value = DateSerial(Year(sDate), Month(sDate), Day(sDate))
End Sub
Result:
References:
http://www.techonthenet.com/excel/formulas/format_date.php
http://www.techonthenet.com/excel/formulas/cdate.php

Date lookup issue: Unable to get VLookup property of WorksheetFunction class

I've tried almost everything in most of the other similar type questions but I can't seem to solve the runtime error. Help please!
What I want to achieve:
1) My macro is supposed to get date from report summary files that are created every day hence, it requires the user to input which date of report he wants the data from
2) I use the vlookup method to get the data from the relevant row and input it into the central workbook with the macro
3) Every part of the code works except using the date to Vlookup and it will give me this error message
4) I have tested the code using other text based lookup values and the whole macro works (i.e. i looked up the row which has the "Total" value so it looks up "Total" but i require the macro to look up the rows with the date as the look up value)
Addtional Info:
1) In the lookup file, the dates are in the format of "m/d/yyyy" but presented in the format of "dd-mmm-yy" (but i've tried both and they dont work)
Sub GetData()
Dim strDate As Date
strDate = InputBox("Input date of report to retrieve (Format: DD-MM-YYYY)", "Input Date", Format(Now(), "DD-MM-YYYY"))
If IsDate(strDate) Then
'there is some code here not relevant but basically i need to keep manipulating the date throughout the code
With ActiveSheet
Dim XstrDate
Dim Xfile As String
XstrDate = Format(strDate, "mmm DD, YYYY")
Xfile = "C:\...\...\...\Report " & XstrDate & ".xls"
Dim wb As Workbook
Dim ws As Worksheet
Dim rng As Range
Dim returnValue as Variant
Set wb = Application.Workbooks.Open(Xfile)
Set ws = wb.Worksheets("Summary")
Set rng = ws.Range("A:K")
Dim Xdate As String
Xdate = Format(XstrDate, "m/d/yyyy")
returnValue = Application.WorksheetFunction.VLookup(Xdate, rng, 2, 0)
'... more code
remember, i've tried looking up using the text in the same column and it returned me a value. So i suspect the problem lies with the date format or something
Any smart and kind soul want to offer some suggestions here:)
Excel internally stores dates as a Serial Number (e.g. 1/1/2014 = 41640), which you can observe yourself if you enter a date into a cell and then change the format to Number.
With this in mind it's unlikely that a VBA date and an Excel date can be matched using the VLookup function so in my experience the best solution is to convert your date into its serial number and then perform the VLookup on that value instead.
Dim Ndate As Long
Dim returnValue As Date
Ndate = DateSerial(<Year>, <Month>, <Day>)
returnValue = Application.WorksheetFunction.VLookup(Ndate, <rng>, <col>, False)
If you need to construct your DateSerial(...) function from a Date variable in VBA you can use the Year(<date>), Month(<date>), and Day(<date>) functions to break it down into the required components.
Note: I've tried this example in the format .VLookup(DateSerial(2014,1,1),...) and it still causes the same error, hence storing the return value of DateSerial in a numeric variable first.
Happy Coding!
I've taken a different approach and found another solution to this problem.
Rather than use Vlookup, this is the other method that bypass the problem of VLookup date format problem, (having defined vdate in previous statements)
Dim rnge As Range, i As Long
Sheets("Summary").Select
Columns("A:A").Select
Set rnge = Intersect(Selection, ActiveSheet.UsedRange)
If rnge Is Nothing Then
MsgBox "Date Not Found"
GoTo done
End If
For i = rnge.Count To 1 Step -1
If rnge.Cells(i).Value = vdate Then rnge.Cells(i).EntireRow.Copy _
Destination:=ThisWorkbook.Sheets("AnotherSheet").Range("A1")
Next
done:
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True