Display current time with time zone in PowerShell - 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

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

Trying to put together a script for automatic timezone updates

I am working on a powershell script and it isn't reading my "If, elseIf" properly so I know I am doing something wrong. I need some help figuring this out.
I start with pulling the Default Gateway and storing it:
$Gateway = (Get-wmiObject Win32_networkAdapterConfiguration | ?{$_.IPEnabled}).DefaultIPGateway
Next I am trying to get it to sort that so that if it equals one of my designated Default Gateways it will update the timezone.
If ($Gateway = "10.100.4.1")
{
$TZ = "Central Standard Time"
}
ElseIf ($Gateway = "10.101.4.1")
{
$TZ = "Central Standard Time"
}
and I am finishing it up with a
Set-TimeZone $TZ
The purpose is that if I image a system at the home office, and I ship it to "remote location" I can't trust an end user to update their time zone, and I have POS that was poorly written so that it doesn't use UTC/GMT, and can cause issues with the BOH systems et cetera.
I will be placing this in as a startup item to execute whenever the system starts up to ensure that it is always up to date with the TZ.
Changing the Win 10 to use an automatic update for the TZ doesn't work because reasons (read: Networking Team and Security Team are out to get me and in this instance it isn't paranoia).
So, where can I find help to put this all together?
Edit: I had a typo which is why it wasn't working. So... nevermind. For those ofyou interested in the typo, I removed it already. it was in my $Gateway portion, adding a " after {$_.IPEnabled} and before the )
Your "if" statements are using the assignment operator = instead of the equality operator, -eq.
Switch it to If ($Gateway -eq "10.100.4.1") and it should work.
P.S. I missed the typo you mentioned, but the assignment operator is still an issue. When the assignment operator is used in an "if/elsif" statment it will always return $true which would be rather problematic.
I would suggest using a lookup Hashtable for the designated default gateways.
Because you want this to be run as startup script, you also need to create a centralized path where errors can be logged or write errors in the windows eventlog.
Create a Hashtable with your designated gateway IP addresses as key and the timezone ids as value.
Either directly in code:
$timeZones = #{
'10.100.4.1' = 'Central Standard Time'
'10.101.4.1' = 'Central Standard Time'
'10.102.4.1' = 'Eastern Standard Time'
# etc.
}
Or by reading a centralized CSV file with columns 'IPAddress' and 'TimeZone'
$defaultTz = Import-Csv -LiteralPath '\\Server\Share\Folder\Timezones.csv'
$timeZones = #{}
$defaultTz | ForEach-Object {
$timeZones[$_.IPAddress] = $_.TimeZone
}
Next, use these values something like this (demo uses a centralized error log file):
$errorlog = '\\Server\Share\Folder\TimezonesErrors.log'
$now = (Get-Date).ToString() # use default format or specify the date format yourself here
$currentTz = (Get-TimeZone).Id # get the current timezone Id
# get the default gateway IPv4 address for this computer
$gw = #((Get-wmiObject Win32_networkAdapterConfiguration | Where-Object {$_.IPEnabled -and $_.DefaultIPGateway-like '*.*.*.*'}).DefaultIPGateway)[0]
# check if the gateway IP address is present in the lookup Hashtable
if ($timeZones.ContainsKey($gw)) {
$tz = $timeZones[$gw]
# only set the wanted timezone if it differs from the current timezone
if ($tz -ne $currentTz) {
try {
Set-TimeZone -Id $tz -ErrorAction Stop
}
catch {
# add the exception to the error log
$msg = "$now - Error setting the timezone on computer $($env:COMPUTERNAME): $_.Exception.Message"
Add-Content -LiteralPath $errorlog -Value $msg
}
}
}
else {
# make it known that the IP address for this gateway was not found in the Hashtable
$msg = "$now - No timezone found for default gateway $gw on computer $($env:COMPUTERNAME)"
# write error to the central error.log file
Add-Content -LiteralPath $errorlog -Value $msg
}

PowerShell Comparing Date issue

I created a small scrip that have do to a couple of thinks.
get string from description field from user in specific container
take part of this string (substring method) that holds date information
convert this string to date format
compare this formated string with a current date - 30 days and do sth
The problem is that comparing is not working correctly. I tried do recognise date that is older than 30 days and do something but i see that comparison not always work. sometimes it does not recognize that date is less then - 30 days from current day
Script below
$DateMaxTime = (Get-date).AddDays(-30)
$DateFormatMaxTime = Get-Date $DateMaxTime -Format dd/MM/yyyy
$getData = get-ADUser -Filter * -Properties * -SearchBase "OU=Disabled,OU=Control,OU=x,OU=x,DC=x,DC=x,DC=x" `
|where {$_.Description -like "LEFT*"} |select name,samaccountname,description
Foreach ($Data IN $getData){
$DataPart = $null
$DataPart=$Data.description
$DatePart= $DataPart.substring(5,10)
$FinalDate = [datetime]::ParseExact($DatePart,'dd/MM/yyyy',$null)
$FinalDateFormat = Get-Date $FinalDate -Format dd/MM/yyyy
If ($FinalDateFormat -lt $DateFormatMaxTime ){ Write-Host "$($Data.samaccountname), $($Data.description) moved to deleteMe" }
else{ Write-Host "$($Data.samaccountname), $($Data.description) still in disabled" }
}
Below output shows me the wrong results (as example i did it for one user - >
Based on this logic the value $FinalDateFormat that hold date -> 31-12-2018 is less then value $DateFormatMaxTime that hold this date -> 25-06-2019 but it still applies else statement ...
I am not sure why, i did something wrong with date conversion ?
I put the comments as the answer:
I would compare the datetime versions of the dates rather than the string versions.
If ($FinalDate -lt $DateMaxTime)
Running
get-date -format
makes them strings.
$finaldate.gettype(); $datemaxtime.gettype()
shows the types. They are [datetime], not [string] like the other two.

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.

Issue with Powershell and date functions

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.