How to remove text before a certain character? - vba

Let's say I have the following values:
1 min 7 sec
23 sec
6 hours 10 min 14 sec
I want to return:
7 sec
23 sec
14 sec
Is there a way to do this in VBA?
Sec, Min, or Hours may not always be present if a value for it is not used.
I.e. if there are 60 seconds, it will just say 1 min. If there are 60 minutes exactly, it will just say 1 hour.

Here is a method using regular expressions. It looks for a number-space-sec and returns a hyphen if not found. Amend to suit.
Code amended to incorporate #Mathieu Guindon's suggestion. + looks for one or more instances, * for zero or more.
Function Regex1(v As Variant) As String
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "\d+\s*sec"
If .Test(v) Then
Regex1 = .Execute(v)(0)
Else
Regex1 = "-"
End If
End With
End Function

Function POP(strInput As String) As String
Dim a() As String
a = Split(strInput, " ")
If UBound(a) > 0 Then
POP = a(UBound(a) - 1) & " " & a(UBound(a))
Else
POP = a(UBound(a))
End If
Erase a
End Function

Related

VBA convert unusual string to Date

I wanted to scrape data from yahoo as an excercise and then make a graph from it. I encountered a problem where when I scrape the dates, they are in a rather weird format:
?10? ?Aug?, ?2020
The question marks in the string are not realy question marks, they are some characters unknown to me, so I cannot remove them with Replace().
Then, when I try to use CDate() to convert this to Date format, the code crashed on "Type mismatch" error.
What I would need is to either find a way to find out what those characters are in order to remove them with Replace(), or to somehow convert even this weird format to a Date.
Alternatively, somehow improving the scraping procedure - so far I've been using for example
ie.document.getElementsByClassName("Py(10px) Ta(start) Pend(10px)")(3).innerText
to get the data - would also solve this problem.
If anyone wanted to try to scrape it, too an example url:
https://finance.yahoo.com/quote/LAC/history?period1=1469404800&period2=1627171200&interval=1d&filter=history&frequency=1d&includeAdjustedClose=true
An example of my code follows:
DateString = doc.getElementsByClassName("Py(10px) Ta(start) Pend(10px)")(j).innerText
LeftDateString = Clean_NonPrintableCharacters(DateString)
Worksheets("Stock_data").Range("A2").Value = CDate(LeftDateString)
With regexp:
Function GetDate(txt)
' set a reference to 'Microsoft VBScript Regular Expression 5.5' in Tools->References VBE menu
Dim re As New RegExp, retval(0 To 2), patterns, i, result
patterns = Array("\b\d\d\b", "\b[a-zA-Z]+\b", "\b\d{4}\b")
For i = 0 To 2
re.Pattern = patterns(i)
Set result = re.Execute(txt)
If result Is Nothing Then Exit Function 'If no day, month or year is found, GetDate() returns ""
retval(i) = result(0)
Next
GetDate = Join(retval)
End Function
Sub Usage()
For Each txt In Array("?10? ?Aug?, ?2020", "Jul 13, 2020", "2021, March?, 18?")
Debug.Print GetDate(txt)
Next
End Sub
Prints:
10 Aug 2020
13 Jul 2020
18 March 2021
Edit 2
Function GetDate2(txt)
' set a reference to 'Microsoft VBScript Regular Expression 5.5' in Tools->References VBE menu
Static re As RegExp, months As Collection
Dim result
If re Is Nothing Then 'do it once
Set re = New RegExp
re.Pattern = "[^a-zA-Z0-9]"
re.Global = True
Set months = New Collection
cnt = 1
For Each m In Split("jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec", ",")
months.Add cnt, m
cnt = cnt + 1
Next
End If
result = Split(WorksheetFunction.Trim(re.Replace(txt, " ")))
For i = 0 To UBound(result)
If Not IsNumeric(result(i)) Then
result(i) = Left(LCase(result(i)), 3)
On Error Resume Next
result(i) = months(result(i))
On Error GoTo 0
End If
Next
result = Join(result)
If IsDate(result) Then GetDate2 = CDate(result)
End Function
Sub Usage2()
For Each txt In Array("?10? ?Aug?, ?2020", "Jul 13, 2020", "2021, March?, 18?", _
"01/12/2021", "04.18.2020", "15 10 20")
Debug.Print GetDate2(txt)
Next
End Sub
Prints:
10.08.2020
13.07.2020
18.03.2021
01.12.2021
18.04.2020
15.10.2020
Note. The order of the dd and mm may be vary
I would use something like so. I've used your ? as question marks for this example, i assumed they were all the same wierd character. This outputs
10 Aug 2020
Sub d()
Dim d As String
d = "?10? ?Aug?, ?2020"
d = Replace(Replace(d, Chr(Asc(Left(d, 1))), vbNullString), ",", vbNullString)
Debug.Print d
End Sub
you could loop though each char in the string and check its ascii values and create your date string from that. Example
Sub GetTheDate(sDate As String)
'97 - 122: lower case Ascii values
Dim i As Integer
Dim strDate As String
'loop through each char
For i = 1 To Len(sDate)
'check to see if it is numeric
If IsNumeric(Mid(sDate, i, 1)) Then
'numeric so add it to the string
strDate = strDate & Mid(sDate, i, 1)
Else
'check to see if it is a char a-z
If Asc(LCase(Mid(sDate, i, 1))) >= 97 And Asc(LCase(Mid(sDate, i, 1))) <= 122 Then
'it is an a char from a-z so add it to string
strDate = strDate & Mid(sDate, i, 1)
Else
'chekc for a space and add a comma - this sets up being able to use cdate()
If Mid(sDate, i, 1) = " " Then
strDate = strDate & ","
End If
End If
End If
Next i
'convert it and print it
Debug.Print CDate(strDate)
End Sub

Substring a Word from a Multiline Textboxes

I want to do the following thing: if I have a Textbox1.Lines:
2
5
15
21
45
and I want to for example get the number left 15 and get the number right 15.
Output: Textbox2.Lines 15 21
5 15
How should I do this? example for the number 2 want substring 2 5
Module Module1
Public Function FirstWords(input As String,
count As Integer) As String
Dim words = count
For i As Integer = 0 To input.Length - 1
' Decrement word count when we reach a space.
If input(i) = " " Then
words -= 1
End If
' When no words remaining, return a substring to this point.
If words = 0 Then
Return input.Substring(0, i)
End If
Next
Return ""
End Function
how should i do this to work?
You can separate them into an array of strings with the Split function, like in this example:
Dim input as String = "2 5 15 21 45"
Dim numbers as String() = input.Split(" ")
For Each s as String In numbers
Console.WriteLine(s)
Next
You just have to figure out how to extract the numbers you want from the lot. Have fun!

Fill combobox with time values

I want fill my combobox with time values like (08:00, 08:10, 08:20 until 09:50) step=10 minutes but the result is like (8:00, 8:10, 8:20, 8:30, 9:-20,9:-10, 9:00, 9:10, 9:20).
My code doesn't show the value like 8:40, 8:50 and he also show negative value like 9:-20, 9:-10).
So please how can I resolve this problem?
Heure_rdv.Items.Clear()
Dim nbr_minute2 As String
For i = 480 To 590 Step 10
Dim nbr_heure As Integer = cint(i / 60)
Dim nbr_minute As Integer = (i - (nbr_heure * 60))
nbr_minute2 = CStr(nbr_minute) + "0"
If ((i - (nbr_heure * 60)) = 0) Then
Heure_rdv.Items.Add(CStr(nbr_heure) + ":" + nbr_minute2)
Else
Heure_rdv.Items.Add(CStr(nbr_heure) + ":" + CStr(nbr_minute))
End If
Next
This is how I should do try it in VBA.
Should be similar in VB.NET
DIM i As Integer 'Counter 1
Dim ii As Integer 'Counter 2
' make 1st loop for hours
for i = 8 To 9
' mkae 2nd loop for minutes
for ii = 0 To 50 Step 10
'
Heure_rdv.Items.Add(i & ":" & ii)
next
next
This could be done with fewer lines of code using linq
Dim steps = Enumerable.Range(0,6)
Dim items as List(Of String) = new List(Of String)()
items.AddRange(steps.Select(Function(x) "08:" & (x * 10).ToString("D2")))
items.AddRange(steps.Select(Function(x) "09:" & (x * 10).ToString("D2")))
Heure_rdv.DataSource = items
First we create a list of integers from 0 to 5 (6 elements), then using these elements we create the strings required multiplying each element of the integer list by ten and converting the result to a two digit string. We do this one time for the 8 hour and one time for the 9. Finally we could set the combo datasource to the resulting list of strings.

How to round time to the nearest quarter hour in word

I need to round time to the nearest quarter hour in a word document. I am not very good at coding.
After a fair bit of searching I have found some vba code but it doesn't quite work. The code is:
Sub Time()
Dim num() As String
Dim tod() As String
Dim temp As String
num = Split(Time, ":")
tod = Split(num(2), " ")
If Val(num(1)) < 15 Then
temp = "00"
ElseIf Val(num(1)) < 30 Then
temp = "15"
ElseIf Val(num(1)) < 45 Then
temp = "30"
ElseIf Val(num(1)) < 60 Then
temp = "45"
End If
gettime = num(0) + ":" + temp + ":00 " + tod(1)
End Function
End Sub
When I try to run it I get a message:
"Compile Error: Expected function or variable"
and "Time" on the fifth line of the code is highlighted which I think is where the program stops running.
The rest of the code in the form is as follows:
This module doesn't affect the time rounding issue but I am including it so as not to leave anything out.
Option Explicit
Sub ClusterCheck()
Dim i As Integer, k As Integer, iCluster As Integer, bResult As Boolean
Dim sFieldNameNo As String, sName As String
On Error Resume Next ' If the first formfield is a checkbox, this will bypass the error that Word returns
sName = Selection.FormFields(1).Name ' Get the name of the formfield
bResult = ActiveDocument.FormFields(sName).CheckBox.Value ' Get the result of the current formfield
sFieldNameNo = Number(sName) ' Get generic number
sName = Left(sName, Len(sName) - Len(sFieldNameNo)) ' Get generic name
' Determine how many fields are within the cluster group
iCluster = 1
Do Until ActiveDocument.Bookmarks.Exists(sName & iCluster) = False
iCluster = iCluster + 1
Loop
iCluster = iCluster - 1
' If the check field is true, turn all of the other check fields to false
Application.ScreenUpdating = False
If bResult = True Then
For k = 1 To iCluster
If k <> sFieldNameNo Then ActiveDocument.FormFields(sName & k).Result = False
Next
End If
Application.ScreenUpdating = True
End Sub
This is the Number module:
Option Explicit
Function Number(ByVal sNumber As String) As String
' This module finds the form fields number within the field name
' Loops through the field name until it only has the number
Do Until IsNumeric(sNumber) = True Or sNumber = ""
sNumber = Right(sNumber, Len(sNumber) - 1)
Loop
Number = sNumber
End Function
This is the protection module:
Option Explicit
Sub Protect()
ActiveDocument.Protect Password:="wup13", NoReset:=True, Type:=wdAllowOnlyFormFields
End Sub
Sub Unprotect()
ActiveDocument.Unprotect Password:="wup13"
End Sub
This is the code that activates on opening and closing the document:
Option Explicit
Sub Document_Open()
' Zooms to page width, turns on Hidden Text, and turns off ShowAll and Table Gridlines
With ActiveWindow.View
.Zoom.PageFit = wdPageFitBestFit
.ShowHiddenText = True
.TableGridlines = False
.ShowAll = False
End With
Options.UpdateFieldsAtPrint = False
End Sub
Sub Document_Close()
' Turn on ShowAll and Table Gridlines
With ActiveWindow.View
.ShowAll = True
.TableGridlines = True
End With
Options.UpdateFieldsAtPrint = True
End Sub
That's all the code in the form. I am not great at VBA but am hoping I can solve this issue (with a little help).
DETAILS OF EXTRA DUTY FORM
Persons details
Family name:
Given name(s):
Level:
No.:
Location:
Cost Centre Code:
Time worked
Were any days of the extra duty performed on a designated public/show holiday? Yes 0 No 0
If yes enter holiday date/details:
Time commenced: [Text Form Field]
Date:
Time ceased: [Text Form Field]
Date:
Total Overtime claimed:
Are you a shift worker? Yes 0 No 0
Details of extra duty performed:
Vehicle details
Car: Yes 0 No 0
Motorcycle: Yes 0 No 0
Registration no.:
Fleet no.:
Stationary vehicle hours:
Yes 0 No 0 (only use for stationary duties)
Vehicle odometer start:
Odometer finish:
Total kms:
Client’s details
Company/Organisation name:
Phone no.:
Contact name:
Job no.:
Payment for special services
Was payment received in advance? Yes 0 No 0
If Yes– Amount:
Receipt no.:
Date:
If No– Amount:
Invoice no.:
Date:
I, , certify the above information to be true
(Signature) (Date)
Manager certification (Checked with roster and certified correct)
(Signature) (Date)
The code from vbforums gives me a subscript out of range error when used as recommended.
In the VBA IDE you can get explanations of what keywords do by placing the cursor on a keyword and pressing F1. This will bring up the MS help page for that particular keyword.
In the OP code the main procedure is 'Time'. This will cause problems for VBA because this is the same as the Time keyword so we would effectively be saying
time(time)
and VBA will stop with an error because the second use of time will be interpreted as the sub time and not the VBA time function so you will get the error message 'Argument not optional'.
The code below will provide what the OP has requested.
Option Explicit
Sub test_gettime()
Dim myTime As String
myTime = Now()
Debug.Print myTime
Debug.Print Format(myTime, "hh:mm:ss")
Debug.Print gettime(Format(myTime, "hh:mm:ss"))
' without the format statement we should also get the date
myTime = Now()
Debug.Print
Debug.Print myTime
Debug.Print gettime(myTime)
End Sub
Public Function gettime(this_time As String) As String
Dim myTimeArray() As String
Dim myQuarterHour As String
myTimeArray = Split(this_time, ":")
' Note that myTimeArray has not been converted to numbers
' Comparison of strings works by comparing the ascii values of each character
' in turn until the requested logic is satisfied
Select Case myTimeArray(1)
Case Is < "15"
myQuarterHour = "00"
Case Is < "30"
myQuarterHour = "15"
Case Is < "45"
myQuarterHour = "30"
Case Is < "60"
myQuarterHour = "45"
Case Else
Debug.Print "More than 60 minutes in the hour??"
End Select
gettime = myTimeArray(0) + ":" + myQuarterHour + ":00 "
End Function

How to use CheckIfPrime

I need to use the CheckIfPrime item in my code, but having some issue, may i know how should I deal with it? Do I need to make declaration?
For number As Integer = 1 To 30
If CheckIfPrime(number) = True Then
sb.Append(number.ToString & " ")
End If
Next
Please advice on how can I do that in Visual Basic.
The VB.net has no CheckIfPrime method, so to deal with it in your task we need to create a method to check the number if is prime or not.
Public Function CheckIfPrime(number As Integer) As Boolean
For i As Integer = 2 To number - 1
If number Mod i = 0 Then
Return False
End If
Next
Return True
End Function
Usage
For number As Integer = 1 To 30
If CheckIfPrime(number) = True Then
'Console.WriteLine(number.ToString & " ")
sb.Append(number.ToString & " ")
End If
Next
Source