Get last business day factoring in holidays in Powershell - powershell

I have the following Powershell script that works fine to get the last business/working days, however I am trying to factor in (US) holidays. Can that be done by improving the following script?
$DateOffset = If ((get-date).dayofweek.value__ -eq 1) {-3} Else {-1}

You could do something like this:
function Get-LastBusinessDay {
[CmdletBinding()]
param (
[Parameter(Position = 1)]
[System.DateTime]$Date = [System.DateTime]::Today,
[Parameter(Position = 2)]
[System.DateTime[]]$Holidays
);
$Weekends = #([System.DayOfWeek]::Saturday, [System.DayOfWeek]::Sunday);
$LastBusinessDay = $Date.AddDays(-1);
while (($LastBusinessDay.DayOfWeek -in $Weekends) -or ($LastBusinessDay.Date -in $Holidays)) {
$LastBusinessDay = $LastBusinessDay.AddDays(-1);
}
return $LastBusinessDay;
}
$HolidayTable = #(
# 2018 US Federal Holidays
'January 1, 2018',
'January 15, 2018',
'February 19, 2018',
'May 28, 2018',
'July 4, 2018',
'September 3, 2018',
'October 8, 2018',
'November 12, 2018',
'November 22, 2018',
'December 25, 2018'
);
Get-LastBusinessDay -Date '2018-12-26' -Holidays $HolidayTable
# Returns December 24
Get-LastBusinessDay -Date '2018-09-04' -Holidays $HolidayTable
# Returns Aug 31
The list of holidays can come from anywhere.

Holidays can be difficult to work with when calculating workdays. I use a script from Alan Kaplan that pulls holidays from TimeAndDate.com for any year and any country as PowerShell object.
With that, it should be easy to adjust the calculation as needed.
#Example. Get US National Holidays for 2015, display in two columns with Holiday and Date with day.
$holidays =Get-Holidays 2015
#$holidays now is an object with the holidays for 2015, which you can manipulate
$holidays | where {$_.type -like "National Holiday"} |
select #{Name="Holiday"; Expression = {$_.holidayName}}, #{Name="Date"; Expression = {Get-Date($_.date) -format D}} |
ft -AutoSize

Related

lastquarter, previousquarter and prioryear - PowerShell Functions

I am running below to get
#lastquarter - SELECT DATEADD(dd, -1, DATEADD(qq, DATEDIFF(qq, 0, GETDATE()), 0)), 23 - Returns 2021-12-31 00:00:00.000
#previousquarter - SELECT DATEADD(qq, -1,DATEADD(dd, -1, DATEADD(qq, DATEDIFF(qq, 0, GETDATE()), 0))), 23 - Returns 2022-12-31 00:00:00.000
#prioryear - SELECT DATEADD(yy, -1,DATEADD(dd, -1, DATEADD(qq, DATEDIFF(qq, 0, GETDATE()), 0))), 23; - Returns 2022-09-30 00:00:00.000
How would I write this in Powershell to get above?
Does it have any functions I can use. I looked into get-date but it seemes getting above in PowerShell is not as straightforward
Would appreciate some assistance with building up the logic in PS
Here's some expressions that give the values you're after. I've not tested them extensively, so you might want to write some Pester tests or something to validate them with edge cases, etc...
It also assumes you don't care about the time part - you might need to clear that in the $quarterStart expression if that matters...
$timestamp = [datetime] "2022-01-12";
write-host "timestamp = $timestamp";
# timestamp = 01/12/2022 00:00:00
# find the start of the *current* quarter - it's easier and
# clearer to work out the others from this interim value
$quarterStart = $timestamp.AddDays(-$timestamp.Day + 1).AddMonths(-($timestamp.Month-1) % 3);
write-host "quarter start = $quarterStart";
# quarter start = 01/01/2022 00:00:00
# end of the quarter before the current one
# (i.e. one day before the current quarter start)
$lastQuarter = $quarterStart.AddDays(-1);
write-host "last quarter = $lastQuarter";
# last quarter = 12/31/2021 00:00:00
# end of the quarter before the last one
# (i.e. three months and one day before the current quarter start)
$previousQuarter = $quarterStart.AddMonths(-3).AddDays(-1);
write-host "previous quarter = $previousQuarter";
# previous quarter = 09/30/2021 00:00:00
# end of the previous year
# (i.e. 31st December of the previous year)
$lastYear = new-object DateTime(($timestamp.Year - 1), 12, 31);
write-host "last year = $lastYear";
# last year = 12/31/2021 00:00:00

Powershell Split PSCustomObject if Property has multiple values while duplicating other values

My current PSCustomObject
Invoice
Auction
Item ID
Date
18585208
X8C3E5651
K5W6L07795
Feb 18, 2021
99457377
U9V7E3466
J2X1D40777
Feb 14, 2022
91833688
D2A7O0545
T7Z6Y74627,H3B4U81837
Feb 23, 2021
76058226
K3N0T6688
G1D0B37486,Q3B8Z87471
Aug 21, 2021
65314754
C1C1F6626
Q8D2J82095
Dec 9, 2021
Rows 3 and 4 have multiple Item ID's which need to be split into their own rows but with the Invoice, Auction, and Date properties duplicated to match.
Intended output:
Invoice
Auction
Item ID
Date
18585208
X8C3E5651
K5W6L07795
Feb 18, 2021
99457377
U9V7E3466
J2X1D40777
Feb 14, 2022
91833688
D2A7O0545
T7Z6Y74627
Feb 23, 2021
91833688
D2A7O0545
H3B4U81837
Feb 23, 2021
76058226
K3N0T6688
G1D0B37486
Aug 21, 2021
76058226
K3N0T6688
Q3B8Z87471
Aug 21, 2021
65314754
C1C1F6626
Q8D2J82095
Dec 9, 2021
After a ton of research, I've tried multiple things but the logic is getting lost on me. I can select by property name and split each but have trouble on the new line portion.
My Object code:
$data = foreach ($foo in $AllMatches)
{
[PSCustomObject]#{
'Invoice' = ($data.Groups.Where{$_.Name -like 'Invoice'}).Value
'Auction' = ($data.Groups.Where{$_.Name -like 'Auction'}).Value
'Items' = ($data.Groups.Where{$_.Name -like 'Items'}).Value
'Date' = ($data.Groups.Where{$_.Name -like 'Date'}).Value
}
}
Your code is a bit unclear, but if you already have an array of PsObjects and need to split on a certain property, here's one way of doing that.
For demo, I'm using your example data as Here-String and convert that from CSV:
$dataToSplit = #"
Invoice Auction Item ID Date
18585208 X8C3E5651 K5W6L07795 Feb 18, 2021
99457377 U9V7E3466 J2X1D40777 Feb 14, 2022
91833688 D2A7O0545 T7Z6Y74627,H3B4U81837 Feb 23, 2021
76058226 K3N0T6688 G1D0B37486,Q3B8Z87471 Aug 21, 2021
65314754 C1C1F6626 Q8D2J82095 Dec 9, 2021
"# | ConvertFrom-Csv -Delimiter "`t"
$data = foreach ($item in $dataToSplit) {
$item.'Item ID' -split ',' | ForEach-Object {
[PSCustomObject]#{
'Invoice' = $item.Invoice
'Auction' = $item.Auction
'Item ID' = $_
'Date' = $item.Date
}
}
}
$data
Output:
Invoice Auction Item ID Date
------- ------- ------- ----
18585208 X8C3E5651 K5W6L07795 Feb 18, 2021
99457377 U9V7E3466 J2X1D40777 Feb 14, 2022
91833688 D2A7O0545 T7Z6Y74627 Feb 23, 2021
91833688 D2A7O0545 H3B4U81837 Feb 23, 2021
76058226 K3N0T6688 G1D0B37486 Aug 21, 2021
76058226 K3N0T6688 Q3B8Z87471 Aug 21, 2021
65314754 C1C1F6626 Q8D2J82095 Dec 9, 2021
Here you have an example of how you can do it, the following iterates over each object of the array and splits by comma the value of Item ID, if result is one elements it returns the element as is, else, it will iterate over the elements of splitting and update a copy of the row the current row.
foreach($obj in $array) {
if(($items = $obj.'Item ID'.Split(',')).Count -eq 1) {
$obj
continue
}
foreach($item in $items) {
$i = $obj.PSObject.Copy()
$i.'Item ID' = $item
$i
}
}

Powershell - Get date from week and weekday

I have a file containing 'year', 'week-number', and 'weekday', (example - '2022', '42', '4',) 42 represents week 42 of 2022, and the 4 represents tuesday. A 7 would represent sunday.
The first week is to be considered the first days until sunday, regardless if it is 7 days or not. Same principle for the last week. That week can also contain fewer than 7 days.
How can i translate this to a date like 'yyyyMMdd'?
Thanks
Note:
The following uses .NET's System.DayOfWeek enumeration to identify weekdays, which means that Sunday is 0, Monday is 1, ..., and Saturday is 6.
If your weekday-numbering scheme differs (which is what it sounds like), you'll have to map it onto the above.
# Convert the strings (representing CSV input) to numbers.
[int] $year, [int] $week, [int] $weekday = '2022', '42', '4'
# The day of the week you consider the start of a calendar week.
$startOfWeekDay = [DayOfWeek]::Monday
# Calculate the start of calendar week 1 for the given year:
# The Monday on or preceding Jan 1
# Note: This date may therefore fall into the previous year.
$Jan1 = [datetime]::new($year, 1, 1)
$StartOfWeek1 = $Jan1.AddDays(-((7 + $Jan1.DayOfWeek-$startOfWeekDay) % 7))
# Calculate the start of the target calendar week.
$StartOfTargetWeek = $StartOfWeek1.AddDays($week * 7)
# Calculate the target date by determining the desired weekday
# inside the target week.
$TargetDate = $StartOfTargetWeek.AddDays((7 + $weekday-$startOfWeekDay) % 7)
$TargetDateString = $TargetDate.ToString('yyyyMMdd')
# Output *for display* both the intermediate results and the final one.
[pscustomobject] #{
Jan1 = ($Jan1 | Out-String).Trim()
StartOfWeek1 = ($StartOfWeek1 | Out-String).Trim()
StartOfTargetWeek = ($StartOfTargetWeek | Out-String).Trim()
TargetWeekDay = [DayOfWeek] $weekday
TargetDate = ($TargetDate | Out-String).Trim()
TargetDateString = $TargetDateString
} | Format-List
Output:
Jan1 : Saturday, January 1, 2022 12:00:00 AM
StartOfWeek1 : Monday, December 27, 2021 12:00:00 AM
StartOfTargetWeek : Monday, October 17, 2022 12:00:00 AM
TargetWeekDay : Thursday
TargetDate : Thursday, October 20, 2022 12:00:00 AM
TargetDateString : 20221020

Generate Series of month and years

How do I generate a series of month and year dates which I can use as a parameter to pass to other data tables.
For example, I am currently using:
MonthYearSelector = GENERATESERIES(DATE(2019,1,1), DATE(2019,12,1),30)
However, this generates the following dates:
01 January 2019
31 January 2019
02 March 2019
...
What I want is the following list:
01 January 2019
01 February 2019
01 March 2019
....
MonthYearSelector = GENERATESERIES(DATE(2019,1,1), DATE(2019,12,1),30)
How about something like this?
MonthYearSelector =
VAR Years = SELECTCOLUMNS( {2018, 2019}, "Year", [Value] )
VAR Months = SELECTCOLUMNS( GENERATESERIES(1, 12, 1), "Month", [Value] )
RETURN
ADDCOLUMNS(
CROSSJOIN( Years, Months ),
"Date", DATE( [Year], [Month], 1 )
)
In the above, I use SELECTCOLUMNS just to rename the default Value column name.
Try this out.. It's basically give you start of the month & End of the Month... your choice to use..
Start of Month Dates =
GENERATE (
GENERATESERIES(1,48),
VAR inc = [Value]
RETURN ROW (
"date", DATE(2017,inc,1),
"Month End",EOMONTH(DATE(2017,inc,1),0)
)
)
Check this for details

how to compare two custom dates in powershell v2.0

Is it possible to compare 2 custom dates. Am trying check if variables hold date1 is lessthan date2, if so, report saying date1 is older date.
I getting both dates from a. date1 from log file and date2 from application itself
now, both date1 and date2 are in required format ie,
$Date1 = Tue,Aug 16, 2016 12:40:03
$Date2 = Mon,Aug 22, 2016 16:33:02
my next step is compare these 2 dates and report if date1 is older date compare to Date2, which I don't know how to proceed.. Any help/ideas is much appreciated.
Thanks to Pete and Ansgar Wiechers
updated working Code :
$Date1DateTime = [DateTime]::ParseExact($Date1,'ddd,MMM d, yyyy, HH:mm:ss',[Globalization.CultureInfo]::InvariantCulture); $Date2DateTime = [DateTime]::ParseExact($Date2,'ddd,MMM d, yyyy, HH:mm:ss',[Globalization.CultureInfo]::InvariantCulture); $Date1DateTime -lt $Date2DateTime
You can only compare date strings if the string sort order is the same as the date sort order. For instance, date strings in ISO format are comparable:
2016-08-16T12:40:03
2016-08-22T16:33:02
Date strings in your custom format are not, because T comes after M, but August 16 should actually come before August 22:
Tue,Aug 16, 2016 12:40:03
Mon,Aug 22, 2016 16:33:02
If you don't have the date strings in ISO format it's usually better to parse them into actual DateTime values (as #PetSerAl suggested), particularly if your reference value is originally a DateTime anyway.
$fmt = 'ddd,MMM d, yyyy, HH:mm:ss'
$culture = [Globalization.CultureInfo]::InvariantCulture
$Date1 = Get-Date $LogFileDate
$val = (b2b.exe -readparams $param | Select-Object -Skip 1 -First 1) -split '='
$Date2 = [DateTime]::ParseExact($val[1], $fmt, $culture)
if ($Date1 -lt $Date2) {
...
}