When I use restrict to set the date range on an Outlook item object, it does not pick up any appointments before whatever I have set as my "Work Hours" on Outlook. How do I make whatever the user sets as "Work Hours" not influence my macro?
Eg. My "Work Hours" in Outlook is set to 1PM to 5PM. I create 4 appointments:
1111 at 10AM Sept 4, 2017
2222 at 1PM Sept 4, 2017
3333 at 6PM Sept 4, 2017
4444 at 1PM Sept 5, 2017
My code will pick up the last 3 appointments, but not the first, if the dates I enter are 9/4/2017 and 9/5/2017.
Sub restrictDemo()
Dim olkItems As Outlook.Items, _
olkSelected As Outlook.Items, _
olkAppt As Outlook.AppointmentItem, _
dateStart As Date, _
dateEnd As Date
dateStart = InputBox("Starting date?", "", "m/d/yyyy")
dateEnd = InputBox("Ending date?", "", "m/d/yyyy")
If IsDate(dateStart) And IsDate(dateEnd) Then
Set olkItems = Session.GetDefaultFolder(olFolderCalendar).Items
olkItems.IncludeRecurrences = True
olkItems.Sort "Start"
Set olkSelected = olkItems.Restrict("[Start] >= '" & dateStart & "' AND [Start] <= '" & dateEnd & "'")
For Each olkAppt In olkSelected
counter = counter + 1
MsgBox counter
MsgBox olkAppt.Subject & " " & olkAppt.Location & olkAppt.Start
Next
Else
MsgBox "You must enter valid starting and ending dates to run this macro.", vbCritical + vbOKOnly, MACRO_NAME
End If
End Sub
Use the format expected by Restict. https://msdn.microsoft.com/en-us/vba/outlook-vba/articles/items-restrict-method-outlook
Sub restrictDemo()
Dim Counter As Long
Dim olkItems As items
Dim olkSelected As items
Dim olkAppt As AppointmentItem
Dim dateStart
Dim dateEnd
Dim StrFilter As String
' You may need to use a different date format.
dateStart = InputBox("Starting date?", , "2017-08-22")
dateEnd = InputBox("Ending date?", , "2017-08-23")
If IsDate(dateStart) And IsDate(dateEnd) Then
Set olkItems = Session.GetDefaultFolder(olFolderCalendar).items
olkItems.IncludeRecurrences = True
olkItems.Sort "Start"
'StrFilter = "[Start] >= '" & dateStart & "' AND [Start] < '" & dateEnd & "'"
'Debug.Print StrFilter
StrFilter = "[Start] >= '" & Format(dateStart, "ddddd h:nn AMPM") & "'"
Debug.Print StrFilter
StrFilter = StrFilter & " AND [Start] < '" & Format(dateEnd, "ddddd h:nn AMPM") & "'"
Debug.Print StrFilter
Set olkSelected = olkItems.Restrict(StrFilter)
For Each olkAppt In olkSelected
Counter = Counter + 1
Debug.Print Counter & ":" & olkAppt.Subject & " " & olkAppt.location & olkAppt.start
'MsgBox Counter & ":" & olkAppt.Subject & " " & olkAppt.location & olkAppt.start
Next
Else
MsgBox "Enter valid starting and ending dates.", vbCritical + vbOKOnly, "MACRO_NAME"
End If
End Sub
The deeper question of why a day begins at the start of a work day and continues for 24 hours remains unanswered.
Related
I have a piece of code that is supposed to find all appointments in a shared calendar between two dates and delete them. However, it ignores the filter and deletes all appointments in the calendar. Could someone take a look at this and give me some idea of what i'm missing please?
Sub DeleteCal_Appts(sCalendarName As String, ap_dateStart As String, ap_startTime As String, ap_dateEnd As String, ap_endTime As String)
Dim objAppointment As AppointmentItem
Dim objAppointments As Items
Dim objNameSpace As Outlook.NameSpace
Dim objRecip As Recipient
Dim sFilter As Object
Dim dtStartTime As Date, dtEndTime As Date
dtStartTime = CDate(ap_dateStart & " " & ap_startTime)
dtEndTime = CDate(ap_dateEnd & " " & ap_endTime)
Dim myOutApp As Object
myOutApp = CreateObject("Outlook.Application")
objNameSpace = myOutApp.GetNamespace("MAPI")
objRecip = objNameSpace.CreateRecipient(sCalendarName)
objRecip.Resolve()
'objAppointments = objNameSpace.GetSharedDefaultFolder(objNameSpace.CreateRecipient("Unit 2 Peanut Delivery Calendar"), 9).Items
objAppointments = objNameSpace.GetSharedDefaultFolder(objNameSpace.CreateRecipient(sCalendarName), 9).Items
sFilter = "[Start] > '" & Format(dtStartTime, "ddddd h:nn AMPM") & "' And [Start] < '" & Format(dtEndTime, "ddddd h:nn AMPM") & "'"
objAppointments.Sort("[Start]", False)
MsgBox("Total Items at begin: " & objAppointments.Count)
objAppointment = objAppointments.Find(sFilter)
For i = objAppointments.Count To 1 Step -1
objAppointment = objAppointments.Item(i)
objAppointment.Delete()
Next
MsgBox("Total Items at finish: " & objAppointments.Count)
objAppointment = Nothing
objAppointments = Nothing
End Sub
Thanks to #TnTinMn got the answer to this. My only remaining stumbling block was for the date conversion from UK to US:
sFilter = "[Start] >= '" & (dtStartTime.ToString("MM/dd/yyyy HH:MM tt")) & "' AND [End] <= '" & (dtEndTime.ToString("MM/dd/yyyy HH:MM tt")) & "'"
objAppointments.Sort("[Start]", False)
itemsCalendar = objAppointments.Restrict(sFilter)
For i = itemsCalendar.Count To 1 Step -1
objAppointment = itemsCalendar.Item(i)
objAppointment.Delete()
Next
I mean to get all AppointmentItems in a Date range and return them as a Collection.
This is the function I wrote
Function GetAppointmentItemsDatesRange(ByVal dstart As Date, ByVal dend As Date) As Outlook.Items
'=======================================================
' Get all AppointmentItem in a range of dates
'=======================================================
Dim oCalendar As Outlook.Folder
Set oCalendar = Application.Session.GetDefaultFolder(olFolderCalendar)
Dim objItems As Outlook.Items
Dim objRestrictedItems As Outlook.Items
Set objItems = oCalendar.Items
objItems.IncludeRecurrences = True
'objItems.IncludeRecurrences = False
objItems.Sort "[Start]"
Dim filterRange As String
filterRange = "[Start] >= " & Chr(34) & Format(dstart, "dd/mm/yyyy hh:mm AM/PM") & Chr(34) & " AND " & _
"[End] <= " & Chr(34) & Format(dend, "dd/mm/yyyy hh:mm AM/PM") & Chr(34) ' <-- Line #1'
Set objRestrictedItems = objItems.Restrict(filterRange)
Debug.Print "Filter : " & filterRange
Dim oItem As Outlook.AppointmentItem
Dim iIt As Long
Dim nItFilter As Long, nIt As Long
nItFilter = objRestrictedItems.Count
nIt = 0
Debug.Print nItFilter & " total items"
For Each oItem In objRestrictedItems
If (Not (oItem Is Nothing)) Then
nIt = nIt + 1
Debug.Print oItem.Start & "-" & oItem.End ' <-- Line #2'
End If
Next oItem
Debug.Print nIt & " net items"
Set GetAppointmentItemsDatesRange = objRestrictedItems
End Function
I tried with both .IncludeRecurrences = True and False.
This is the output I get.
False:
Filter : [Start] >= "07/11/2020 05:30 PM" AND [End] <= "07/11/2020 06:15 PM"
9 total items
31/12/2015 9:00:00-31/12/2015 9:00:00
31/01/2017 15:30:00-31/01/2017 15:30:00
18/03/2020 12:00:00-18/03/2020 16:00:00
13/04/2020 8:45:00-13/04/2020 9:00:00
09/09/2020 11:00:00-09/09/2020 12:00:00
28/09/2020 14:45:00-28/09/2020 18:00:00
01/10/2020 13:30:00-01/10/2020 15:00:00
07/11/2020 17:30:00-07/11/2020 17:45:00
07/11/2020 17:45:00-07/11/2020 18:15:00
9 net items
True:
Filter : [Start] >= "07/11/2020 05:30 PM" AND [End] <= "07/11/2020 06:15 PM"
2147483647 total items
07/11/2020 17:30:00-07/11/2020 17:45:00
07/11/2020 17:45:00-07/11/2020 18:15:00
2 net items
So I identify two problems to get to my result:
The outputs of Line #1 and Line #2 seem inconsistent, in both cases.
I do not understand why are the first 7 items not filtered out in the False case, even if I can get rid of them with True.
And I do not understand what are those too many Nothing items in the True case.
I do not know hot to define a Collection where I can add the items that satisfy the If (Not (oItem Is Nothing)) condition, so I can return it upon exiting for the caller to use.
What is the explanation for the questions?
How can I achieve my goal?
Since you found a way to identify the required items, add them to a new collection. Pass that collection to the caller.
Option Explicit
Sub collNotNothingItems()
Dim dtSt As Date
Dim dtEn As Date
Dim notNothingItems As Collection
Dim i As Long
dtSt = Date - 7
dtEn = Date
Set notNothingItems = GetAppointmentItemsDatesRange(dtSt, dtEn)
Debug.Print notNothingItems.count & " in the collection passed to the caller"
For i = 1 To notNothingItems.count
With notNothingItems(i)
Debug.Print .Start & "-" & .End
End With
Next
End Sub
Function GetAppointmentItemsDatesRange(ByVal dstart As Date, ByVal dend As Date) As Collection
'=======================================================
' Get all AppointmentItem in a range of dates
'=======================================================
Dim oCalendar As Folder
Dim objItems As Items
Dim objRestrictedItems As Items
Dim filterRange As String
Dim myItems As Collection
Dim oItem As AppointmentItem
Dim iIt As Long
Dim nItFilter As Long
Dim nIt As Long
Set oCalendar = Application.Session.GetDefaultFolder(olFolderCalendar)
Set objItems = oCalendar.Items
objItems.IncludeRecurrences = True
objItems.Sort "[Start]"
'filterRange = "[Start] >= " & Chr(34) & Format(dstart, "dd/mm/yyyy hh:mm AM/PM") & Chr(34) & " AND " & _
"[End] <= " & Chr(34) & Format(dend, "dd/mm/yyyy hh:mm AM/PM") & Chr(34)
filterRange = "[Start] >= " & Chr(34) & Format(dstart, "yyyy-mm-dd hh:mm AM/PM") & Chr(34) & " AND " & _
"[End] <= " & Chr(34) & Format(dend, "yyyy-mm-dd hh:mm AM/PM") & Chr(34)
Debug.Print "filterRange: " & filterRange
Set objRestrictedItems = objItems.Restrict(filterRange)
nItFilter = objRestrictedItems.count
Debug.Print nItFilter & " total items"
nIt = 0
Set myItems = New Collection
For Each oItem In objRestrictedItems
If (Not (oItem Is Nothing)) Then
nIt = nIt + 1
Debug.Print oItem.Start & "-" & oItem.End
myItems.Add oItem
End If
Next oItem
Debug.Print nIt & " net items"
Set GetAppointmentItemsDatesRange = myItems
End Function
I'm trying to grab the last week of appointments using VBA in Outlook.
I'm using the .Restrict method, but something is making my string grab 3 years further back.
I start by declaring formatted dates for my time bracket:
myStart = Format(DateAdd("d", -7, Now()), "ddddd h:nn AMPM")
myEnd = Format(Now(), "ddddd h:nn AMPM")
I build a string to hold my restriction criterion.
strRestriction = "[Start] <= '" & myEnd _
& "' AND [End] >= '" & myStart & "'"
Finally I call restrict on my appointment items:
Set oRestrItems = oItems.Restrict(strRestriction)
For a little more context, here's how I use/call the result:
For Each oApptItem In oRestrItems 'oItems will grab everything, but that's hardly perfect.
If oApptItem.Sensitivity <> olPrivate Then
MsgBox (oApptItem.Subject)
MsgBox (oApptItem.Start)
MsgBox (oApptItem.End)
End If
Next
I can guess that you are missing two statements.
oItems.IncludeRecurrences = True
oItems.Sort "[Start]"
If this is the case, you can ask another question about why the Restrict requires these additional statements. Someone may have an answer.
Minimal, Complete, and Verifiable example. Try commenting out either or both statements. You should see that the items are not the same.
Option Explicit
Sub LastWeekAppts()
Dim objFolder As Folder
Dim oItems As items
Dim oRestrItems As items
Dim strRestriction As String
Dim myStart As Date
Dim myEnd As Date
Dim temp As Object
Set objFolder = Session.GetDefaultFolder(olFolderCalendar)
Set oItems = objFolder.items
' *****************************************
' Odd results without these two statements
oItems.IncludeRecurrences = True
oItems.Sort "[Start]"
' *****************************************
myEnd = Date
myStart = DateAdd("d", -7, Date)
Debug.Print " myStart: " & myStart
Debug.Print " myEnd : " & myEnd
strRestriction = "[Start] <= '" & myEnd _
& "' AND [End] >= '" & myStart & "'"
Debug.Print strRestriction
Set oRestrItems = oItems.Restrict(strRestriction)
For Each temp In oRestrItems
Debug.Print temp.start & " - " & temp.Subject
Next temp
End Sub
How do I send email reminders 2 weeks prior to the Lead Date?
Below is my SQL code from a query.
SELECT CalibrationRecord.RecordID, CalibrationRecord.CalRequirement, CalibrationRecord.CalStatus,
CalibrationRecord.CalLocation, Equipment.EquipmentType, Equipment.SerialNo, Equipment.ModelNo,
Equipment.AssetNo, CalibrationRecord.EmpName, Employees.EmailAddress, CalibrationRecord.LastCalDate,
CalibrationRecord.CalTimeInterval, CalibrationRecord.UOM,
DateAdd(IIf([CalibrationRecord]![UOM]="days","d",IIf([CalibrationRecord]![UOM]="month","m","yyyy")),
[CalTimeInterval],[LastCalDate]) AS CalUpcomingDate, CalibrationRecord.DateEmailSent,
DateAdd(IIf([Equipment]![UOM]="weeks","ww"),-[LeadInterval],[CalUpcomingDate]) AS LeadDate
FROM Equipment INNER JOIN (Employees INNER JOIN CalibrationRecord ON Employees.EmpID = CalibrationRecord.EmpName)
ON Equipment.ItemID = CalibrationRecord.EquipItemID
WHERE (((CalibrationRecord.CalStatus)="Not Started")
AND ((Employees.EmailAddress) Is Not Null)
AND ((CalibrationRecord.CalTimeInterval) Between 6 And 9)
AND ((CalibrationRecord.UOM) Like "month")
AND ((Employees.EmpName) Not Like "MFGUSER")) OR (((CalibrationRecord.UOM) Like "days"));
This is my Email reminder code. I just want to include a piece of code that will send email reminder 2 weeks prior to the Lead Date.
Function GenerateEmail(MySQL As String)
On Error GoTo Exit_Function:
Dim oOutLook As Outlook.Application
Dim oEmailAddress As MailItem
Dim MyEmpName As String
Dim MyEquip As String
Dim MyModel As String
Dim MyAsset As String
Dim MySerial As String
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset(MySQL)
If rs.RecordCount > 0 Then
rs.MoveFirst
Do Until rs.EOF
If Not IsNull(rs!EmailAddress) Then
' Only Send Emails if never been sent before - or past 14 days since last one'
If (IsNull(rs!DateEmailSent)) Or DateDiff("d", rs!DateEmailSent, Date) >= 14 Then
If rs!LeadDate - 2 * 7 <= Date Then **This is what i have so far for the 2 weeks prior to Lead Date**
If oOutLook Is Nothing Then
Set oOutLook = New Outlook.Application
End If
Set oEmailAddressItem = oOutLook.CreateItem(olMailItem)
With oEmailAddressItem
'MyEmpName = DLookup("EmpName", "Employees", "[EmpID]= " & rs!EmpName)
MyEquip = rs!EquipmentType
MyModel = rs!ModelNo
MyAsset = rs!AssetNo
MySerial = rs!SerialNo
.To = "dgaskins#eeimfg.com"
.Subject = "Monthly Calibrations"
.Body = "Calibration ID: " & rs!RecordID & vbCr & _
"Location: " & rs!CalLocation & vbCr & _
"Requirement: " & rs!CalRequirement & vbCr & _
"Name: " & MyEquip & vbCr & _
"Serial No.: " & MySerial & vbCr & _
"Model No.: " & MyModel & vbCr & _
"Asset No.: " & MyAsset & vbCr & _
"Upcoming Date: " & rs!CalUpcomingDate & vbCr & vbCr & _
"This email is auto generated. Please Do Not Reply!"
'.Display
.Send
' Make sure to record that reminder was sent '
rs.Edit
rs!DateEmailSent = Date
rs!LeadDate = DateAdd("ww", -2, Now)
rs.Update
End With
' Only do this if this has been set '
Set oEmailAddressItem = Nothing
End If
End If
End If
rs.MoveNext
Loop
' Do this at end '
Set oOutLook = Nothing
Else
End If
rs.Close
Exit_Function:
Exit Function
End Function
Get rid of this If and Matching Endif
If DateDiff("d", Date, rs!LeadDate) Then **This is what i have so far for the 2 weeks prior to Lead Date**
Change this
' Only Send Emails if never been sent before - or past 14 days since last one'
If (IsNull(rs!DateEmailSent)) Or DateDiff("d", rs!DateEmailSent, Date) >= 14 Then
To This
' Only Send Emails if never been sent before
' - or past 14 days since last one
' - or with 14 days of LeadDate
If (IsNull(rs!DateEmailSent)) Or DateDiff("d", rs!DateEmailSent, Date) >= 14 Or DateDiff("d", Date, rs!LeadDate) <= 14 Then
I have an Outlook macro that filters email objects by date and returns items based on an array.
The filter for today is the following:
sfilter = "[ReceivedTime]>=""&Date()12:00am&"""
Set myItems = myNewFolder.Items.Restrict(sfilter)
sFilter is a string and this returns the items for today as intended.
I am trying to filter to emails received yesterday.
The following were my attempts.
sfilter = "[ReceivedTime]>=""&Date(-1) 12:00am&"" AND [ReceivedTime]<= ""&Date() 12:00am&"" "
tfilter = Format(DateAdd("d", -1, Date), "mm/dd/yyyy")
rFilter = Format(DateAdd("d", 0, Date), "mm/dd/yyyy")
I intended to use the tFilter and rFilter as the upper and lower bound for sFilter.
I tried to use the DateAdd method after looking on the MSDN site with the function information but that did not return yesterday's items.
I tried the solution offered on this question (Outlook .Restrict method does not work with Date).
The method with date(-1) did not work in tandem with date. According to the MSDN site logical operators should work.
Note: The lower three examples cited compile and do not return any errors.
You can find yesterday's mail with two separate Restricts.
Private Sub EmailYesterday()
Dim oOlInb As Folder
Dim oOlItm As Object
Dim oOlResults As Object
Dim i As Long
Dim sFilter As String
Dim sFilter2 As String
Set oOlInb = Session.GetDefaultFolder(olFolderInbox)
'Filter recent - Lower Bound of the range
sFilter = "[ReceivedTime]>'" & format(Date - 1, "DDDDD HH:NN") & "'"
Debug.Print vbCr & sFilter
Set oOlResults = oOlInb.Items.Restrict(sFilter)
Debug.Print oOlResults.count & " items."
If oOlResults.count > 0 Then
For i = 1 To oOlResults.count
Set oOlItm = oOlResults(i)
Debug.Print oOlItm.Subject & " - " & oOlItm.ReceivedTime
Next i
End If
' Filter range - Upper Bound
sFilter2 = "[ReceivedTime]<'" & format(Date, "DDDDD HH:NN") & "'"
Debug.Print vbCr & sFilter; " AND " & sFilter2
Set oOlResults = oOlResults.Restrict(sFilter2) ' Restrict the Lower Bound result
Debug.Print oOlResults.count & " items."
If oOlResults.count > 0 Then
For i = 1 To oOlResults.count
Set oOlItm = oOlResults(i)
Debug.Print oOlItm.Subject & " - " & oOlItm.ReceivedTime
Next i
End If
ExitRoutine:
Set oOlInb = Nothing
Set oOlResults = Nothing
Debug.Print "Done."
End Sub
Yesterday date could be filtered as below
oOlResults.Restrict("#SQL=%yesterday(""urn:schemas:httpmail:datereceived"")%")
The same for today or this month.
oOlResults.Restrict("#SQL=%today(""urn:schemas:httpmail:datereceived"")%")
oOlResults.Restrict("#SQL=%thismonth(""urn:schemas:httpmail:datereceived"")%")
More info here