capture dates by quarters in access - ms-access

To make my life much easier working with my data I set 3 columns of same date content. The first displays in the format mm/dd/yyyy, second in the format of yyyy-mm and the third in the format yyyy-q.
I did it purposely due to my reports. Sometimes I need to create monthly, quarterly, yearly etc. Usually I work with a form where I invite the user select start and end date and by a click of a button run a report. This report extracts a query where I specify on the date section to pull all information between start and end date. This time I want to do the same procedure but instead of start and end date - I want the user to select which quarter he wants so that the query will pull all information regarding this quarter. What do I specify in the criteria to archive this?

Filter on
DatePart("q", [YourDateField])
or
Format([YourDateField], "yyyyq")
To obtain the first and last date of a quarter, given the year and the quarter, you can use these expressions:
DateQuarterFirst = DateSerial(Y, 1 + 3 * (Q - 1), 1)
DateQuarterLast = DateSerial(Y, 1 + 3 * Q, 0)
If you have a date of the quarter, you can these functions to obtain the first and last date of the quarter of that date:
Public Function DateThisQuarterFirst( _
Optional ByVal datDateThisQuarter As Date) As Date
Const cintQuarterMonthCount As Integer = 3
Dim intThisMonth As Integer
If datDateThisQuarter = 0 Then
datDateThisQuarter = Date
End If
intThisMonth = (DatePart("q", datDateThisQuarter) - 1) * cintQuarterMonthCount
DateThisQuarterFirst = DateSerial(Year(datDateThisQuarter), intThisMonth + 1, 1)
End Function
Public Function DateThisQuarterLast( _
Optional ByVal datDateThisQuarter As Date) As Date
Const cintQuarterMonthCount As Integer = 3
Dim intThisMonth As Integer
If datDateThisQuarter = 0 Then
datDateThisQuarter = Date
End If
intThisMonth = DatePart("q", datDateThisQuarter) * cintQuarterMonthCount
DateThisQuarterLast = DateSerial(Year(datDateThisQuarter), intThisMonth + 1, 0)
End Function

Related

MS Access: Calculate First Day of Next Quarter based on Date field

On a MS Access Table, I would like to have a column that has the first day of the following quarter based on a date in another column.
For example, I have a Start_Date column with a value of 2018-02-04, I would like the record on that column to show that the following quarter would start on 2018-04-01.
I have this working fine in Excel using the following formula:
=DATE(YEAR(M2),((INT((MONTH(M2)-1)/3)+1)*3)+1,1)
In Access, I added the column choosing Calculated Field > Date/Time, but could not figure out a way to get it to work like an Excel.
Has anyone tried this before or know of a possible solution? Thanks
You can use this function:
Public Function DateNextQuarterFirst( _
Optional ByVal datDateThisQuarter As Date) As Date
Const cintQuarterMonthCount As Integer = 3
Dim intThisMonth As Integer
If datDateThisQuarter = 0 Then
datDateThisQuarter = Date
End If
intThisMonth = DatePart("q", datDateThisQuarter) * cintQuarterMonthCount
DateNextQuarterFirst = DateSerial(Year(datDateThisQuarter), intThisMonth + 1, 1)
End Function
To use it in a query with SQL similar to this:
Select *, DateNextQuarterFirst([Start_Date]) As NextQuarterFirst
From YourTable

Sum values in one field based on date range in another

I have two tables, both with dates (which is what they are/will be joined on)
Table 1 has daily rainfall values
Table 2 has weekly water volume values.
I'm trying to get Access to calculate the weekly rainfall (from the daily values) based on the dates that are given for the water volumes ie: total rainfall between two dates calculated dynamically. I have some experience with using Access SQL but I am stumped on this one. To add to the complication, every once in a while the volume values aren't always 7 days apart.
One method is to find the week numbers from each table, then join between these:
Select
Sum(Table1.Volume) As Volume1,
ISO_Weeknumber(Table1.[datefield]) As WeekNumber1,
Sum(Table2.Volume) As Volume2,
ISO_Weeknumber(Table2.[datefield]) As WeekNumber2
From
Table1
Inner Join
Table2
On ISO_Weeknumber(Table1.[datefield])=ISO_Weeknumber(Table2.[datefield])
Group By
ISO_Weeknumber(Table1.[datefield]),
ISO_Weeknumber(Table2.[datefield])
using this function:
Public Function ISO_WeekYearNumber( _
ByVal datDate As Date, _
Optional ByRef intYear As Integer, _
Optional ByRef bytWeek As Byte) _
As String
' Calculates and returns year and week number for date datDate according to the ISO 8601:1988 standard.
' Optionally returns numeric year and week.
' 1998-2007, Gustav Brock, Cactus Data ApS, CPH.
' May be freely used and distributed.
Const cbytFirstWeekOfAnyYear As Byte = 1
Const cbytLastWeekOfLeapYear As Byte = 53
Const cbytMonthJanuary As Byte = 1
Const cbytMonthDecember As Byte = 12
Const cstrSeparatorYearWeek As String = "W"
Dim bytMonth As Byte
Dim bytISOThursday As Byte
Dim datLastDayOfYear As Date
intYear = Year(datDate)
bytMonth = Month(datDate)
bytWeek = DatePart("ww", datDate, vbMonday, vbFirstFourDays)
If bytWeek = cbytLastWeekOfLeapYear Then
bytISOThursday = Weekday(vbThursday, vbMonday)
datLastDayOfYear = DateSerial(intYear, cbytMonthDecember, 31)
If Weekday(datLastDayOfYear, vbMonday) >= bytISOThursday Then
' OK, week count of 53 is caused by leap year.
Else
' Correct for Access97/2000+ bug.
bytWeek = cbytFirstWeekOfAnyYear
End If
End If
' Adjust year where week number belongs to next or previous year.
If bytMonth = cbytMonthJanuary Then
If bytWeek >= cbytLastWeekOfLeapYear - 1 Then
' This is an early date of January belonging to the last week of the previous year.
intYear = intYear - 1
End If
ElseIf bytMonth = cbytMonthDecember Then
If bytWeek = cbytFirstWeekOfAnyYear Then
' This is a late date of December belonging to the first week of the next year.
intYear = intYear + 1
End If
End If
ISO_WeekYearNumber = CStr(intYear) & cstrSeparatorYearWeek & Format(bytWeek, "00")
End Function
For this to work, you must have all weeks in Table1 that exist in Table2 and vice versa.

#type Error on VBA function when input left blank

I am using the following code in Access 2010. I use it in an unbound text box to return weekdays between two schedule dates (start/Finish) on various scheduled tasks on a form. Code is working properly when dates are entered, however on this particular form not every task will have start / end dates. I would like the code to just return "" or 0 if inputs are blank.
I should note I did not write this code myself, I am very very new to VBA and found this code online and manipulated it slightly to work for my application. How can I modify this to fit my needs?
Public Function Weekdays( ByRef startDate As Date, _
ByRef endDate As Date _
) As Integer
' Returns the number of weekdays in the period from startDate
' to endDate inclusive. Returns -1 if an error occurs.
' If your weekend days do not include Saturday and Sunday and
' do not total two per week in number, this function will
' require modification.
On Error GoTo Weekdays_Error
' The number of weekend days per week.
Const ncNumberOfWeekendDays As Integer = 2
' The number of days inclusive.
Dim varDays As Variant
' The number of weekend days.
Dim varWeekendDays As Variant
' Temporary storage for datetime.
Dim dtmX As Date
' Calculate the number of days inclusive (+ 1 is to add back startDate).
varDays = DateDiff(Interval:="d", _
date1:=startDate, _
date2:=endDate) + 1
' Calculate the number of weekend days.
varWeekendDays = (DateDiff(Interval:="ww", _
date1:=startDate, _
date2:=endDate) _
* ncNumberOfWeekendDays) _
+ IIf(DatePart(Interval:="w", _
Date:=startDate) = vbSunday, 1, 0) _
+ IIf(DatePart(Interval:="w", _
Date:=endDate) = vbSaturday, 1, 0)
' Calculate the number of weekdays.
Weekdays = (varDays - varWeekendDays)
Weekdays_Exit:
Exit Function
Weekdays_Error:
Weekdays = -1
Resume Weekdays_Exit
End Function
Your code will have to accept a Null value, since Date is a Data type that will not tolerate Null, you have two methods, change the declaration of the function from.
Public Function Weekdays( ByRef startDate As Date, _
ByRef endDate As Date _
) As Integer
To,
Public Function Weekdays(startDate, endDate) As Integer
This way the code can have Null values, so a few more additions could be made as,
Public Function Weekdays(startDate, endDate) As Integer
' Returns the number of weekdays in the period from startDate
' to endDate inclusive. Returns -1 if an error occurs.
' If your weekend days do not include Saturday and Sunday and
' do not total two per week in number, this function will
' require modification.
On Error GoTo Weekdays_Error
If IsNull(startDate) Or IsNull(endDate) Then
Weekdays = 0
Exit Function
End If
Const ncNumberOfWeekendDays As Integer = 2
'so on....
Or the other way is to make sure you pass dates by employing Nz() or even prevent the function to be called if you have Null values.

Multi Select List Boxes with Multiple Columns in Access 2013

I have a listbox set to Multiselect property of Simple.
The listbox is populated by using a table.
There are 4 columns in the listbox
1 3/23/2014 4/5/2014 2014
2 4/6/2014 4/19/2014 2014
3 4/20/2014 5/3/2014 2014
The columns are PayPeriod, StartDate, EndDate, FiscalYear
What I want to be able to do is highlight a chunk of dates and have the first selected StartDate and the last selected EndDate populate two hidden text boxes so I can use them for my queries/reports.
I've tried a couple different ways. Each time what happens is it only uses the last item I have selected in it's calculations.
Dim ItemIndex As Variant
For Each ItemIndex In Me.lstPayPeriods.ItemsSelected
If Me.lstPayPeriods.Selected(ItemIndex) And Me.lstPayPeriods.Selected(ItemIndex - 1) = False Then
Date1.SetFocus
Date1.Text = Me.lstPayPeriods.Column(2, Me.lstPayPeriods.ListIndex)
End If
Next
In this example I tried to have it go through each Item of the listbox. I wanted to check to see if the current row was selected and the row before it wasn't. That way I could determine it was the first item selected in the group of selected items. It would always only use the last item I had selected.
Dim CurrentRow As Integer
Dim FirstDate As Date
For CurrentRow = 0 To Me.lstPayPeriods.ListCount - 1
If Me.lstPayPeriods.Selected(CurrentRow) Then
Date2.SetFocus
Date2.Text = Me.lstPayPeriods.Column(3, Me.lstPayPeriods.ListIndex)
End If
Next CurrentRow
For CurrentRow = 0 To Me.lstPayPeriods.ListCount - 1
If Me.lstPayPeriods.Selected(CurrentRow) And Me.lstPayPeriods.Selected(CurrentRow - 1) = False Then
Date1.SetFocus
Date1.Text = Me.lstPayPeriods.Column(2, Me.lstPayPeriods.ListIndex)
End If
Next CurrentRow
I tried to do something similar with this code. Again, it only uses the last item I have selected.
I am running into a wall figuring out how to accomplish my goal.
I think the issue is in your approach. I'm personally not keen on the approach you're using to determine the earliest start and latest end, though the issue might just be your column numbers: the first column in a listbox is column 0, and the last column (of 4 columns) is column 3. Accordingly in your code above, you're setting Date2 = fiscal year, not enddate.
I would however recommend a different approach to determining (a) the earliest selected StartDate, and (b) the latest selected enddate. You could have a loop for each operation, or your can encapsulate both in a function:
private function GetPayPeriodDate(baseValue as Date, findLater as boolean, colNo as long) as Date
'baseValue is the default date to test against
'findLater tells the function whether to look for < or > the baseValue
'colNo tells the function which column of data to test
Dim vv as variant
For each vv in lstPayPeriods.ItemsSelected
if lstPayPeriods.Selected(vv) then
if findLater then
if lstPayPeriods.Column(colNo, vv) > baseValue then
baseValue = lstPayPeriods.Column(colNo, vv)
end if
else
if lstPayPeriods.Column(colNo, vv) < baseValue then
baseValue = lstPayPeriods.Column(colNo, vv)
end if
end if
end if
next vv
GetPayPeriodDate = baseValue
end function
Then you can set your start and end date textboxes by calling this function:
me.StartDate = GetPayPeriodDate(CDate("31/12/2099"), false, 1)
'since startdate looks for the earliest date, the base date must be in the future
me.EndDate = GetPayPeriodDate(CDate("01/01/1900"), true, 2)
'similarly, looking for the latest date, base date must be in the past

Check Microsoft Access Form Values before Save

I have an Access Form - lets call it "Add Labor" (Access 2007) that saves data into a table.
The table has two columns in particular called "Start Date" and "End Date" (This table stores tasks)
There is also another table called FiscalYears which includes Start and End Dates for Fiscal Years, which is structured as follows
FyID
FYear
StartDate
EndDate
Example Data:
FYId FYear StartDate EndDate
-----------------------------
1 2010 10/1/2009 9/30/2010
2 2011 10/1/2010 9/30/2011
So in My Add Labor Form if someone enters labor that span across two fiscal years I need to enter two labor entries. Here is an example
If a user selects Labor Start Date = 6/30/2009
And End Date 10/2/2010 , it spans two fiscal years
So in my Labor Table I should enter two things
LaborID StartDate EndDate
-----------------------------
1 6/30/2009 9/30/2010
2 10/1/2010 10/2/2010
Basically I need to do a check before I save the record and add two records if they span Fiscal years, right now I'm just blindly doing Save Record on the form (inbuilt), but I guess I need to add some VBA. I've hardly ever used Access so this may be simple(hopefully). I am thinking instead of the event which just calls Save Record, I need it to add custom VBA.
Say you have an unbound form for adding the dates, you can say:
Dim rsFY As DAO.Recordset
Dim rsAL As DAO.Recordset
Dim db As Database
Dim sSQL As String
Set db = CurrentDb
''Select all years from the fiscal years table
sSQL = "SELECT FYear, StartDate, EndDate " _
& "FROM FiscalYears WHERE StartDate>=#" & Format(Me.StartDate, "yyyy/mm/dd") _
& "# Or EndDate <=#" & Format(Me.Enddate, "yyyy/mm/dd") _
& "# ORDER BY FYear"
Set rsFY = db.OpenRecordset(sSQL)
Set rsAL = db.OpenRecordset("AddLabor") ''table
''Populate recordset
rsFY.MoveLast
rsFY.MoveFirst
Do While Not rsFY.EOF
''Add records for each year selected
rsAL.AddNew
If rsFY.AbsolutePosition = 0 Then
rsAL!StartDate = Format(Me.StartDate, "yyyy/mm/dd")
Else
rsAL!StartDate = rsFY!StartDate
End If
If rsFY.AbsolutePosition + 1 = rsFY.RecordCount Then
rsAL!Enddate = Format(Me.Enddate, "yyyy/mm/dd")
Else
rsAL!Enddate = rsFY!Enddate
End If
rsAL.Update
rsFY.MoveNext
Loop
If the code was running in a main form with a subform showing the Addlabor table, you could update the subform to show the new records like so:
Me.Addlabor_subform.Requery
Why do you need a FiscalYears table? If your organization's fiscal years always start on Oct. 1 and end on Sept. 30, you can use a function to determine the fiscal year for a given date.
Public Function Fy(ByVal pDate As Date) As Integer
Dim intYear As Integer
Dim intReturn As Integer
intYear = Year(pDate)
If pDate > DateSerial(intYear, 9, 30) Then
intReturn = intYear + 1
Else
intReturn = intYear
End If
Fy = intReturn
End Function
And simple functions to return the Start and End dates for a given year.
Public Function FyStart(ByVal pYear As Integer) As Date
FyStart = DateSerial(pYear - 1, 10, 1)
End Function
Public Function FyEnd(ByVal pYear As Integer) As Date
FyEnd = DateSerial(pYear, 9, 30)
End Function
You can then determine how many fiscal years are included in a given date range by:
Fy(EndDate) - Fy(StartDate)
But I may be totally off base because you said "Start Date = 6/30/2009 And End Date 10/2/2010" spans two years. However, this expression returns 2 (3 years):
Fy(#10/2/2010#) - Fy(#6/30/2009#)