Issue with Powershell and date functions - powershell

I have a script which pulls information from our SQL server estate. The scripts pulls off some dates (last check DBCC) and does some calculations for aging.
The problematic section of the code is here:
$lastDBCC_CHECKDB=$database.ExecuteWithResults("DBCC DBINFO () WITH TABLERESULTS").Tables[0] | Where-Object {$_.Field -eq "dbi_dbccLastKnownGood"} | Select-object Value
$lastDBCC_CHECKDB=$lastDBCC_CHECKDB.value -f (get-date)
$lastDBCC_CHECKDB=$lastDBCC_CHECKDB.Substring(0,19) | get-date -format 'dd/MM/yyyy HH:mm:ss'
Add-Member -InputObject $dbinfo -MemberType NoteProperty -Name 'Last CheckDBCC' -Value $lastDBCC_CHECKDB
$DBCCProblem = "None"
if ($lastDBCC_CHECKDB -lt $date.AddDays(-7)) { $DBCCProblem = "No CheckDBCC in the last week" }
if ($lastDBCC_CHECKDB -eq "01/01/1900 00:01:00") { $DBCCProblem = "CheckDBCC has never run" }
Add-Member -InputObject $dbinfo -MemberType NoteProperty -Name 'DBCC Issues' -Value $DBCCProblem
My date calculations are working, however, it looks like the process is calculating based on the current $date being in a different format.
No matter what I try, I can't seem to force $date to the correct format without the AddDays function failing.
On my Powershell terminal, if run $date (set in the script from the get-date cmdlet), I get:
09 July 2015 15:42:26
However, if I run:
$date=get-date
I get the incorrect format of:
09/07/2015 15:42:26
Notice the date and month (American format). My calculations are being done against that date despite me specifying the date format "dd/MM/yyyy HH:MM:ss".
Again, if I set $date in the script as:
$date=Get-date
My AddDays calculation function fails with:
Method invocation failed because [System.String] does not contain a method named 'AddDays'.
At ...\SQLServerBackupSummary.ps1:122 char:21
+ if ($lastDBCC_CHECKDB -lt $now.AddDays(-7)) ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Specifying the addition of Datetime doesn't work:
$lastDBCC_CHECKDB.Datetime -lt $date.AddDays(-7))
That seems to just mess everything up.
Any pointers for this gratefully appreciated, I'm still fairly new to Powershell but have never struggled with date conversion in other scripting languages before.

I think it has nothing to do with date formats. Based on the line :
if ($lastDBCC_CHECKDB -eq "01/01/1900 00:01:00")
I guess $lastDBCC_CHECKDB is a string and therefore you are comparing 2 strings. You want to compare 2 dates: so either try
if ([DateTime]$lastDBCC_CHECKDB -lt $date.AddDays(-7))
or follow this link if you need to parse custom date formats or more info.

Related

Powershell keep looping until condition is true then proceed

I have written a script that so far is able to check a file "latest.json" for the "created_at" object which shows the last date that a commit has occurred for software.
$websiteJson = Invoke-WebRequest "https://website/latest.json" | ConvertFrom-Json | select created_at
$todaysDate = Get-Date -Format "yyyy-MM-dd HH:mm"
if($websitejson.created_at | where {$_.created_at -eq $todaysDate}){
Write-Output "Today's date matches"
} else {
Write-Output "has not yet been updated"
}
How part of latest.json looks like
"created_at":"2020-03-23 17:32:48"
How do I change this to keep looping until the date pull from latest.json matches then proceed to next step (would download and install software). Also, since "created at" has "17:32:48" will this cause the date check to fail since the time does not match?
. I want it to keep checking if dates match.
Thank you!
Right now, I'm not going to bother converting dates to match to make sure they're the same format, but what you need for your specific questions is just a do until loop. I might update this to check the date formats if you supply an example layout of the returned JSON.
Do{
$websiteJson = Invoke-WebRequest "https://website/latest.json" | ConvertFrom-Json | select created_at
$todaysDate = Get-Date -Format "yyyy-MM-dd HH:mm"
if($websitejson.created_at | where {$_.created_at -eq $todaysDate}){
Write-Output "Today's date matches"
} else {
Write-Output "has not yet been updated"
}
start-sleep -s 60
}until($websiteJson -eq $todaysDate)
I believe this wont work right off the bat. You'll have to get the JSON date and $todaysDate to be the same format, then you can do this and it will work.
if you want to compare the date and/or time, use datetime objects instead of datetime strings. something like this ...
if you want to test for the actual time difference between two time objects ...
((Get-Date -Date '2020-03-23 18:11:22') - [datetime]'2020-03-23 17:32:48').TotalHours
# result = 0.642777777777778
you keep mentioning date as if you don't want the time, so this method would work for comparing the date parts of two timestamps ...
# the date at the time the code was run = 2020 April 03, Friday 4:30:34 PM
$Today = (Get-Date).Date
$Created_At = '2020-04-03 15:15:15'
$Today -eq ([datetime]$Created_At).Date
result = True

Trouble with [datetime]::parseexact

I know I must be missing something simple, but I've stared at it for a while and can't seem to see it.
I have the following powershell code
.".\Get-FileMetaDataReturnObject.ps1"
$BaseDirectory = "C:\Users\me\Desktop\Photo Test"
$Folders = get-childitem -path $BaseDirectory -Directory
foreach ($folder in $folders)
{
Write-Host "Folder name:$($folder.Name)"
$picture = Get-ChildItem -Path "$BaseDirectory\$($folder.Name)\"
$picMetaData = Get-FileMetaData -folder "$BaseDirectory\$($folder.Name)\"
$picDate = $picMetaData | Select 'Date Taken'
$picDateTaken = $picDate[0].'Date taken'
Write-Host $picDateTaken
$dateTime = [datetime]::parseexact($picDateTaken, "M/d/yyyy h:mm tt", [System.Globalization.CultureInfo]::InvariantCulture)
$dateStr = $dateTime.ToString('yyyy-MM-dd')
Write-Host $dateStr
}
When I run it I get the following error
Folder name:Deruta - Umbria, September 4, 2012
‎9/‎4/‎2012 ‏‎4:12 PM
Exception calling "ParseExact" with "3" argument(s): "String was not recognized as a valid DateTime."
At C:\Users\me\Desktop\Picture Folder Rename\PhotoFolderRename.ps1:18 char:5
+ $dateTime = [datetime]::parseexact($picDateTaken, "M/d/yyyy h:mm ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : FormatException
The meta data script is found here
I'm really not sure what I've screwed up with the date time parsing, so any help would be appreciated.
Comparing "9/‎4/‎2012 ‏‎4:12 PM".Length (20) with "9/4/2012 4:12 PM".Length (16) shows us something fishy is going on, and indeed this string is not what it appears to be; there are U+200E LEFT-TO-RIGHT MARK and U+200F RIGHT-TO-LEFT MARK control characters in there, which are invisible (or not, depending on your console settings). Replacing these out will make the string parseable:
$picDateTaken = $picDateTaken -replace "\u200e|\u200f", ""
Replacing control characters in general would be more involved, and I'm not sure if there isn't a better/more general way to get the metadata in an invariant format, but as long as you know exactly what you're dealing with this is good enough.

Converting CSV date string to another format

I got a CSV file with a date column with the format dd/MM/YYY HH:mm:ss, e.g. 14/11/2016 00:00:00.
I'm trying to batch convert the formating on that column to an ISO-8601 date format: YYYY-mm-dd HH:mm:ss
I have tried: ( I have edited I had an error on input string format...)
Import-Csv someCSV.csv | % {
$_.'[Date]' = ([datetime]::ParseExact(($_.'[Date]'),"dd/MM/YYYY HH:mm:ss").ToString('yyyy-MM-dd HH:mm:ss'))
} | Export-Csv 'C:\testBis.csv' -NoTypeInformation
I'm getting an error:
Cannot find an overload for "ParseExact" and the argument count: "2".
At line:1 char:69
+ ... Date]' ; ([datetime]::ParseExact(($_.'[Date]'),"dd/MM/YYY HH ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
I've had a look and tryed with Try parse and sever few ideas but nothing seam to work.
I dont know what I'm going wrong. any suggestions?
The pattern for matching the year must be yyyy (with lowercase y), there is no comma in your sample date, and you need to provide culture information for ParseExact(). I would also recommend to escape the forward slashes, otherwise they'll match whatever date separator is configured in the computer's regional settings. And you need to echo the modified record back to the pipeline, otherwise there'd be nothing to export.
$culture = [Globalization.CultureInfo]::InvariantCulture
Import-Csv someCSV.csv | ForEach-Object {
$_.'[Date]' = [DateTime]::ParseExact($_.'[Date]', 'dd\/MM\/yyyy HH:mm:ss', $culture).ToString('yyyy-MM-dd HH:mm:ss')
$_ # <-- feed modified record back into pipeline
} | Export-Csv 'C:\testBis.csv' -NoType
DateTime.ParseExact requires at least three parameters. The third parameter is the culture to use. You can pass the invariant culture [System.Globalization.CultureInfo]::InvariantCulture, eg:
([datetime]::ParseExact(($_.'[Date]'),"dd/MM/YYYY HH:mm:ss",[System.Globalization.CultureInfo]::InvariantCulture)
or a cleaner version:
$inv = [System.Globalization.CultureInfo]::InvariantCulture
$fmtFrom = "dd/MM/YYY HH:mm:ss"
$fmtTo = "yyyy-MM-dd HH:mm:ss"
Import-Csv someCSV.csv | % {
$_.'[Date]' = ([datetime]::ParseExact(($_.'[Date]'),$fmtFrom, $inv).ToString($fmtTo))
} | Export-Csv 'C:\testBis.csv' -NoTypeInformation
I removed the , from the format string because you mentioned that the actual format is dd/MM/YYY HH:mm:ss, not dd/MM/YYY, HH:mm:ss

Manipulating Text after Get-Content

I have a log file that I have to scan every 5 minutes for specific keywords - specifically "Order Failed" then capture the lines around that. I have that part programmed with no issues. The rest of the log entries don't matter. I'm able to use the command:
[string] $readFile = get-content c:\test\friday.log | select-String -pattern '(order.*failed)' -context 1,7
which outputs:
Message: Order submission failed.
Timestamp: 4/1/2016 4:05:09 PM
Severity: Error
Message: Modifier not authorized for parent item
Message: Order submission failed.
Timestamp: 4/1/2016 4:18:15 PM
Severity: Error
Message: Modifier not authorized for parent item
Which is exactly what I want. My problem is trying to read through the above output and store the "Timestamp" into a variable that I can manipulate.
The first challenge is the "Timestamp" time is written in UTC time and we are located in the Pacific Time Zone.
The second challenge is I need to compare the "Timestamp" time to the current time and store that as an integer. We only want to report errors that are within 5 minutes of the current time.
My current code only grabs the first "Timestamp" entry since I hard-coded it:
[string] $readFile = get-content c:\test\friday.log | select-String -pattern '(order.*failed)' -context 1,7
$fileArray = $readFile.Split(“`n”)
[dateTime] $TrimTime = $fileArray[3].trim("Timestamp: ")
[dateTime] $currentTime = get-date
[int32] $getMinutes = (new-timespan -start $TrimTime -end $currentTime).minutes
I don't know how to loop through the output of the Get-content - to check for all of the Timestamps - we only want to report errors that are within 5 minutes of the current time.
Don't cast your matches to a string right away.
$readFile = get-content c:\test\friday.log | select-String -pattern '(order.*failed)' -context 1,7
If you leave the MatchInfo objects intact for the time being you can extract the timestamps from the aftercontexts like this:
$readFile | ForEach-Object {
$_.Context.AfterContext | Where-Object {
$_ -match 'timestamp: (\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{2}:\d{2} [ap]m)'
} | ForEach-Object {
$timestring = $matches[1]
}
}
Use the ParseExact() method to convert the matched substring to a DateTime value:
$fmt = 'M\/d\/yyyy h:mm:ss ttK'
$culture = [Globalization.CultureInfo]::InvariantCulture
$timestamp = [DateTime]::ParseExact("${timestring}Z", $fmt, $culture)
The Z that is appended to $timestring indicates the UTC timezone and the trailing K in the format string makes the method recognize it as such. The result is automatically converted to your local time. If you need the time in UTC: append .ToUniversalTime() to the method call.

Display current time with time zone in PowerShell

I'm trying to display the local time on my system with the TimeZone. How can I display time in this format the simplest way possible on any system?:
Time: 8:00:34 AM EST
I'm currently using the following script:
$localtz = [System.TimeZoneInfo]::Local | Select-Object -expandproperty Id
if ($localtz -match "Eastern") {$x = " EST"}
if ($localtz -match "Pacific") {$x = " PST"}
if ($localtz -match "Central") {$x = " CST"}
"Time: " + (Get-Date).Hour + ":" + (Get-Date).Minute + ":" + (Get-Date).Second + $x
I'd like to be able to display the time without relying on simple logic, but be able to give the local timezone on any system.
While this is a bit ... naive perhaps, it's one way to get an abbreviation without a switch statement:
[Regex]::Replace([System.TimeZoneInfo]::Local.StandardName, '([A-Z])\w+\s*', '$1')
My regular expression probably leaves something to be desired.
The output of the above for my time zone is EST. I did some looking as I wanted to see what the value would be for other GMT offset settings, but .NET doesn't seem to have very good links between DateTime and TimeZoneInfo, so I couldn't just programmatically run through them all to check. This might not work properly for some of the strings that come back for StandardName.
EDIT: I did some more investigation changing the time zone on my computer manually to check this and a TimeZoneInfo for GMT+12 looks like this:
PS> [TimeZoneInfo]::Local
Id : UTC+12
DisplayName : (GMT+12:00) Coordinated Universal Time+12
StandardName : UTC+12
DaylightName : UTC+12
BaseUtcOffset : 12:00:00
SupportsDaylightSavingTime : False
Which produces this result for my code:
PS> [Regex]::Replace([System.TimeZoneInfo]::Local.StandardName, '([A-Z])\w+\s*', '$1')
U+12
So, I guess you'd have to detect whether the StandardName appears to be a set of words or just offset designation because there's no standard name for it.
The less problematic ones outside the US appear to follow the three-word format:
PS> [TimeZoneInfo]::Local
Id : Tokyo Standard Time
DisplayName : (GMT+09:00) Osaka, Sapporo, Tokyo
StandardName : Tokyo Standard Time
DaylightName : Tokyo Daylight Time
BaseUtcOffset : 09:00:00
SupportsDaylightSavingTime : False
PS> [Regex]::Replace([System.TimeZoneInfo]::Local.StandardName, '([A-Z])\w+\s*', '$1')
TST
You should look into DateTime format strings. Although I'm not sure they can return a time zone short name, you can easily get an offset from UTC.
$formatteddate = "{0:h:mm:ss tt zzz}" -f (get-date)
This returns:
8:00:34 AM -04:00
Be loath to define another datetime format! Use an existing one, such as RFC 1123. There's even a PowerShell shortcut!
Get-Date -format r
Thu, 14 Jun 2012 16:44:18 GMT
Ref.: Get-Date
This is a better answer:
$A = Get-Date #Returns local date/time
$B = $A.ToUniversalTime() #Convert it to UTC
# Figure out your current offset from UTC
$Offset = [TimeZoneInfo]::Local | Select BaseUtcOffset
#Add the Offset
$C = $B + $Offset.BaseUtcOffset
$C.ToString()
Output:
3/20/2017 11:55:55 PM
I'm not aware of any object that can do the work for you. You could wrap the logic in a function:
function Get-MyDate{
$tz = switch -regex ([System.TimeZoneInfo]::Local.Id){
Eastern {'EST'; break}
Pacific {'PST'; break}
Central {'CST'; break}
}
"Time: {0:T} $tz" -f (Get-Date)
}
Get-MyDate
Or even take the initials of the time zone id:
$tz = -join ([System.TimeZoneInfo]::Local.Id.Split() | Foreach-Object {$_[0]})
"Time: {0:T} $tz" -f (Get-Date)
I just combined several scripts and finally was able to run the script in my domain controller.
The script provides the output of time and timezone for all the machines connected under the domain.
We had a major issue with our application servers and used this script to cross check the time and timezone.
# The below scripts provides the time and time zone for the connected machines in a domain
# Appends the output to a text file with the time stamp
# Checks if the host is reachable or not via a ping command
Start-Transcript -path C:\output.txt -append
$ldapSearcher = New-Object directoryservices.directorysearcher;
$ldapSearcher.filter = "(objectclass=computer)";
$computers = $ldapSearcher.findall();
foreach ($computer in $computers)
{
$compname = $computer.properties["name"]
$ping = gwmi win32_pingstatus -f "Address = '$compname'"
$compname
if ($ping.statuscode -eq 0)
{
try
{
$ErrorActionPreference = "Stop"
Write-Host “Attempting to determine timezone information for $compname…”
$Timezone = Get-WMIObject -class Win32_TimeZone -ComputerName $compname
$remoteOSInfo = gwmi win32_OperatingSystem -computername $compname
[datetime]$remoteDateTime = $remoteOSInfo.convertToDatetime($remoteOSInfo.LocalDateTime)
if ($Timezone)
{
foreach ($item in $Timezone)
{
$TZDescription = $Timezone.Description
$TZDaylightTime = $Timezone.DaylightName
$TZStandardTime = $Timezone.StandardName
$TZStandardTime = $Timezone.StandardTime
}
Write-Host "Timezone is set to $TZDescription`nTime and Date is $remoteDateTime`n**********************`n"
}
else
{
Write-Host ("Something went wrong")
}
}
catch
{
Write-Host ("You have insufficient rights to query the computer or the RPC server is not available.")
}
finally
{
$ErrorActionPreference = "Continue"
}
}
else
{
Write-Host ("Host $compname is not reachable from ping `n")
}
}
Stop-Transcript
Russia, France, Norway, Germany:
get-date -format "HH:mm:ss ddd dd'.'MM'.'yy' г.' zzz"
Output for Russian time zone: 22:47:27 Чт 21.11.19 г. +03:00
Others - just change the code.
If you have a internet connection... API
Function tzAbbreviation {
Try {
$webData = Invoke-WebRequest -Uri "https://worldtimeapi.org/api/ip" -UseBasicParsing -TimeoutSec 3 -ErrorAction Stop
$Content = ConvertFrom-Json $webData.Content
Return $($Content.Abbreviation)
}
Catch {}
}
$tzAbbreviation = tzAbbreviation
In the Netherlands...
Output: CET