Tag Archives: divison

My Work-Specific Vacation Time Function

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.

Formatting Decimal and Whole Numbers

This post will show how to use some simple logic and formatting to display only two decimal places with decimal numbers, and no decimal places with whole numbers.

I recently updated a game I wrote in Windows PowerShell called, “The 1 to 100 Game.” In this game, a person tries to guess a number between 1 and 100 that has been chosen by the computer (or rather, the advanced function). The computer will offer hints, as to whether the number it has chosen, is higher or lower than what was last guessed by the person playing the game. This person continues to guess until they correctly match the number. You can find the advanced function on the Microsoft TechNet Gallery here: http://gallery.technet.microsoft.com/The-1-to-100-Game-5288e279. While it’s not my most popular advanced function, I enjoyed the project, and especially when I shared it with my son as the 1 to 100 Game is one of the many games we play during our longer car rides. Oh, and if you’re looking for an example of nested do-while language constructs, then look no further – it’s up to three now.

During the rewrite to version 2.0, I decided to add something new. The game records the number of attempts it takes to guess the correct number, and how many games you’ve played. With that type of information, it can easily calculate the average number of attempts per game. This won’t always be a whole number and so I needed a way to only use two decimal places when decimal numbers were returned, and to use no decimal places when whole numbers were returned.

We can format our decimal numbers to only use two decimal places by using the -f Format operator. In the example below, we take a number that has four decimal places and specify that it is formatted to only use two decimal places.

PS C:\> $x = 100.9851
PS C:\> $x
100.9851
PS C:\> '{0:N2}' -f $x
100.99 #We want this, and notice the rounding!

In the next example, we have a whole number. If we use the -f Format operator on this number it will also show two decimal places. This isn’t what we want – we want to leave whole numbers the way they are and not add any decimal places.

PS C:\> $x = 150
PS C:\> $x
150
PS C:\> '{0:N2}' -f $x
150.00 #We don't want this!

Formatting decimal numbers, and leaving whole numbers alone, is going to require some conditional logic (If-Else statement) along with modulus division. Modulus division returns the remainder of a division operation. Here are some examples using the division operator (/) and the modulus operator (%).

PS C:\> 10 / 2
5
PS C:\> 10 % 2
0
PS C:\> 10 / 3
3.33333333333333
PS C:\> 10 % 3
1
PS C:\> 10 / 7
1.42857142857143
PS C:\> 10 % 7
3

When zero is returned from a modulus division operation, it means that the division operation did not result in a remainder. This is a great way to handle whether or not a number should or should not be formatted. In the example below, the modulus division operation on line 3 returns zero. Since zero is equal (-eq) to zero, then $a is divided by $b on line 4.

$a = 10
$b = 2
If ($a % $b -eq 0) {
    $a / $b
} Else {
    '{0:N2}' -f ($a / $b)
}
5

In this example, the modulus division operation does not return zero. Therefore, the else statement is called on line 5. On line 6, $a is divided by $b and then formatted to two decimal places. The parenthesis around ($a / $b)  indicates to PowerShell to divide first, and then do the formatting.

$a = 10
$b = 3
If ($a % $b -eq 0) {
    $a / $b
} Else {
    '{0:N2}' -f ($a / $b)
}
3.33