I've been trying to create code in PowerShell that's gonna run if the time is below a specific time but upon experimenting it always gets stuck in an infinite loop executing even when the condition is already not satisfied
here's my code:
$a = Get-Date
Write-Host $a
[datetime]$time = "05/12/2016 1:57:00 AM"
DO
{
if($a -gt $time) {break}
Write-Host "not yet time"
if($a -gt $time) {break}
}while( $a -le $time)
CLS
Write-Host "done"
But it still is running even at 1:59 AM already. Any ideas on how to properly compare time? I want it to run till a specific time or I want it to run at least 2 hours.
Since you only assign the output from Get-Date to $a once, it's never updated, and you're performing the exact same comparison (times 3) every time the loop runs.
Call Get-Date in the loop body or conditional to get the updated time:
[datetime]$time = "05/12/2016 1:57:00 AM"
do {
Write-Host "not yet time"
}while($(Get-Date) -le $time)
cls
Write-Host "done"
Related
The script I'm working on is a ticket reserving bot for student events, it waits in the specified loop until sales start, and the write-progress is supposed to let you know when they do start. I'm fairly new to PowerShell and I'm sorry if the code is a sore for the eyes. I'm just baffled since this part did work earlier today, even though nothing about the loop changed iirc.
Here is the relevant part of the script:
While ($currentDate -lt $purchaseTime){
$currentDate = Get-Date
$waitTime = (New-TimeSpan -End $purchaseTime).TotalSeconds
Write-Progress -Activity "Waiting until sales start" -SecondsRemaining $waitTime
Start-Sleep -Milliseconds 10
If ($currentDate -ge $purchaseTime){continue}
}
I also tried this:
Do {
$currentDate = Get-Date
$waitTime = (New-TimeSpan -End $purchaseTime).TotalSeconds
Write-Progress -Activity "Waiting until sales start" -SecondsRemaining $waitTime
Start-Sleep -Milliseconds 201
} until ($currentDate -gt $purchaseTime)
I tried changing the loop from While to Do and even If statements but nothing changed. Can anyone solve this? I'm not getting any errors either, it just won't render.
EDIT 1
This is how the value is fetched, $jsonObject is made out of a GET request, and below is its value.
$purchaseTime = $jsonObject.model.product.dateSalesFrom
"2022-11-25T11:00:00+02:00"
The format shouldn't be the issue, since they've been the same throughout the process of me writing this script. And it used to with that formatting too.
I tried inserting the code suggested by Mathias before that declaration, but it didn't change the end result.
Did he mean I should insert it within the loop? Would that screw it up since I really need it to be this static place in time in order for the script to work as intended. My PSVersion is 5.1. Should I post the entire script for clarity?
EDIT 2.
I have implemented the code that Santiago suggested as the answer, but nothing has changed. The progress still won't render. Could the issue be with how the date from the jsonobject is formatted? The code Santiago posted runs and works on its own in a powershell instance, so I doubt my settings or anything like that is not working as intended.
Here is what the loop currently looks like:
$nowDate = Get-Date
$targetDate = Get-Date $jsonObject.model.product.dateSalesFrom
$timeSpan = $targetDate - $nowDate
$stopWatch = [System.Diagnostics.Stopwatch]::StartNew()
Do {
$progess = #{
Activity = 'Waiting until sales start'
SecondsRemaining = ($timeSpan - $stopWatch.Elapsed).TotalSeconds
}
Write-Progress #progess
Start-Sleep -Milliseconds 200
}
until($stopWatch.Elapsed -ge $timeSpan)
Proof of concept
This example shows how you can achieve your your do loop (I prefer to use do in this case, though it could be done with a while loop too) for exactly 1 minute displaying a progressive countdown. I believe a StopWatch shines for this use case, and it simplifies the code a lot.
$nowDate = Get-Date
$targetDate = $nowDate.AddMinutes(1)
$timeSpan = $targetDate - $nowDate
$stopWatch = [System.Diagnostics.Stopwatch]::StartNew()
do {
$progess = #{
Activity = 'Waiting until sales start'
SecondsRemaining = ($timeSpan - $stopWatch.Elapsed).TotalSeconds
}
Write-Progress #progess
Start-Sleep -Milliseconds 200
}
until($stopWatch.Elapsed -ge $timeSpan)
Using this same logic, you would only need to change the following:
$nowDate = Get-Date
$targetDate = Get-Date $jsonObject.model.product.dateSalesFrom
This would make your loop run until Friday, November 25, 2022 6:00:00 AM.
I have solved the issue.
The problem lies within the $progressPreference variable. I had set it to silentlyContinue to help with WebRequests being smoother, but unbeknownst to me it also affected the Write-Progress cmdlet. It works fine now.
I am trying to test Time out after 30 seconds.
Sample code:
$a = "y"
$b = "n"
$timeout = New-TimeSpan -Seconds 30
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$stopwatch.Start()
$timeout.Seconds
$stopwatch.elapsed.Seconds
do{
if($a -eq "n"){
Write-Host "This block will never run"
break
}
if($stopwatch.elapsed.Seconds -lt $timeout.Seconds){
Write-Host "Testing this block: Time OUT!!"
break
}
}while($a -eq $b)
$stopwatch.Stop()
But the if block if($stopwatch.elapsed.Seconds -lt $timeout.Seconds) is true even $stopwatch.elapsed.Seconds value is 0 and $timeout.Seconds value is 30 in the loop and complete the code in few milliseconds and not taking 30 seconds to print the Time out statement.
Could anyone please give me pointer to resolve this issue.
A couple of things:
You don't need these two lines: $timeout.Seconds and $stopwatch.elapsed.Seconds above the loop
Your while condition should be while($a -ne $b)
The test inside the loop should read if($stopwatch.elapsed.Seconds -ge $timeout.Seconds)
Try
$a = "y"
$b = "n"
$timeout = New-TimeSpan -Seconds 30
$stopwatch = [System.Diagnostics.Stopwatch]::new()
$stopwatch.Start()
do {
if($stopwatch.Elapsed.Seconds -ge $timeout.Seconds){
Write-Host "Testing this block: Time OUT!!"
break
}
# no timeout, so proceed with what needs to be done here
# . . .
} while($a -ne $b) # loop forever unless you set $a equal to $b in the loop somewhere
$stopwatch.Stop()
Theo's helpful answer addresses incidental logic problems with your approach and offers a solution that probably will work, but isn't fully robust: If the activity in your loop exceeds 1 minute before the timeout condition is tested, the test won't work as intended (even with the logic problems fixed).
You have two options:
Use .TotalSeconds instead of .Seconds, for the reasons explained below.
More simply, taking advantage of the fact that [timespan] instances are directly comparable (see below), you can use:
if ($stopwatch.elapsed -gt $timeout) { # ...
As zett42 points out, [timespan] instances are directly comparable, due to implementing the .NET System.IComparable interface (as well as its generic counterpart); e.g.:
# -> $true - a timespan representing a 61-second duration
# is greater than one representing a 60-second (1-minute) duration.
[timespan]::FromSeconds(61) -gt [timespan] '00:01:00'
Therefore, as shown in the top section, you can simply directly compare $stopwatch.elapsed and $timeout - both of which are [timespan] instances.
The .Seconds property of a [timespan] instance is only the seconds component, potentially alongside larger units, such as minutes (.Minutes) and hours (.Hours)
You need the .TotalSeconds property to get the total amount of seconds (analogously, there are also .TotalDays, .TotalHours, and .TotalMinutes properties).
Also note that .Seconds is always a whole number ([int]), whereas .TotalSeconds can be a fractional value ([double]).
To illustrate the difference:
PS> [timespan] '00:01:05' | # 1 minute and 5 seconds
Select-Object Seconds, TotalSeconds
Seconds TotalSeconds
------- ------------
5 65
#sivam The issue is-
You're not applying the proper properties of the timespan command if it goes beyond 59 seconds then at 60 seconds it will consider it 1 minute.
Update the condition inside the loop if($stopwatch.elapsed.Seconds -lt $timeout.Seconds)
Try
$a = "y"
$b = "n"
$timeout = New-TimeSpan -Minutes 1
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$stopwatch.Start()
$timeout.Minutes
$stopwatch.elapsed.Minutes
do{
if($a -eq "n"){
Write-Host "This block will never run"
break
}
if($stopwatch.elapsed.Minutes -ge $timeout.Minutes){
Write-Host "Time OUT!!"
break
}
}while($a -ne $b)
$stopwatch.Stop()
I've been trying to get the while & the If loop to read in the time correctly, but it doesn't end the program when the right time has hit. I tried using single quotes '' and double quotes "", as well as different syntax like (-eq, -match, -ne) to see if any of those work....and they don't.
Program Goal: loops until it hits 07:00am
# While the value is 1.
while ($value -ne 2)
{
# Value should be 1 in order to stay in loop.
$value = 1
# Get's the time in 24hr format
$time = get-date -Format HH:mm:ss
# For Debugging; Writes out Time
Write-Output $time
# Creates a Pop-Up Windows that prevents the computer from timing out; runs every 15 minutes.
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("Operation Completed",0,"Done",0x1)
# Causes the Program to wait to send the Enter Keystroke.
Sleep 4
# Sends the Enter Keystroke.
$wshell.sendkeys('~')
# Causes the Program to wait to send the Enter Keystroke in seconds (900sec = 15 Minutes).
Sleep 4
# If Condition; If the time is over 2am then the program quits.
If ($time -eq "02:03:00")
{
# While Loop End Condition
$value = 2
# "Debugging Output"
Write-Output $value
Write-Output $time
}
Else
{
# While Loop Condition
$value = 1
# "Debugging Output"
Write-Output $value
Write-Output $time
}
}
# "Debugging Output"
Write-Output "End"
Write-Output $time
Write-Output $value
The chances are really low that your if-statement will become true. Because your while loop takes at least 8 (2x Start-Sleep and other work) seconds until a new beginning. That means the $time variable will probably never be exactly 02:03:00. In this case I would not go for the exact time. Instead I would check if it's 02:03:00 or later. Try that:
$time = Get-Date
if ($time -ge (Get-Date -Hour 02 -Minute 03 -Second 00))
{
}
This condition should do the work:
if ((Get-Date) -gt (Get-Date -Hour 7 -Minute 0 -Second 0)) {
# While Loop End Condition
$value = 2
# more actions
}
It's comparing the current time with the DateTime object with current day, but time set to 07:00:00.
Keep in mind two things:
It will allow the loop to run only between midnight and 7AM. If you want to start the script the day before you need to adjust the conditions.
It might be more readable to not use if, but put the condition directly in while() like this:
while ((Get-Date) -lt (Get-Date -Hour 7 -Minute 0 -Second 0)) {
# do something
}
Currently, you're checking for exact time, so in theory the end condition might be met, However, if it hits that specific line one second before/after, it won't stop the loop.
Finished Code (at least this version of it anyhow). Appreciate the help too :)!
<###
Program Goal: Prevent PC Timeout/Sleep; also loops until it hits 07:00am
Date: 10/14/19
Version: 1.2
###>
# Creates a Pop-Up Windows that prevents the computer from timing out; runs every 15 minutes.
$wshell = New-Object -ComObject Wscript.Shell
# Get's the time in 24hr format
$time = get-date
# While the value is not equal to 2.
while ($value -ne 2)
{
# Value should be 1 in order to stay in loop.
$value = 1
$wshell.Popup("Operation Completed", 5,"Done", 1)
# Causes the Program to wait to send the Enter Keystroke.
Sleep 2
# If Condition; If the time is over 7am then the program quits.
if ((Get-Date) -gt (Get-Date -Hour 7 -Minute 0 -Second 0))
{
# While Loop End Condition
$value = 2
}
else
{
# While Loop Condition
$value = 1
# Causes the Program to wait (900sec = 15 Minutes) to prevent PC Timeout, will re-run again after Sleep.
Sleep 900
}
}
# Cleans up the COM Object
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wshell) | Out-Null
I'm trying to just get the groundwork for running some code with a time conditional. But I can't seem to grasp how to add time into the equation. Any Powershell people out there?
The $EndDate doesnt matter anymore. I just tried to use it as a way to understand how powershell uses conditional
$StartTime=(Get-Date)
$EndDate=[datetime]”00:00”
if ($StartDate -gt "00:00" -and $StartTime -lt "11:59")
{
Write-Host "It is time to work"
}
else
{
Write-Host "it is time to go"
}
$StartTime
My code right now should say its time to go but should say its time to work because as of right now its only 11:56 AM ET.
If you want to compare against the time of day, use the TimeOfDay TimeSpan exposed by [datetime] - PowerShell will automatically convert the right-hand "HH:mm" string into a meaningful TimeSpan that can be compared against:
$StartTime = Get-Date
if($StartTime.TimeOfDay -gt "00:00" -and $StartTime.TimeOfDay -le "12:00"){
# AM
}
else{
# PM
}
These two things are usually true.
(get-date) -gt '00:00'
True
(get-date) -lt '23:59'
True
Check for unset variables:
set-strictmode -v 1
I have a script that is being called via a Windows Scheduled Task, and that task is triggered based on a certain Windows Application Event. It is only critical to execute the script, though, if the event occurs 3 or more times in 1 minute; if the event occurs once a minute, no action should be taken.
I know this can be handled in the script itself. Let's say there are at least 2 new variables I will need:
# time window, in seconds
$maxTime = 60
# max number of times this script needs to be called, within $maxTime window,
# before executing the rest of the script
$maxCount = 3
I started outlining an algorithm using a temp file as tracking, but thought there might be a simpler solution that someone can show me. Thanks
You could store your execution times in an environment variable.
Before this script will work, you must create the LastExecutionTimes environment variable.
$maxTime = 60
$maxCount = 3
$now = Get-Date
# Get execution times within the time limit.
$times = #($env:LastExecutionTimes -split ';'|
Where-Object {$_ -and $now.AddSeconds(-1 * $maxTime) -lt $_})
$times += '{0:yyyy-MM-dd HH:mm:ss}' -f $now
$env:LastExecutionTimes = $times -join ';'
if($times.Length -lt $maxCount) {return}
# Reset the execution times
$env:LastExecutionTimes =''
Write-Host 'Continue Script' -ForegroundColor Yellow
I would write a text file and a secondary script or function to check it. Where essentially it will call it each time, and then writes the information writes to a text file at call time.
The something like this:
if(!((Get-Date).AddMinutes(-1) -lt $oldTime))
{
$CurDate = Get-Date
"$CurDate, 1" | out-File "TheCheck.txt"
}
else
{
$counter++
if($counter -ge 3) {Call WorkerFunction}
else{
"$oldTime, $counter" | Out-File "TheCheck.txt"
}
Its missing some variables, but overall should be functional as a supplemental script. Then what your scheduled task actually does is call this, if the time since the $oldTime is over 1 minute, then it over writes the file with the current time and 1 for a $counter variable. If its less than a minute since the first call it then checks the $counter and if it is 3 or higher (could also do -eq ) to 3 then it calls your main script.