I receive word ( .doc ) file names in this format -
MDouglas-DouglasM-02-01-2017-493058190498601
Wherein,
(1) " MDouglas-DouglasM- " remains static in the beginning of the name
(2) "02-01-2017" is the date, which keeps changing every day ( so next day it will be 02-02-2017, and so on
(3) finally, "-493058190498601", which again, keeps changing with every file.
I am only interested in the date "02-01-2017", which I want populated in the Word document at two places:
(1) in the body of the document, in place of typed "Month dd, yyyy",
(2), in the 2nd pg header of the document, in place of typed "Month dd, yyyy".
Again, the "Month dd, yyyy" is only typed twice in the document; one in the body and one in the 2nd pg header. And I want both places populated with the date in the expanded format, that is:
"February 1, 2017"
I could come up with the following macro:
Selection.HomeKey Unit:=wdStory
Selection.Fields.Add Range:=Selection.Range, Type:=wdFieldEmpty, _
text:="FILENAME "
Selection.TypeParagraph
However, this only populates the complete filename at the top of the document. Please suggest (in word VBA).
This will extract the date, others can do the code for the Range field swap (I'd be interested in how this is done)
Option Explicit
Sub Test()
Dim dtExtracted As Date
dtExtracted = ExtractDate("MDouglas-DouglasM-", "MDouglas-DouglasM-02-01-2017-493058190498601")
End Sub
Function ExtractDate(ByVal sPrefix As String, ByVal sFileName As String)
Dim lPrefixLen As Long
lPrefixLen = Len(sPrefix)
If Left$(sFileName, lPrefixLen) = sPrefix Then
Dim sRemainder As String
sRemainder = Mid$(sFileName, lPrefixLen + 1)
Dim vSplit As Variant
vSplit = VBA.Split(sRemainder, "-")
ReDim Preserve vSplit(0 To UBound(vSplit) - 1)
Dim sReJoin As String
sReJoin = VBA.Join(vSplit, "-")
If VBA.IsDate(sReJoin) Then
ExtractDate = CDate(sReJoin)
End If
End If
End Function
Related
I have a word file (doc or docx) that follows a specific name pattern.
The pattern is:
XXXXXXX_X_XXXXXXXX_NNNNNNN_NNNNNNN_XXXXXXXXX.doc(x)
and an example is:
Contract_J_Smith_01032022_31032022_GOOGLE.doc(x)
My intention is to create a macro in Word that will get the name of the file, split it in parts by the underscore (_) and then create two variables (startdate and enddate) with the 4th and 5th parts of the filename in order to use them inside the document.
These parts are dates in the format ddMMyyyy.
Also, I want to check if these variables represent a true date, so if they are true, the variable remains as it is and if they are false, a warning text will replace the variable inside the document.
This warning text should be bold and red.
So far I have managed to write the following code:
Sub AutoOpen()
Dim aStory As Range
Dim aField As Field
Dim fname As String
Dim startdate As String
Dim enddate As String
fname = ActiveDocument.Name
startdate = Split(fname, "_")(3)
enddate = Split(fname, "_")(4)
startdate = Left(startdate, 2) & "/" & Mid(startdate, 3, 2) & "/" & Right(startdate, 4)
enddate = Left(enddate, 2) & "/" & Mid(enddate, 3, 2) & "/" & Right(enddate, 4)
If IsDate(startdate) = False Then
startdate = "Problem with start date in filename"
End If
If IsDate(enddate) = False Then
enddate = "Problem with end date in filename"
End If
With ActiveDocument
.Variables("startdate").Value = startdate
End With
With ActiveDocument
.Variables("enddate").Value = enddate
End With
For Each aStory In ActiveDocument.StoryRanges
For Each aField In aStory.Fields
aField.Update
Next aField
Next aStory
End Sub
The only things that are missing are making the text bold and red.
Any suggestions?
The simplest way to do this is to wrap the DOCVARIABLE fields in your document in an IF field, as shown below.
Here is a link to documentation on the IF Field if you are not familiar with it.
I have a comment field with cells containing text like this:
Cancelled by user at 2018-01-03 03:11:57 without charge
I want to get the date and time information, but it may not always be in the 3rd/4th from last spaces, otherwise I might try to do some sort of complicated split of the cell. Is there an "in cell" way extract the date time information? Or will this need a VBA script? I prefer the former, but I'm trying to make a macro to simplify my life anyway, so VBA would work too.
I'd propose the following formula:
=MID(A1,FIND("at 20",A1)+3,19)
This would require that the date is always preceded by the word 'at' and the date string starts with 20.
You can try this function. It splits the string checking for items that have the first letter numeric, and builds a result string of just the date information.
Public Function ParseForDate(sCell As String) As String
Dim vSplit As Variant
Dim nIndex As Integer
Dim sResult As String
vSplit = Split(sCell, " ")
For nIndex = 0 To UBound(vSplit)
If IsNumeric(Left$(vSplit(nIndex), 1)) Then
sResult = sResult & vSplit(nIndex) & " "
End If
Next
ParseForDate = Trim$(sResult)
End Function
If you wanted to use it in a formula it would look something like this:
=ParseForDate(A1)
To use it in a VBA routine:
Dim s as String
s = ParseForDate(Range("A1"))
Non-VBA solution: (this is assuming the date format is always the same for all cells)
= MAX(IFERROR(DATEVALUE(MID(A1,ROW(INDEX($A:$A,1):INDEX($A:$A,LEN(A1)-19)),20)),0))
+MAX(IFERROR(TIMEVALUE(MID(A1,ROW(INDEX($A:$A,1):INDEX($A:$A,LEN(A1)-19)),20)),0))
Note this is an array formula, so you must press Ctrl+Shift+Enter instead of just Enter when typing this formula.
You will obviously then need to format the cell as a date and time, but this formula gets the numerical value that Excel uses for its internal date and time system.
Using a regex will enable you to fetch the date and time, irrespective of its placement in the string. The following solution will work if the date and time are of the same format as shown in the example string.
Code:
Sub getDateTime()
Dim objReg, matches, str
str = Sheet1.Cells(1, 1).Value 'Change this as per your requirements
Set objReg = CreateObject("vbscript.regexp")
objReg.Global = True
objReg.Pattern = "\d{4}(?:-\d{2}){2}\s*\d{2}(?::\d{2}){2}"
If objReg.test(str) Then
Set matches = objReg.Execute(str)
strResult = matches.Item(0)
MsgBox strResult
End If
End Sub
Click for Regex Demo
Regex Explanation:
\d{4} - matches 4 digits representing the year
(?:-\d{2}){2} - matches - followed by 2 digits. {2} in the end repeats this match 2 times. Once for getting MM and the next time for DD
\s* - matches 0+ whitespaces to match the space between the Date and Time
\d{2} - matches 2 digits representing the HH
(?::\d{2}){2} - matches : followed by 2 digits. The {2} in the end repeats this match 2 times. First time for matching the :MM and the next time for matching the :SS
Screenshots:
Output:
This will be good for about 90 years (using cell C3 for example):
Sub GetDate()
Dim s As String
s = Range("C3").Comment.Text
arr = Split(s, " ")
For i = LBound(arr) To UBound(arr)
If Left(arr(i), 2) = "20" Then
msg = arr(i) & " " & arr(i + 1)
MsgBox msg
Exit Sub
End If
Next i
End Sub
How do I get a sentence with multiple commas in MS Word with VBA that the cursor is in?
All the posts I've found said to get the sentence the cursor is in then use the code:
Selection.Sentences(1)
The above works well with a sentence with only 1 comma. But if I have a sentence with multiple commas like this:
For example, tomorrow is Tuesday(e.g., not Wednesday) or Thursday.
where the cursor is set somewhere in "For example" then "Selection.Sentences(1)" returns between the bars "...(e.g.|, |n...".
I'm using the latest version of Word. I plan on launching the code on an older version (I think 2013) that I first noticed the problem on.
This code is better suited to explain why MS didn't solve your problem than it is to actually solve it. However - depending upon your circumstances - you may like to play with it.
Option Explicit
Sub SelectSentence()
' 30 Jan 2018
' list abbreviations containing periods only
' in sequence of their expected frequency of occurrance
Const Abbs As String = "e.g.,f.i.,etc.,i.e."
Dim Fun As String ' sentence to select
Dim Para As Range
Dim SelStart As Long ' location of selection
Dim Sp() As String ' array of Abbs
Dim Cp() As String ' array of encoded Abbs
With Selection
Set Para = .Paragraphs(1).Range
SelStart = .Start
End With
Sp = Split(Abbs, ",")
With Para
Application.ScreenUpdating = False
.Text = CleanString(.Text, Sp, Cp)
Fun = ActiveDocument.Range(SelStart, SelStart + 1).Sentences(1).Text
SelStart = InStr(.Text, Fun) + .Start - 1
.Text = OriginalString(.Text, Cp)
.SetRange SelStart, SelStart + Len(Fun) - 1
Application.ScreenUpdating = True
.Select
End With
Fun = Selection.Text
Debug.Print Fun
End Sub
Private Function CleanString(ByVal Txt As String, _
Abbs() As String, _
Cp() As String) As String
' 30 Jan 2018
Dim i As Integer
ReDim Cp(UBound(Abbs))
For i = 0 To UBound(Abbs)
If InStr(Txt, ".") = 0 Then Exit For
Cp(i) = AbbToTxt(Abbs(i))
Txt = Replace(Txt, Abbs(i), Cp(i))
Next i
ReDim Preserve Cp(i)
CleanString = Txt
End Function
Private Function AbbToTxt(ByVal Abb As String) As String
' 30 Jan 2018
' use a character for Chr(92) not occurring in your document.
' Apparently it must be a character with a code below 128.
' use same character as function 'AbbToTxt'
AbbToTxt = Replace(Abb, ".", Chr(92))
End Function
Private Function OriginalString(ByVal Txt As String, _
Cp() As String) As String
' 30 Jan 2018
Dim i As Integer
For i = 0 To UBound(Cp) - 1
Txt = Replace(Txt, Cp(i), TxtToAbb(Cp(i)))
Next i
OriginalString = Txt
End Function
Private Function TxtToAbb(ByVal Txt As String) As String
' 30 Jan 2018
' use same character as function 'AbbToTxt'
TxtToAbb = Replace(Txt, Chr(92), ".")
End Function
For one, the code will only handle abbreviations which you program into it (see Const Abbs at the top of the code). For another, it will fail to recognise a period with dual meaning, such as "etc." found at the end of a sentence.
If you are allowed to edit the documents you work with, the better way of tackling your problem may well be to remove the offending periods with Find > Replace. After all, whoever understands "e.g." is also likely to understand "eg". Good Luck!
I have been trying to figure out a way to, after performing a mail merge, separate the documents into individual ones and name them after a specific item, preferably the first line of the header. I have only been able to find ways to split the document, but cannot figure out how to name it. Any help with how to write the VBA code to save a document as the header would be very much appreciated.
Since you already separated the documents, the code below might give them names using their first sentence.
Private Function DocName(Doc As Document) As String
' 23 Aug 2017
Const Illegals As String = "\:/;?*|>"""
Static FaultCounter As Integer
Dim Fun As String
Dim Title As String
Dim Ch As String
Dim i As Integer
Title = Trim(Doc.Sentences(1))
For i = 1 To Len(Title)
Ch = Mid(Title, i, 1)
If (Asc(Ch) > 31) And (Asc(Ch) < 129) Then
If InStr(Illegals, Ch) = 0 Then Fun = Fun & Ch
End If
Next i
If Len(Fun) = 0 Then
FaultCounter = FaultCounter + 1
Fun = Format(FaultCounter, """Default File Name (""0"")""")
End If
DocName = Fun
End Function
Before saving the file you might want to check for duplicates. Use the Dir() function for that and add a number to duplicate names using the system I included above to name files where the first sentence might be empty.
You may also have to review the characters which aren't permitted in file names. I have simply excluded all below ASCII(32) and above ASCII(128), and then the known ones Windows doesn't like. You might want to modify that range further.
To call the above function use code like this:-
Private Sub GetName()
Debug.Print DocName(ActiveDocument)
End Sub
This is the code I have so far, I was able to find it off of a very helpful website, but the code saves as the word "report" which I set it to right now while I'm trying to figure it out, and then the number of the document.
Option Explicit
Sub splitter()
' splitter Macro
' Macro created by Doug Robbins to save each letter created by a mailmergeas
a separate file.
Application.ScreenUpdating = False
Dim Program As String
Dim DocName As String
Dim Letters As Integer, Counter As Integer
Letters = ActiveDocument.Sections.Count
Selection.HomeKey Unit:=wdStory
Counter = 1
While Counter < Letters
'program = ActiveDocument.MailMerge.DataSource.DataFields("Program_Outcomes_PlanReport_Name").Value
DocName = "Reports" & LTrim$(Str$(Counter)) 'Generic name of document
ActiveDocument.Sections.First.Range.Cut
Documents.Add
Selection.Paste
ActiveDocument.Sections(2).PageSetup.SectionStart = wdSectionContinuous
ActiveDocument.SaveAs filename:="E:\assessment rubrics\Templates" & "\" & DocName, FileFormat:=wdFormatDocument, LockComments:=False, Password:="",
AddToRecentFiles:=False, WritePassword:="", ReadOnlyRecommended:=False, EmbedTrueTypeFonts:=False, SaveNativePictureFormat:=False, SaveFormsData:=False, SaveAsAOCELetter:=False
ActiveWindow.Close
Counter = Counter + 1
Wend
Application.ScreenUpdating = True
End Sub
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