ParseExact to convert string to date format - powershell

I have a csv file with date column format as below. How can i use ParseExact command to convert to date format to compare with current date.
Thu Oct 28 09:40:54 WEST 2021
Sun Mar 20 07:23:44 WET 2022
Sat Oct 30 15:23:02 EDT 2021
Thu Aug 26 11:07:22 SGT 2021
Tue Sep 28 10:00:54 HKT 2021
Fri Jan 07 11:08:45 SAST 2022
$date = "Thu Oct 28 09:40:54 WEST 2021"
[datetime]::ParseExact($date, 'ddd MMM dd HH:mm:ss \W\E\S\T yyyy', [cultureinfo]'en-US')
this works.. but how do i loop through all the date string and compare with current date.

As Jeroen Mostert commented, you need to parse out the TimeZone abbreviations and get the UTC offset from that so you can convert the strings to dates you can compare.
Below does this by using a switch, but if you have lots of different timezones in the file, using a lookup Hashtable would be preferable.
Import-Csv -Path 'X:\dates.csv' | ForEach-Object {
# assuming the property is listed under a column header 'Date'
if ($_.Date -match '(WES?T|EDT|SGT|HKT|SAST) \d{4}$') {
# either use this switch or create a Hashtable with all UTC offsets
# for each TimeZone abbreviation you might have in the CSV
# for demo, I'm using a switch
$UTCoffset = switch ($matches[1]) {
'WET' { 0; break} # Western Europe Standard Time
'WEST' { 1; break} # Western European Summer Time
'EDT' {-4; break} # Eastern Daylight Time
'EST' {-5; break} # Eastern Standard Time
'SGT' { 8; break} # Singapore Time (Standard time)
'HKT' { 8; break} # Hong Kong Time (Standard time)
'SAST' { 2; break} # South Africa Standard Time
}
# remove the timezone abbreviation from the date string
$dateToParse = $_.Date -replace "$($matches[1]) "
# parse the date as UTC ([cultureinfo]'en-US' can be replaced by [cultureinfo]::InvariantCulture)
$dateUTC = ([datetime]::ParseExact($dateToParse, 'ddd MMM dd HH:mm:ss yyyy', [cultureinfo]'en-US')).AddHours(-$UTCoffset)
# unfortunately, the above specifies the .Kind property as 'Local', so we must first set this to 'Utc'
# and then do .ToLocalTime() on it in order to compare with our local reference date
$dateLocal = ([datetime]::SpecifyKind($dateUTC, 'Utc')).ToLocalTime()
# do your comparison here against the reference date
# for demo, just output the converted date
Write-Host "'$($_.Date)' translates to $dateLocal"
}
}

Related

Powershell sorts by MM/DD/YY, HH:MM AM/PM

Importing a CSV file that has date/time values a column "Expiration" as "MM/DD/YY, HH:MM AM" (or PM). When I parse through the file I store it in an object of type System.Collections.ArrayList (not sure that matters) and I'd like to export the results in descending date/time order. When I use:
Sort-Object -Property Expiration -Descending
It sorts the results mostly in order but by the 1st integer of the DD portion so it looks like this:
2/1/21, 3:54 AM
2/11/21, 7:59 AM
2/2/21, 4:44 AM
2/21/21, 6:24 AM
2/3/21, 3:58 AM
2/4/21, 3:59 AM
What can I do to get this sorted properly upon export? I also tried sorting A-Z in Excel upon output but it does the same thing.
That is because these are strings, not real DateTime objects. You need to make them DateTime objects.
By default, this...
2/1/21, 3:54 AM
2/11/21, 7:59 AM
2/2/21, 4:44 AM
2/21/21, 6:24 AM
2/3/21, 3:58 AM
2/4/21, 3:59 AM
... on import will be separate columns if no header is specified, and only the date, not time, if the property used. These strings must be properly quoted to be read as one column.
"Expiration"
"2/1/21, 3:54 AM"
"2/11/21, 7:59 AM"
"2/2/21, 4:44 AM"
"2/21/21, 6:24 AM"
"2/3/21, 3:58 AM"
"2/4/21, 3:59 AM"
Example - Date actions
Sort-Object { $PSitem.Expiration -as [datetime] }
# Or these
$sortedDates = $dates |
Sort-Object {[System.DateTime]::ParseExact($PSItem, "MM/dd/yyyy", $null)}
# Or DateTIme parsing/formatting like these
[DateTime]"2020-7-16"
[DateTime]"Jul-16"
'{0:yyyy-MM-dd}' -f [DateTime]'Jul-16'
# Or
[datetime]::parseexact($PSitem.Expiration, 'dd-MMM-yy', $null)
# Output DateTime
[datetime]::parseexact($PSitem.Expiration, 'dd-MMM-yy', $null).ToString('yyyy-MM-dd')
# Or accept the default of ParseExect.
Import-CSV -Path 'C:\Scripts\Dates.csv' |
ForEach {[DateTime]::Parse($PSitem.Expiration)} |
Sort-Object
# Results
<#
Monday, February 1, 2021 3:54:00 AM
Tuesday, February 2, 2021 4:44:00 AM
Wednesday, February 3, 2021 3:58:00 AM
Thursday, February 4, 2021 3:59:00 AM
Thursday, February 11, 2021 7:59:00 AM
Sunday, February 21, 2021 6:24:00 AM
#>
Import-CSV -Path 'C:\Scripts\Dates.csv' |
ForEach {[DateTime]::Parse($PSitem.Expiration)} |
Sort-Object -Descending
# Results
<#
Sunday, February 21, 2021 6:24:00 AM
Thursday, February 11, 2021 7:59:00 AM
Thursday, February 4, 2021 3:59:00 AM
Wednesday, February 3, 2021 3:58:00 AM
Tuesday, February 2, 2021 4:44:00 AM
Monday, February 1, 2021 3:54:00 AM
#>
See also: PowerTip: Use PowerShell to Format Dates

How to Convert datetime formats using powershell

I Have a variable stored with this value
PS C:\Users\> $Time
Monday, November 30, 2020 8:55:01 AM
Sunday, October 18, 2020 11:10:01 PM
Sunday, November 8, 2020 10:40:34 PM
Sunday, November 8, 2020 11:47:37 PM
Sunday, November 8, 2020 10:59:57 PM
Tuesday, December 1, 2020 3:15:42 AM
Monday, November 30, 2020 7:00:32 PM
Monday, November 30, 2020 12:19:06 AM
Monday, November 30, 2020 7:01:34 PM
Tuesday, December 1, 2020 1:12:10 AM
Tuesday, December 1, 2020 2:37:18 AM
Sunday, November 1, 2020 7:39:34 PM
Sunday, September 27, 2020 11:48:38 PM
I want the time formats of the variable $time to change to "yyyy-mm-dd hh:mm:ss" so that ALL of the list is displayed
PS C:\Users\> $Time
2020-11-30 08:55:01
2020-10-18 11:10:01
2020-11-08 10:40:34
2020-11-08 11:47:37
2020-11-08 10:59:57
2020-12-01 03:15:42
2020-11-30 07:00:32
2020-11-30 12:19:06
2020-11-30 07:01:34
2020-12-01 01:12:10
2020-12-01 02:37:18
2020-11-01 07:39:34
2020-09-27 11:48:38
Please help me creating a code for the same
thanks
This is a pretty common question that gets asked here on StackOverflow, however, it seems most of the answers are directed towards converting a variable which stores a single date, to a formatted string.
Whereas you have an array of dates you want to convert.
I'm going to make the assumption that you have an Array of DateTime values, and not an Array of String.
For starters, there's TONS of blogs and articles about this, not to mention the documentation.
https://devblogs.microsoft.com/scripting/formatting-date-strings-with-powershell/
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-date
Depending on how you need to use this data there is a million different ways to do this.
Primarily, you need to learn how to perform actions against an array of objects. Using things like ForEach, ForEach-Object, Select-Object, etc. Once you learn how to use those, then the problem just becomes "how do you format a date to a string", which is all over the place on here and the rest of the internet.
Here's some examples:
# Use this to generate sample data:
$Time = 10000,9000,8000,7000,6000,5000,4000,3000,2000,1000 |
ForEach-Object { (Get-Date).AddMinutes(-$_) }
## Various solutions:
$Time | ForEach-Object { $_.ToString('yyyy-MM-dd HH:mm:ss') }
$Time | ForEach-Object { $_ | Get-Date -Format 'yyyy-MM-dd HH:mm:ss' }
$Time | ForEach-Object { $_ | Get-Date -f 'yyyy-MM-dd HH:mm:ss' }
$Time | ForEach-Object { Get-Date $_ -f 'yyyy-MM-dd HH:mm:ss' }
$Time | Select-Object #{N='TimeString'; E={$_.ToString('yyyy-MM-dd HH:mm:ss')}}
foreach ($tv in $time) { $tv.ToSTring('yyyy-MM-dd HH:mm:ss') }
$Time.ForEach({$_.ToString('yyyy-MM-dd HH:mm:ss')})
# Other methods submitted in comments, thanks #iRon
$Time | ForEach-Object ToString('yyyy-MM-dd HH:mm:ss')
$Time.ForEach('ToString', 'yyyy-MM-dd HH:mm:ss')
Note that this is case sensitive.
MM - Means the two digit month value
mm - Means the two digit day value

convert Fri Nov 02 14:37:02 2018 to 2018-11-02 14:37:02

I want to convert Fri Nov 02 14:37:02 2018 to 2018-11-02 14:37:02 in Windows Powershell, thanks for any proposals.
here's how to convert a time string to a datetime object & back again ... [grin]
$TimeString = 'Fri Nov 02 14:37:02 2018'
$TimeObject = Get-Date -Date '2018-11-02 14:37:02'
'String = {0}' -f $TimeString
# convert the above string to a datetime object
$TS_DateTimeObject = [datetime]::ParseExact('Fri Nov 02 14:37:02 2018', 'ddd MMM dd HH:mm:ss yyyy', $Null)
# my PC locale is set to use yyyy-MM-dd hh:mm:ss as the datetime format
# most stateside PC locale setting will be MM-dd-yyyy
'Object = {0}' -f $TimeObject
'TS_Object = {0}' -f $TS_DateTimeObject
# convert a datetime object to a string
'DT to String = {0}' -f $TimeObject.ToString('yyyy-MM-dd HH:mm:ss')
output ...
String = Fri Nov 02 14:37:02 2018
Object = 2018-11-02 2:37:02 PM
TS_Object = 2018-11-02 2:37:02 PM
DT to String = 2018-11-02 14:37:02
here is a link to the datetime format codes ...
Date and Time formats - PowerShell - SS64.com
— https://ss64.com/ps/syntax-dateformats.html

Get the last day of a month on powershell

I'm trying to put in a .txt file the first day and the last day of the months using PowerShell.
In the exemple below i was trying to get the first and the last day of July, but i'm just getting the first day. The second line of the script isn't working.
#PowerShell "(Get-Date).AddMonths(1).ToString('01/MM/yyyy')" >>dates.txt
$LastDayInMonthString = "$($(get-date).AddMonths(1).ToString("dd/mm/yyy"))$LastDayInMonth" >>dates.txt
Someone can say me what is wrong?
I wanted a .txt file like it: 01/07/2018, 31/07/2018.
The first line write the first day of next month,
and second line write the last day of that month.
Much simpler solution is to call into the DaysInMonth function
[DateTime]::DaysInMonth(2018, 11)
For the current month that would look like:
$today = get-date
$lastDay = [DateTime]::DaysInMonth($today.Year, $today.Month)
$firstDate = [DateTime]::new($today.Year, $today.Month, 1)
$lastDate = [DateTime]::new($today.Year, $today.Month, $lastDay)
$firstDate
$lastDate
This also works around any hindering daylight savings changes and other weird things that can happen with timezones etc.
Or if pure strings are all you need:
(get-date -Format "yyyy/MM") + "/1"
(get-date -Format "yyyy/MM") + "/" + [DateTime]::DaysInMonth((get-date).Year, (get-date).Month)
An easy way is to take the last day of the previous year and add 1..12 months to it:
1..12 | % { (New-Object DateTime(2017,12,31)).AddMonths($_) }
Output will be in the user's date/time format, in my case Dutch:
woensdag 31 januari 2018 00:00:00
woensdag 28 februari 2018 00:00:00
zaterdag 31 maart 2018 00:00:00
maandag 30 april 2018 00:00:00
donderdag 31 mei 2018 00:00:00
zaterdag 30 juni 2018 00:00:00
dinsdag 31 juli 2018 00:00:00
vrijdag 31 augustus 2018 00:00:00
zondag 30 september 2018 00:00:00
woensdag 31 oktober 2018 00:00:00
vrijdag 30 november 2018 00:00:00
maandag 31 december 2018 00:00:00
If required you can format it as you need it, e.g.
1..12 | % { (New-Object DateTime(2017,12,31)).AddMonths($_).ToString("yyyyMMdd") }
20180131
20180228
20180331
20180430
20180531
20180630
20180731
20180831
20180930
20181031
20181130
20181231
This seems simple enough
$firstDate = [DateTime]::new($reportDate.Year, $reportDate.Month, 1)
$lastDate=$firstDate.AddMonths(1).AddDays(-1)
Edit removed the for only date unneccessary time adjustments
In PowerShell to get the first day and last of next month
$CIGB = New-Object System.Globalization.CultureInfo('en-GB')
'{0}, {1}' -f (Get-Date -Day 1).AddMonths(1).ToString('d',$CIGB),
(Get-Date -Day 1).AddMonths(2).AddDays(-1).ToString('d',$CIGB)|sc dates.txt
The $CIGB is neccessary for me because my local date separator overrides the /
If your short date format 'd' returns dd/MM/yyyy the first line and the ,$CIGB can be removed.
01/07/2018, 31/07/2018
This can be wrapped in a single (albeit quite long) line.
powershell -nop -c "$CIGB=New-Object System.Globalization.CultureInfo('en-GB');'{0}, {1}' -f (Get-Date -Day 1).AddMonths(1).ToString('d',$CIGB),(Get-Date -Day 1).AddMonths(2).AddDays(-1).ToString('d',$CIGB)">>dates.txt
> type dates.txt
01/07/2018, 31/07/2018
Strings can be passed to Get-Date. The -Format parameter also accepts strings/variables.
# Get the previous month
$LastMonth = (Get-Date).AddMonths(-1)
# Format the first day of the month
$FirstDayOfMonth = $LastMonth | Get-Date -Format "yyyy/MM/01"
# Calculate the number of days (last day) in the month
$LastDay = [DateTime]::DaysInMonth($LastMonth.Year, $LastMonth.Month)
# Use it in the format parameter
$LastDayOfMonth = $LastMonth | Get-Date -Format "yyyy/MM/$lastDay"

Get-Date formatting/culture

How do I specify what part of my input string is the date and month?
If the input is 01/10/2017, this can be read as 1st Oct 2017 and 10th Jan 2017. Both are correct.
I want to be explicit that 01 is date and 10 is month, so that irrespective of locale and time format I can get a consistent result.
Sample code:
get-date -Date '01/10/2017'
The output is:
Tuesday, January 10, 2017 12:00:00 AM
The desired output is:
Sunday, October 01, 2017 12:00:00 AM
I have a solution for you. It requires that the culture as one of the arguments.
([datetime]::ParseExact($date,"dd/MM/yyyy",[Globalization.CultureInfo]::CreateSpecificCulture('en-GB')))
A culture does not have to be specified. However, the argument for it does, otherwise you will get an error:
Cannot find an overload for "ParseExact" and the argument count: "2".
[cultureinfo]::InvariantCulture or $null can be used as the third argument:
$date = "01/10/2017"
[datetime]::ParseExact($date, "dd/MM/yyyy", [cultureinfo]::InvariantCulture)
[datetime]::ParseExact($date, "dd/MM/yyyy", $null)
Output in all three cases
01 October 2017 00:00:00
Try this:
Get-Date(Get-Date -Date $date -Format 'dd/MM/yyyy')
You can enforce the culture for single commands (or command blocks). This should help avoiding that date chaos.
PS C:\> [System.Threading.Thread]::CurrentThread.CurrentUICulture = "en-US" ; [System.Threading.Thread]::CurrentThread.CurrentCulture = "en-US"; get-date -Date '01/10/2017'
Tuesday, January 10, 2017 12:00:00 AM
PS C:\> [System.Threading.Thread]::CurrentThread.CurrentUICulture = "en-GB" ; [System.Threading.Thread]::CurrentThread.CurrentCulture = "en-GB"; get-date -Date '01/10/2017'
01 October 2017 00:00:00