How to split a date range into monthly subranges in PowerShell? - powershell

In Powershell, assuming:
$rangeFrom = "2020-03-01"
$rangeTo = "2020-05-13"
How could I obtain:
$monthRange[0] = "2020-03-01","2020-03-31"
$monthRange[1] = "2020-04-01","2020-04-30"
$monthRange[2] = "2020-05-01","2020-05-13"
Dates will be used in a loop as strings (from/to) on commands that do not support more than one month in range, such as:
myCommand -From $rangeFrom -To $rangeTo # keep this in one month range

By using DateTime objects you can solve the most trouble, like calculating the last day of a month or iterating over dates. You can use the following code to solve your problem:
$rangeFrom = "2019-12-15"
$rangeTo = "2020-05-13"
$monthRange = #()
$dateFrom = Get-Date $rangeFrom
$dateTo = Get-Date $rangeTo
$dateCur = Get-Date $dateFrom -Day 1
while ($dateCur -lt $dateTo) {
if (($dateCur.Year -eq $dateFrom.Year) -and ($dateCur.Month -eq $dateFrom.Month)) {
$dateBegin = $dateFrom # First month exception
} else {
$dateBegin = $dateCur
}
if (($dateCur.Year -eq $dateTo.Year) -and ($dateCur.Month -eq $dateTo.Month)) {
$dateEnd = $dateTo # Last month exception
} else {
$dateEnd = $dateCur.AddMonths(1).AddDays(-1)
}
$monthRange += [Tuple]::Create($dateBegin.toString('yyyy-MM-dd'), $dateEnd.toString('yyyy-MM-dd'))
$dateCur = $dateCur.AddMonths(1)
}
$monthRange
Output:
Item1 Item2 Length
----- ----- ------
2019-12-15 2019-12-31 2
2020-01-01 2020-01-31 2
2020-02-01 2020-02-29 2
2020-03-01 2020-03-31 2
2020-04-01 2020-04-30 2
2020-05-01 2020-05-13 2
You can access individual elements like this:
$monthRange[2].Item2
Output:
2020-02-29

Related

updating column value for exported csv powershell

I have following code which is working correctly.
Although I now need to modify the output in one specific column, so I can sort by this column correctly.
Here is my code:
$inputFile = "C:\Data\expPasswords\expPasswords.csv"
$outputFile = "C:\Data\expPasswords\expPasswordsUp.csv"
$result = Import-Csv $inputFile |
Select-Object #{ Name = 'Account'; Expression = { $_.Account } },
#{ Name = 'Days until Expiry'; Expression = { $_.'time until password expires' } },
#{ Name = 'Email address'; Expression = { $_.'email address' } }
# output on screen
$result | Sort-Object -Property 'Days until Expiry' | Format-Table -AutoSize
# output to csv
$result | Sort-Object -Property 'Days until Expiry' | Export-Csv -Path $outputFile -NoTypeInformation
I need to sort by the 'Days until Expiry' column. Although makes it hard when the output is as below:
0 minutes
0 minutes
1 day and 19 hours
1 day and 2 hours
1 day and 20 hours
1 day and 23 hours
13 hours
2 days
20 hours
Basically, what I would like to do is:
- If less than 1 day, make the value: Today
- Remove the hours and minutes blocks.
- So if it is 13 hours, make the value: Today
- If the value is 1 day and 1 hours and 35 minutes, make the value: 1 day
Any assistance will be greatly appreciated. ;-)
Its a shame you should spend time to make some sense out of this rather foolish output, but of course it can be done.
Basically, all you want to do is find out if the string starts with a number followed by the word 'day' or 'days' and cut off all the rest. If this is not the case, the returned value should be 'Today'.
The easiest way to do that I think is by using switch -Regex.
Try
$inputFile = "C:\Data\expPasswords\expPasswords.csv"
$outputFile = "C:\Data\expPasswords\expPasswordsUp.csv"
$result = Import-Csv $inputFile | ForEach-Object {
$daysLeft = switch -Regex ($_.'time until password expires') {
'^(\d+ days?)' { $matches[1] }
default { 'Today' }
}
[PsCustomObject]#{
'Account' = $_.Account
'Days until Expiry' = $daysLeft
'Email address' = $_.'email address'
}
} | Sort-Object -Property 'Days until Expiry'
# output on screen
$result | Format-Table -AutoSize
# output to csv
$result | Export-Csv -Path $outputFile -NoTypeInformation
Regex details:
^ Assert position at the beginning of the string
\d Match a single character that is a “digit” (any decimal number in any Unicode script)
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
\ day Match the character string “ day” literally (case sensitive)
s Match the character “s” literally (case sensitive)
? Between zero and one times, as many times as possible, giving back as needed (greedy)
Seeing your comment, I would suggest adding a real DateTime object to sort on.
Something like this:
$today = (Get-Date).Date
$result = Import-Csv 'D:\test.csv' | ForEach-Object {
$expiryString = $_.'time until password expires'
$expiryDate = $today
if ($expiryString -match '(\d+)\s*day') { $expiryDate = $expiryDate.AddDays([int]$matches[1]) }
if ($expiryString -match '(\d+)\s*hour') { $expiryDate = $expiryDate.AddHours([int]$matches[1]) }
if ($expiryString -match '(\d+)\s*minute') { $expiryDate = $expiryDate.AddMinutes([int]$matches[1]) }
if ($expiryString -match '(\d+)\s*second') { $expiryDate = $expiryDate.AddSeconds([int]$matches[1]) }
$daysLeft = if ($expiryDate.Date -eq $today) { 'Today' } else { ($expiryDate - $today).Days}
[PsCustomObject]#{
'Account' = $_.Account
'Email address' = $_.'email address'
'Days until Expiry' = $daysLeft
'Expiration Date' = $expiryDate
}
} | Sort-Object -Property 'Expiration Date'
# output on screen
$result
Output:
Account Email address Days until Expiry Expiration Date
------- ------------- ----------------- ---------------
User1 user1#yourcompany.com Today 6-4-2020 0:00:00
User6 user6#yourcompany.com Today 6-4-2020 0:03:00
User8 user8#yourcompany.com Today 6-4-2020 13:00:00
User4 user4#yourcompany.com Today 6-4-2020 20:00:00
User9 user9#yourcompany.com 1 7-4-2020 2:00:00
User2 user2#yourcompany.com 1 7-4-2020 19:00:00
User5 user5#yourcompany.com 1 7-4-2020 20:00:00
User7 user7#yourcompany.com 1 7-4-2020 23:00:00
User3 user3#yourcompany.com 2 8-4-2020 0:00:00
If you don't want that new property 'Expiration Date' in your output, simply filter it away with:
$result | Select-Object * -ExcludeProperty 'Expiration Date'
I think the following might be of help (you will need to edit some of it, off course):
$Timings = #("0 minutes","0 minutes","1 day and 19 hours","1 day and 2 hours","1 day and 20 hours","1 day and 23 hours","13 hours","2 days","20 hours")
foreach ($Timing in $Timings) {
$Output = $null
if ($Timing -like "* minutes") {$Output = 0}
elseif ($Timing -like "* Day and * hours") {$Output = [int](($Timing).Split(' day')[0])}
elseif ($Timing -like "* hours") {$Output = 0}
else {$Output = [int](($Timing).Split(' day')[0]) }
switch ($Output) {
0 {$Result = "Today"}
1 {$Result = "Tomorrow"}
default {$Result = "Over $Output Days"}
}
Write-Output "$timing ==> $Result"
}
The constrains you defined will likely make it more confusing. I would just convert it to a [TimeSpan] structure which makes it easy to sort:
$Result = ConvertFrom-Csv #'
"Account","Days until Expiry", "Email address"
"Account1","0 minutes", "Name1#gmail.com"
"Account2","1 day and 19 hours","Name2#gmail.com"
"Account3","2 days", "Name3#gmail.com"
"Account4","20 hours", "Name4#gmail.com"
"Account5","1 day and 20 hours","Name5#gmail.com"
"Account6","3 minutes", "Name6#gmail.com"
"Account7","1 day and 23 hours","Name7#gmail.com"
"Account8","13 hours", "Name8#gmail.com"
"Account9","1 day and 2 hours", "Name9#gmail.com"
'#
Function ConvertTo-TimeSpan([String]$String) {
$Days = If ($String -Match '\d+(?=\s*day)') {$Matches[0]} Else {0}
$Hours = If ($String -Match '\d+(?=\s*hour)') {$Matches[0]} Else {0}
$Minutes = If ($String -Match '\d+(?=\s*minute)') {$Matches[0]} Else {0}
$Seconds = If ($String -Match '\d+(?=\s*second)') {$Matches[0]} Else {0}
New-TimeSpan -Days $Days -Hours $Hours -Minutes $Minutes -Seconds $Seconds
}
$Result | Sort #{e = {ConvertTo-TimeSpan $_.'Days until Expiry'}}
Result:
Account Days until Expiry Email address
------- ----------------- -------------
Account1 0 minutes Name1#gmail.com
Account6 3 minutes Name6#gmail.com
Account8 13 hours Name8#gmail.com
Account4 20 hours Name4#gmail.com
Account9 1 day and 2 hours Name9#gmail.com
Account2 1 day and 19 hours Name2#gmail.com
Account5 1 day and 20 hours Name5#gmail.com
Account7 1 day and 23 hours Name7#gmail.com
Account3 2 days Name3#gmail.com

The months difference between dates using PowerShell

I need to have AD User Account Expiration Date and now how many months and date its remain until will be disabled.
I tried the code under but I am getting in the months 1 and I have less than one month I would like to have answer like 0 month and 27 days
$StartDate (DateNow)
2019-08-29 00:00:00
AccountExpirationDate
---------------------
2019-09-26 00:00:00
$ExpirDate = Get-ADUser test111 -Properties AccountExpirationDate | select AccountExpirationDate
AccountExpirationDate
---------------------
2019-09-26 00:00:00
$EndDate= $ExpirDate.AccountExpirationDate
2019-09-26 00:00:00
$StartDate = (GET-DATE)
2019-08-29 00:00:00
NEW-TIMESPAN –Start $StartDate –End $EndDate
Days : 27
Hours : 10
Minutes : 29
Seconds : 56
$monthdiff = $EndDate.month - $StartDate.month + (($EndDate.Year - $StartDate.year) * 12)
1
(Here I got the number 1 but I have less than one month)
I found no easy way to do this in PowerShell (as TimeSpan doesn't support month counting), hence I ended up with the following. Starting with the years of the two dates, take their difference and course correct if the start day hasn't passed in the current year. Then do the same with the months:
$StartDate = [DateTime]'2021-01-23'
$today = Get-Date
$daydiff = New-TimeSpan -Start $StartDate -End $today
$yeardiff = $today.year - $StartDate.year
If($yeardiff -gt 0 -And $StartDate.month -gt $today.month
-And $StartDate.day -gt $today.day) {
$yeardiff = $yeardiff -1
}
$monthdiff = $today.month - $StartDate.month + ($yeardiff * 12)
If($StartDate.day -gt $today.day) { $monthdiff = $monthdiff -1 }
Write-Host "$($daydiff.days) days | $($monthdiff) months"
Simplest solution I could work out.
I get the number of months between the start date and the monthly anniversary day in the current month. Then adjust:
$start_date = Get-Date '2022-02-27 21:00'
$end_date = Get-Date
# get the monthly anniversary of the $start_date in the current month
$this_month_anniversary = Get-Date ('{0}-{1}-{2} {3:d2}:{4:d2}' -f $end_date.Year, $end_date.Month, $start_date.Day, $start_date.Hour, $start_date.Minute)
# get the number days in the month, so we can get a denominator when figuring the percent of the way we are towards the next anniversay.
# Which month? If we're past this month's anniversary use the current month. If we haven't reached it yet, use the previous month
if ($end_date -gt $this_month_anniversary) {
$days_in_month = [DateTime]::DaysInMonth($end_date.Year, $end_date.Month)
} else {
$last_month = (Get-Date ('{0}-{1}-01' -f $end_date.Year, $end_date.Month)).AddDays(-1)
$days_in_month = [DateTime]::DaysInMonth($last_month.Year, $last_month.Month)
}
# get months between the start date and this month's anniversay, then
# adjust for the current month, this will be negative if the anniversay hasn't occured yet, otherwise positive
($this_month_anniversary.Month - $start_date.month + (($this_month_anniversary.Year - $start_date.year) * 12) +
(New-TimeSpan -Start $this_month_anniversary -End $end_date).TotalDays / $days_in_month)
I believe this is what you want, or can be tweaked to achieve it relatively easily.
$today = Get-Date;
$endOfYearDate = "12/31/$($today.Year)";
$endOfYear = Get-Date($endOfYearDate);
$monthsLeftInTheYear = ($endOfYear.Month - $today.Month);
$daysLeftInTheYear = ($endOfYear - $today);
$daysLefInTheYear.Days;
You can just do normal arithmetic on dates, but if there are no months, it will return $null not 0.
$today - date
$ExpirDate = Get-ADUser test111 -Properties AccountExpirationDate | select AccountExpirationDate
$diffday = $today - $expirDate
$diffday.days
$diffday.months
if ($diffday.months -eq $null)
{
$Diffday.months =0
}

Get Approximate Date from Week Number

I'm getting information on monitors from WMI. There are properties called "WeekOfManufacture" and "YearOfManufacture". In this case it's:
Week : 24
Year : 2009
I'm trying to get the approximate date corresponding to that.
I'm doing this to get a really rough estimate:
(Get-Date -Date $YearOfManufacture/01/01).AddDays(7*$WeekOfManufacture)
But obviously the "week" doesn't necessarily always have 7 days.
What is the best approach?
The complete code I'm using right now is:
$Monitors = Get-WmiObject WmiMonitorID -Namespace root\wmi
foreach ($Monitor in $Monitors) {
$Manufacturer = ($Monitor.ManufacturerName -notmatch 0 | ForEach{[char]$_}) -join ""
$Code = ($Monitor.ProductCodeID -notmatch 0 | ForEach{[char]$_}) -join ""
$Name = ($Monitor.UserFriendlyName -notmatch 0 | ForEach{[char]$_}) -join ""
$Serial = ($Monitor.SerialNumberID -notmatch 0 | ForEach{[char]$_}) -join ""
$WeekOfManufacture = $Monitor.WeekOfManufacture
$YearOfManufacture = $Monitor.YearOfManufacture
$DateManufacture = (get-date -Date $YearOfManufacture/01/01).AddDays(7*$WeekOfManufacture)
"$Manufacturer - $Code - $Name - $Serial - $DateManufacture"
}
which returns something like (info obfuscated):
SAM - 1234 - SycMastr - XXXX - 06/18/2009 00:00:00
SAM - 1234 - SycMastr - XXXX - 09/10/2009 00:00:00
Answer from Technet works perfectly
(https://social.technet.microsoft.com/Forums/en-US/f65c80b0-f74f-4234-870c-c5ffe8d9b1ea/powershell-get-date-from-week-number-of-year?forum=ITCG)
:
Function FirstDateOfWeek
{
param([int]$year, [int]$weekOfYear)
$jan1 = [DateTime]"$year-01-01"
$daysOffset = ([DayOfWeek]::Thursday - $jan1.DayOfWeek)
$firstThursday = $jan1.AddDays($daysOffset)
$calendar = ([CultureInfo]::CurrentCulture).Calendar;
$firstWeek = $calendar.GetWeekOfYear($firstThursday, [System.Globalization.CalendarWeekRule]::FirstFourDayWeek, [DayOfWeek]::Monday)
$weekNum = $weekOfYear
if($firstweek -le 1) { $weekNum -= 1 }
$result = $firstThursday.AddDays($weekNum * 7)
return $result.AddDays(-3)
}
FirstDateOfWeek -year 2009 -weekOfYear 24
8 juin 2009 00:00:00

Get random items from hashtable but the total of values has to be equal to a set number

I'm trying to build a simple "task distributor" for the house tasks between me and my wife. Although the concept will be really useful at work too so I need to learn it properly.
My hashtable:
$Taches = #{
"Balayeuse plancher" = 20
"Moppe plancher" = 20
"Douche" = 15
"Litières" = 5
"Poele" = 5
"Comptoir" = 5
"Lave-Vaisselle" = 10
"Toilette" = 5
"Lavabos" = 10
"Couvertures lit" = 5
"Poubelles" = 5
}
The total value for all the items is 105 (minutes).
So roughly 50mins each of we split it in two.
My goal:
I want to select random items from that hashtable and build two different hashtables - one for me and my wife, each having a total value of 50 (So it's fair). For example 20+20+10 or 5+5+5+15+20, etc. The hard part is that ALL tasks have to be accounted for between the two hashtables and they can only be present ONCE in each of them (no use in cleaning the same thing twice!).
What would be the best option?
For now I successfully achieved a random hashtable of a total value of 50 like this:
do {
$Me = $null
$sum = $null
$Me = #{}
$Me = $Taches.GetEnumerator() | Get-Random -Count 5
$Me | ForEach-Object { $Sum += $_.value }
} until ($sum -eq 50)
Result example :
Name Value
---- -----
Poubelles 5
Balayeuse plancher 20
Douche 15
Poele 5
Toilette 5
It works but boy does it feel like it's a roundabout and crooked way of doing it. I'm sure there is a better approach? Plus I'm lacking important things. ALL the tasks have to be accounted for and not be present twice. This is quite complicated although it looked simple at first!
You can not maximise randomness and fairness at the same time so one has to give. I think you should not risk being unfair to your wife and so fairness must prevail!
Fairness at the expense of randomness
This approach sorts the items in descending time order and then randomly assigns them items to each person unless that assignment would be unfair.
The fairness calculation here is that the maximum time difference should be at most the duration of the quickest task.
$DescendingOrder = $Taches.Keys | Sort-Object -Descending { $Taches[$_] }
$Measures = $Taches.Values | Measure-Object -Sum -Minimum
$UnfairLimit = ($Measures.Sum + $Measures.Minimum) / 2
$Person1 = #{}
$Person2 = #{}
$Total1 = 0
$Total2 = 0
foreach ($Item in $DescendingOrder) {
$Time = $Taches[$Item]
$Choice = Get-Random 2
if (($Choice -eq 0) -and (($Total1 + $Time) -gt $UnfairLimit)) {
$Choice = 1
}
if (($Choice -eq 1) -and (($Total2 + $Time) -gt $UnfairLimit)) {
$Choice = 0
}
if ($Choice -eq 0) {
$Person1[$Item] = $Time
$Total1 += $Time
} else {
$Person2[$Item] = $Time
$Total2 += $Time
}
}
An example run:
PS> $Person1 | ConvertTo-Json
{
"Comptoir": 5,
"Lavabos": 10,
"Litières": 5,
"Couvertures lit": 5,
"Douche": 15,
"Lave-Vaisselle": 10
}
and the other person:
PS> $Person2 | ConvertTo-Json
{
"Moppe plancher": 20,
"Toilette": 5,
"Balayeuse plancher": 20,
"Poubelles": 5,
"Poele": 5
}
Randomness at the expense of fairness
This approach is to randomize the list, go through each item and then assign it to the person who has the least time allocated to them so far.
Earlier decisions might mean that later decisions end up being unfair.
$RandomOrder = $Taches.Keys | Sort-Object { Get-Random }
$Person1 = #{}
$Person2 = #{}
$Total1 = 0
$Total2 = 0
foreach ($Item in $RandomOrder) {
$Time = $Taches[$Item]
if ($Total1 -lt $Total2) {
$Person1[$Item] = $Time
$Total1 += $Time
} else {
$Person2[$Item] = $Time
$Total2 += $Time
}
}
An example run:
PS> $Person1 | ConvertTo-Json
{
"Poele": 5,
"Douche": 15,
"Couvertures lit": 5,
"Lave-Vaisselle": 10,
"Balayeuse plancher": 20,
"Toilette": 5
}
and the other person:
PS> $Person2 | ConvertTo-Json
{
"Lavabos": 10,
"Comptoir": 5,
"Poubelles": 5,
"Litières": 5,
"Moppe plancher": 20
}
You should probably write the algorithm to always have you take the extra task in a rounding error (Happy Wife, Happy Life).
This is probably over-engineered, but I was intrigued by the question, and learned some French in the process.
$Taches = #{
"Balayeuse plancher" = 20
"Moppe plancher" = 20
"Douche" = 15
"Litières" = 5
"Poele" = 5
"Comptoir" = 5
"Lave-Vaisselle" = 10
"Toilette" = 5
"Lavabos" = 10
"Couvertures lit" = 5
"Poubelles" = 5
}
$target = 0
$epsilon = 5
# copy if you don't want to destroy original list (not needed probably)
# put all entries in first list.
# randomly move entry to p2 if count over target +/- epsilon
# randomly move entry from p2 if count under target +/- epsilon
# (unless you know you can always get exactly target and not loop forever trying)
$p1 = #{} # person 1
$p2 = #{} # person 2
$p1Total = 0 # optimizaton to not have to walk entire list and recalculate constantly
$p2Total = 0 # might as well track this too...
$Taches.Keys | % {
$p1.Add($_, $Taches[$_])
$p1Total += $Taches[$_]
$target += $Taches[$_]
}
$target = $target / 2
$done = $false
while (-not $done)
{
if ($p1Total -gt ($target+$epsilon))
{
$item = $p1.Keys | Get-Random
$value = $p1[$item]
$p1.Remove($item)
$p2.Add($item, $value)
$p1Total -= $value
$p2Total += $value
continue
}
elseif ($p1Total -lt ($target-$epsilon))
{
$item = $p2.Keys | Get-Random
$value = $p2[$item]
$p2.Remove($item)
$p1.Add($item, $value)
$p1Total += $value
$p2Total -= $value
continue
}
$done = $true
}
"Final result"
"p1"
$p1Total
$p1
"`np2"
$p2Total
$p2
Yet another approach:
$MinSum = ($Taches.Values | Measure-Object -Minimum ).Minimum
$HalfSum = ($Taches.Values | Measure-Object -Sum ).Sum / 2
do {
$sum = 0
$All = $Taches.GetEnumerator() |
Get-Random -Count $Taches.Keys.Count
$Me = $All | ForEach-Object {
if ( $Sum -lt $HalfSum - $MinSum ) {
$Sum += $_.value
#{ $_.Key = $_.Value }
}
}
Write-Host "$sum " -NoNewline # debugging output
} until ($sum -eq 50 )
$Em = $Taches.Keys | ForEach-Object {
if ( $_ -notin $Me.Keys ) {
#{ $_ = $Taches.$_ }
}
}
# show "fairness" (task count vs. task cost)
$Me.Values | Measure-Object -Sum | Select-Object -Property Count, Sum
$Em.Values | Measure-Object -Sum | Select-Object -Property Count, Sum
Sample output(s):
PS D:\PShell> D:\PShell\SO\54610011.ps1
50
Count Sum
----- ---
4 50
7 55
PS D:\PShell> D:\PShell\SO\54610011.ps1
65 65 50
Count Sum
----- ---
6 50
5 55
Great answers guys, learned a lot. Here is what I ended up doing thanks to "Fischfreund" on Reddit (https://www.reddit.com/r/PowerShell/comments/aovs8s/get_random_items_from_hashtable_but_the_total_of/eg3ytds).
His approach is amazingly simple yet I didn't think of it at all.
First hashtable : Get a random count of 5 until the sum is 50. Then create a second hashtable where the items are not in the first hashtable! I assign that first hahstable containing 5 items to my wife so I'm the one who always has an extra task (like suggested by Kory ;)). Phew i'm safe.
$Taches = #{
"Balayeuse plancher" = 20
"Moppe plancher" = 20
"Douche" = 15
"Litières" = 5
"Poele" = 5
"Comptoir" = 5
"Lave-Vaisselle" = 10
"Toilette" = 5
"Lavabos" = 10
"Couvertures lit" = 5
"Poubelles" = 5
}
do {
$Selection1 = $Taches.GetEnumerator() | Get-Random -Count 5
} until (($Selection1.Value | measure -Sum ).Sum -eq 50)
$Selection2 = $Taches.GetEnumerator() | Where-Object {$_ -notin $Selection1}
$Selection1 | select-object #{Name="Personne";expression={"Wife"} },Name,Value
""
$Selection2 | select-object #{Name="Personne";expression={"Me"} },Name,Value

Selecting items in CSV file using Date

I am trying to select items by date in a CSV file using PowerShell. The format in the CSV file for the date is 1/8/2018 10:04:00 AM. When I run this I get no data although I know that data exists.
$events = Import-Csv c:\normtest\server2_perf.csv | foreach {
New-Object PSObject -prop #{
Date = [DateTime]::Parse($_.Date);
CPULoad = $_.CPULoad;
MemLoad = $_.Memload
}
}
$events | Where { $_.Date -eq (Get-Date).AddDays(-4) }
As you have a time part to your date, this will only work for exactly 4 days from now (i.e. where time of day = right now).
Assuming this part works correctly: Date = [DateTime]::Parse($_.Date);, you can do this:
$start = (Get-Date).Date.AddDays(4)
$fin = $start.AddDays(1) # assuming 1 day window
$events |
Where {$_.Date -gt $start -and $_.Date -lt $fin}
Alternatively, you could treat the date field as string:
$events = Import-Csv c:\normtest\server2_perf.csv |
Where {$_.Date -like "$(Get-Date).AddDays(-4).ToString("M/d/yyyy"))*" }
Assuming your date format is "M/d/yyyy"