Why do my DateTime objects behave differently? - powershell

I have a script that checks for certain logs between two times $startDate and $endDate using Search-UnifiedAuditLog where $startDate is found by checking a line in a .txt file and $endDate is the current time. I run this as shown below.
$startDate = Get-Content $logPath -Last 1
$startDate = [datetime]$startDate
$startDate = $startDate.AddHours(-1)
$endDate = (Get-Date)
This particular script is run every hour, so the time between $startDate and $endDate is two hours (due to the AddHours). If I check the value of these variables, they are indeed two hours apart. However, when I run the script, it goes through the previous 6 hours of logs. This makes me think it is assuming that $startDate is in UTC, and it is converting it to my time zone. Is this what it is doing, and if so, how can I get my script to only check for logs one hour before the time listed in my .txt document?

Turning my comment into an answer
You can check the .Kind property of the $startDate variable.
This property can be either Local, Utc or Unspecified. See DateTimeKind Enum.
In case of Unspecified, since .NET 2.0, "This instance of DateTime is assumed to be a UTC time, and the conversion is performed as if Kind were Utc." as stated in the docs

Related

Powershell Script to archive files over 1 day

I am looking for a script that can ignore the timing and utilise just the date to move files after 1 day, so yesterday, to an archive folder.
My knowledge of powershell is not great so any advice on how i can do this would be great.
Everyday i run a script that generates a .txt report which has a filename .....2022 01 02 (The filename ends with the date) so would like to add some extra lines that archives the .txts that were created yesterday to an archive folder.
The [datetime] type has a Date property that gives you the date at midnight, thereby allowing you to compare dates without taking the time component into account:
# Construct datetime value describing yesterday at midnight
$yesterday = (Get-Date).AddDays(-1).Date
# Filter files based on date of the `CreationTime` property
$filesCreatedYesterday = Get-ChildItem -Path .\path\to\folder -File |Where-Object { $_.CreationTime.Date -eq $yesterday }
$filesCreatedYesterday will now contain the files created yesterday

Formatting a UTC time

I am looking to get the current UTC time, add 2 hours to it, then format both in the UTC format, so I can store that data in an XML file. Ultimate goal is then to get current UTC time on another machine, that could be in another time zone, and compare to the incremented time. So I can conditionally do stuff if two hours hasn't passed.
What I have is this
$now = Get-Date
$enqueued = $now.ToUniversalTime().ToString('u')
$expires = $now.Addhours(2).ToUniversalTime().ToString('u')
Write-Host "Q: $enqueued"
Write-Host "E: $expires"
$enqueued is formatted correctly, but $expires is not. I get
Q: 2020-06-02 18:41:58Z
E: 06/02/2020 22:41:58
And I expect
Q: 2020-06-02 18:41:58Z
E: 2020-06-02 20:41:58Z
I tried swapping .Addhours(2) & .ToUniversalTime() but it still doesn't format at all.
Interestingly
$now = (Get-Date).Addhours(2).ToUniversalTime().ToString('u')
$now
works right. So it's not the adding of hours alone. It's more like, once it's a variable the ability to add hours AND format is lost. Does that make any sense?
I also tried moving the UTC part to the $now assignment, since it was duplicated before.
$now = (Get-Date).ToUniversalTime()
$enqueued = $now.ToString('u')
$expires = $now.Addhours(2).ToString('u')
Write-Host "Q: $enqueued"
Write-Host "E: $expires"
Same results, $expires won't format.

How does PowerShell handle ((Get-Date).Month)-1 in January?

I wrote a Powershell/Robocopy backup script a few months back. It creates a folder for the backups with the year and month that the backups were taken (ex: 2019 11). The month always has to be one less because the script runs on the first of each new month. Everything has been smooth sailing but I just realized that I'm not really sure how the script will behave come January 1st. Does anyone have any incite as to what the output will be on January 1st, and is there a way for me to test this to confirm?
$month = (Get-Date -UFormat "%Y") + ' ' + ((((Get-Date).Month) - 1)).ToString()
# When run on November 1st, it creates a folder for the October backups called "2019 10".
# When run on December 1st, it creates a folder for the November backups called "2019 11".
When run on January 1st, what will it name the folder for the December backups? Will it be called "2019 12"? "2019 00"? Is there a way for me to easily test the behavior that relies on time, without manually adjusting my PC's calendar?
Get-Date optionally accepts a date to operate on (via -Date or positionally), which defaults to the current point in time.
Additionally, you can use -Day to modify the day-of-the-month part of the target date (as well as -Month and -Year, analogously); passing -Day 1 returns the date of the first of the month that the target date falls into.
Calling .AddDays(-1) on the resulting date is then guaranteed to fall in the previous month (it returns the previous month's last day).
The .ToString() method of System.DateTime allows you to perform custom string formatting of a date, using custom date and time format strings.
To put it all together:
# PRODUCTION USE:
# The reference date - now.
$now = Get-Date
# OVERRIDE FOR TESTING:
# Set $now to an arbitrary date, 1 January 2020 in this case.
# Note: With this syntax, *month comes first*, irrespective or the current culture.
$now = [datetime] '1/1/2020'
# Get the first day of the month of the date in $now,
# subtract 1 day to get the last day of the previous month,
# then use .ToString() to produce the desired format.
(Get-Date $now -Day 1).AddDays(-1).ToString('yyyy MM')
The above yields:
2019 12
Note: PowerShell's casts such as [datetime] '1/1/2020' generally use the invariant culture for stability of behavior across different cultures; this virtual culture is associated with the US-English culture and supports its month-first date format (e.g., 12/1/2020 refers to 1 December 2020, not 12 January 2020).
Surprisingly, by contrast, when you pass arguments to cmdlets, data conversions are culture-sensitive; that is, in the French culture (fr-FR), for instance, calling Get-Date 12/1/2020 would result in 12 January 2020, not 1 December 2020, which is what it returns in the US-English culture (en-US)).
This problematic discrepancy in behavior is discussed in this GitHub issue - the behavior is unlikely to change, however, in the interest of preserving backward compatibility.
You are able to create an arbitrary date like so
$testdate = get-date -year 2020 -month 1 -day 1
Your code however will then generate "2020 0" as an output.
You'd be better off with something like this. You would not be bound to running on the first day of the next month either:
$month = $(get-date (get-date $testdate).AddMonths(-1) -format "yyyy MM")
If you can guarantee that you will always run this on the first of the month, then you can use $folderName = ((Get-Date) - (New-TimeSpan -Days 1)).ToString("yyyy MM"). See Microsoft Docs on New-TimeSpan and this StackOverflow question on formatting dates
EDIT: hcm's answer leads to a better response here; instead of using the New-Timespan method above, modify my suggested code above to
$foldername = (Get-Date).AddMonths(-1).ToString("yyyy MM")
This eliminates the requirement that the supporting code be run on the first of the month.

Working with dates in extensionAttributes and Get-Date

I'm attempting to use Active Directory extensionAttributes to keep track of certain dates (like start date, termination date, etc) so that I can trigger certain actions when that date occurs.
I'm having an issue with the different variations that a date can be entered in (M/D/YY, MM/DD/YY, MM/DD/YYYY, etc). For example, I can use Get-Date to output to a format of M/D/YYYY, but I run into issues when someone enters MM/DD/YY.
Is there a way to make this work so that it can accept other variations (as long as it's month/date/year)?
Here are a couple of lines from the script in question. This runs once a day, and checks for new users starting the following day.
$StartingOn = (Get-Date).AddDays(1).ToShortDateString()
$NewUserCheck = Get-QADUser -DontUseDefaultIncludedProperties -IncludedProperties extensionAttribute11 | where { $_.extensionAttribute11 -eq $StartingOn }
Notice how it only returns as long as the date equals the Get-Date output. It was the only way I was able to get this to work properly. Even at that, if someone typed in 07/20/15, the output would return nothing.
Don't try to compare date strings. Use DateTime comparison which won't care about formatting details e.g.:
$StartingOn = (Get-Date).AddDays(1)
$NewUserCheck = Get-QADUser -DontUseDefaultIncludedProperties -IncludedProperties extensionAttribute11 |
Where { [DateTime]($_.extensionAttribute11) -eq $StartingOn}

updating PS script to run daily exchange logs

i create this script
$VarDay = (Get-Date).day
$VarMonth = (Get-Date).month
get-messagetrackinglog -Recipients:haavarot-from#my-domain.co.il -EventID "FAIL" -Start "09/20/14" -End "09/23/14" | export-csv c:\MailboxStatistics-$VarMonth-$VarDay.csv -encoding "utf8"
to create CSV file with the date name for FAIL mails from mail box
its work fine
but the only problem i cant found is to way to make it run daily wit no need to edit the DATES in the Ps code
-i want it to sat auto run at 22:00 every day and make the log for the some day only for 7 days
in the 8 day i want it to delete the old and create a new one
i need to save only the last 7 days
and idea?
-Start and -End accepts [System.DateTime] so you can just use Get-Date and play with the days using AddDays()
Straight from MSDN. You could do something like this
$endDate = Get-Date # This is today
$startDate = (Get-Date).AddDays(-7) # This is 7 days ago
If you would feel more comfortable with just the date and drop the time you can use the .ToString() method to format the time. Note that the datetime object would be lost as this returns a string.
$endDate = (Get-Date).ToString("MM/dd/yy")
$startDate = ((Get-Date).AddDays(-7)).ToString("MM/dd/yy")
More information on formatting dates can be found here