Edit: There were some logic errors that Mark pointed out in the comments section. I was rounding throughout the function and skewing some of my own totals. I’ve updated this post to reflect all of the changes (code, images, text, etc.). Thank you for your time, Mark, for both reading the post and passing along some beneficial information.
I did that thing where I start a post that will include a shareable PowerShell function and I’ve yet to actually write any PowerShell. I’ll get there eventually, but for now, let me explain the purpose of the function I’ll be writing. Realistically, this explanation for you ought to help me with authoring the code. It’s obvious, but once I’ve defined the specifications and needs, it should be even easier to write.
My current employer — this is about to sound like a word problem from grade school — allows me to carry forward 320 vacation hours into the next calendar year. Anything above 320 hours is forfeited. Because I was on track to have greater than 320 hours by the end of the year, I’ve been taking off every Wednesday since the last week of September. If you haven’t been to Target at 8 a.m. on a weekday morning, then you’re missing out. It was so empty; it was just me and very few people — amazing. I digress.
My local HR is adamant about employees not losing/forfeiting vacation time. In all of this, I decided I should write the code to do the arithmetic in the PowerShell console. I later decided I should write a function that would do the calculations for me, instead. Before I actually construct the code and build the function, let’s walk through some needed values.
1. Number of carry forward vacation hours: 320 (default value, same for all employees)
2. Number of current vacation hours: 362.670
3. Number of pay periods during the remainder of the calendar year: 5
4. Hours of vacation accrued per pay period: 6.77 (default value, not the same for all employees)
First, I need to determine how many hours I currently have above the carry forward vacation hours of 320. That’s the current vacation hours minus the carry forward vacation hours: 362.670 – 320. It’s possible someone using my (so far non-existent) function could have less than 320 vacation hours. This would change things, and it’s probably worth keeping in mind, as the function will need to handle this possibility.
Second, I’d like to know how many more vacation hours I’ll accrue during the remainder of the calendar year. That’s the vacation accrual hours times the number of pay periods during the remainder of the calendar year: 6.77 * 5. Let’s go.
367.670 – 320 = 42.67
6.77 * 5 = 33.85
42.67 + 33.85 = 76.52
If we divide that total by 8 hours (as in, hours worked per day), we know how many days of vacation I need to take to use up my hours: 76.52 / 8 = 9.565. It’s a little over nine days, but not quite 10. In thinking through this assignment, I’ve realized that it’s possible someone may only want to know their current overage (the subtraction problem) versus their future overage (the multiplication problem) or vice versa. Anyway, I’m off to write this function now. To you, it’ll seem like no time passed at all; for me, I won’t be nearly as lucky. Still, having defined the function’s requirements upfront, before writing any code, has already simplify things.
Well, that’s done and working. I’m going to include images of the function because they’re easier on the eyes and I think it’ll simplify my code explanations. I will, however, make the function copyable at the end of this post.
Let’s start with the parameters.
This function includes five parameters. Beginning from the top, we have CarryForwardVacationHours
. This has a default value of 320, however, this can be changed by the person/process when this function is invoked. It won’t need to be for me unless my current employer changes the carry forward amount. Next, we have CurrentVacationHours
. The is the parameter that’s used to supply how many hours a person currently has. The value needs to be greater than the CarryForwardVacationHours
or there won’t be any overage hours.
The following parameter is VacationAccrualRate
. This contains a default value of 6.77 hours. This value does vary depending on how long a person has been employed by my current employer. This is the largest possible amount, as it’s based on longevity. The second to last parameter is PayPeriods
. This is how many pay periods there are until the end of the calendar year. The person invoking the function will have to gather this information themself.
And finally, there is the Round
parameter. This was a late addition to this function that was discussed in a recent post, Setting the Rounding Scale. This parameter has a default value of 2. This means that any values with decimals with more than 2 values, will be rounded down to two, or to whatever the value is set to when the function is invoked. As we saw in that recent post, it’s not going to add zeros into decimal positions. If the number is 42.1 and you indicate to round to three decimal places, it’s still going to be 42.1. Read the linked post to see how to add zeros if that’s something that would ever be of use to you.
Now, let’s move on to the Process block and its first region, Current Hours. It’s not pictured in these images, but the Begin block isn’t being used in this function. This is the reason it didn’t make it into this or the previous image.
This region of this Process block, first determines if the current vacation hours are greater than the carry forward vacation hours. If it is, it does a little math, and we’ll get there in a moment. If it isn’t, it sets the overage hours and overage days to zero. We’re storing all of our values in an ordered hash table, so that’s where these would land. If the current vacation hours are greater than the carry forward vacation hours, then it subtracts the two numbers as we mentioned earlier. In order to determine how many overage days there are, it divides the result by 8 hours per day, placing both values into the hash table. The carry forward hours are added to the hash table, as well.
Following this region, let’s discuss the Future/Total Hours region.
In this region, we determine our vacation accrual hours and from that total, our vacation accrual days. Finally, we put it all together for the total hours by combining the overage hours and vacation accrual hours and our total days by combing the overage days and the vacation accrual days. Much like the carry forward hours in the above region, we include the vacation accrual rate, too.
Let’s close out with the End Block. This is simple; we convert our hash table — the keys and values — into PowerShell objects, where they become properties and values. This way, they can be easier manipulated by other PowerShell commands if we choose to do that.
Now, let’s include a few test invocations and ensure we’re getting the proper results. This first one just calculates my vacation overage hours and days.
Show-VacationTime -CurrentVacationHours 362.670
CarryForward : 320
OverageHours : 42.67
OverageDays : 5.33
AccrualRate : 6.77
AccrualHours : 0
AccrualDays : 0
TotalHours : 42.67
TotalDays : 5.33
This one returns my vacation overages, as well as my vacation accruals.
Show-VacationTime -CurrentVacationHours 362.670 -PayPeriods 5
CarryForward : 320
OverageHours : 42.67
OverageDays : 5.33
AccrualRate : 6.77
AccrualHours : 33.85
AccrualDays : 4.23
TotalHours : 76.52
TotalDays : 9.57
These last example invocations run the same command as above but include the Round
parameter with a couple of different values. Remember, 2 is the default value for this parameter.
Show-VacationTime -CurrentVacationHours 362.670 -PayPeriods 5 -Round 0
CarryForward : 320
OverageHours : 43
OverageDays : 5
AccrualRate : 6.77
AccrualHours : 34
AccrualDays : 4
TotalHours : 77
TotalDays : 10
Show-VacationTime -CurrentVacationHours 362.670 -PayPeriods 5 -Round 4
CarryForward : 320
OverageHours : 42.67
OverageDays : 5.3338
AccrualRate : 6.77
AccrualHours : 33.85
AccrualDays : 4.2312
TotalHours : 76.52
TotalDays : 9.565
Okay, one final example of when you don’t send in the current vacation hours to determine an overage, but you do want to know your vacation accrual hours.
Show-VacationTime -PayPeriods 5
CarryForward : 320
OverageHours : 0
OverageDays : 0
AccrualRate : 6.77
AccrualHours : 33.85
AccrualDays : 4.23
TotalHours : 33.85
TotalDays : 4.23
Finally, as I promised, here’s a copyable version of this function. I’m quite aware that there are only so many of us that can and/or would make use of this function. Still, some great concepts have been demonstrated and maybe some of them can be included in some of your own projects.
Function Show-VacationTime {
[CmdletBinding()]
Param (
[Parameter()]
$CarryForwardVacationHours = 320,
[Parameter()]
$CurrentVacationHours,
[Parameter()]
$VacationAccrualRate = 6.77,
[Parameter()]
[int]$PayPeriods,
[Parameter()]
[ValidateRange(0,4)]
[int]$Round = 2
) # End Param.
Begin {} # End Begin.
Process {
#region: Current Hours.
If ($CurrentVacationHours -gt $CarryForwardVacationHours) {
$OverageHours = $CurrentVacationHours - $CarryForwardVacationHours
$OverageDays = $OverageHours / 8 # 8 hours.
$Hashtable = [ordered]@{
CarryForward = $CarryForwardVacationHours
}
} Else {
$Hashtable = [ordered]@{
CarryForward = $CarryForwardVacationHours
OverageHours = 0
OverageDays = 0
}
} # End If-Else.
#endregion.
#region: Future/Total Hours.
$VacationAccrualHours = $PayPeriods * $VacationAccrualRate
$VacationAccrualDays = $VacationAccrualHours / 8
$TotalHours = $OverageHours + $VacationAccrualHours
$TotalDays = $OverageDays + $VacationAccrualDays
$Hashtable['OverageHours'] = ([math]::Round($OverageHours,$Round))
$Hashtable['OverageDays'] = ([math]::Round($OverageDays,$Round))
$Hashtable['AccrualRate'] = $VacationAccrualRate
$Hashtable['AccrualHours'] = ([math]::Round($VacationAccrualHours,$Round))
$Hashtable['AccrualDays'] = ([math]::Round($VacationAccrualDays,$Round))
$Hashtable['TotalHours'] = ([math]::Round($TotalHours,$Round))
$Hashtable['TotalDays'] = ([math]::Round($TotalDays,$Round))
#endregion.
} # End Process.
End {
[PSCustomObject]$Hashtable
} # End End.
} # End Function: Show-VacationTime.