How to compare time between two variables - powershell

I am trying to compare the time difference between two variables. $dt is a string pulled from the text of a log file. $curtime is pulled from the current date and time.
If I output $dt, it displays correctly the date and time from the log files. If I output $curtime, it displays correctly the date and time from the workstation.
But when I try and run New-TimeSpan and output the difference, my response is always "02:24:12".
Here is the code I am using:
New-TimeSpan $dt $curtime | Set-Variable -Name $TimeDiff
if ($TimeDiff.Seconds -lt 0) }
$Hrs = ($TimeDiff.Hours) + 23
$Mins = ($TimeDiff.Minutes) + 59
$Secs = ($TimeDiff.Seconds) + 59 }
else {
$Hrs = $TimeDiff.Hours
$Mins = $TimeDiff.Minutes
$Secs = $TimeDiff.Seconds }
$Difference = '{0:00}:{1:00}:{2:00}' -f $Hrs,$Mins,$Secs
$Difference

Unless he datetime from the logs are from a very narrow timespan,
it makes IMO no sense to manipulate by adding 23:59:59.
Better take the integer part from .TotalHours
Here returning hours > 24/99
$dt = "05/26/19 08:23:37"
$LogDT = [datetime]::parseexact($dt,"MM/dd/yy HH:mm:ss",[cultureInfo]::Invariantculture)
$TimeDiff = [datetime]::now - $LogDT
$Difference = '{0:00}:{1:00}:{2:00}' -f ([math]::floor($Timediff.TotalHours)),
$Timediff.Minutes,$Timediff.Seconds
Sample output:
> $Difference
111:16:17

Related

Modifying a Date variable in PowerShell

I'm looking to create a schedule type check.
I want to check today's date and if it's not the correct Day of the week for a task to run it continues.
Once the day is correct, I will then create a timer based on the difference between the expected execution time (let's say 01:00) and now. I have had a few ideas on how to achieve this, one of them being below. The problem I am facing is I am unsure of how to create a CountDown timer from the NEW-TIMESPAN output and how to dynamically change the $EndDate time value to meet the relevant schedule value. Any pointers would be appreciated.
$date = Get-Date -DisplayHint Date
if ($date.DayOfWeek -eq 'Friday')
{
$StartDate = (GET-DATE)
# I am unsure of how to dynamically change the $EndDate variable time value and create the 'CountDown variable' from the New-TimSpan output.
$EndDate = [datetime]"11/13/2020 01:00"
$countdown = NEW-TIMESPAN -Start $StartDate -End $EndDate
}
Write-Host $date
EDIT
With some help from #AdminOfTHings I have come up with the following solution.
if ($date.DayOfWeek -eq $schedule.Day)
{
$StartDate = (GET-DATE)
$tempDate = (GET-DATE).ToString()
$tempDate, $tempTime = $tempDate.Split(' ')
[datetime]$DateTime = $tempDate + " $($schedule.Time)"
$Timer = NEW-TIMESPAN -End $DateTime
$CountDown = $Timer.TotalSeconds
while ($CountDown -gt 0)
{
sleep 1
$CountDown--
}
else
{
#START SERVICE
}
}
New-Timespancreates a TimeSpan object regardless of the parameter set being used. If you know how long a timespan should be, then you can statically create that combination of days, hours, minutes, and seconds, making end time irrelevant.
$date = Get-Date -DisplayHint Date
if ($date.DayOfWeek -eq 'Friday') {
$countdown = New-Timespan -Hours 8
}
You could have a situation where you know you want the task to span 2 days but want it to end at 01:00. This would make the total time variable. You can make adjustments based on start time:
$date = Get-Date -DisplayHint Date
if ($date.DayOfWeek -eq 'Friday') {
$end = Get-Date -Day $date.AddDays(2).Day -Month $date.AddDays(2).Month -Hour 1 -Minute 0 -Second 0
$countdown = New-TimeSpan -End $end
}
Edit:
If you reach a target day of week and want to create a timer that lasts until the next 01:00 hour from that point in time, you can do the following:
$date = Get-Date
if ($date.DayOfWeek -eq 'Friday') {
if ($date.Hour -lt 1) {
# dynamic end date - start date
$countdown = (Get-Date $date -Hour 1 -Minute 0 -Second 0) - $date
} else {
# dynamic end date - start date
$countdown = (Get-Date $date.AddDays(1) -Hour 1 -Minute 0 -Second 0) - $date
}
}
$countdown.TotalSeconds # total seconds of the time span
As an aside, if you expect the starting time to be now, you can skip the -Start parameter. Leaving off -Start assumes the starting time is now.
Thanks for the help, I have no idea why I didn't think of it before but I have come up with
if ($date.DayOfWeek -eq 'Friday')
{
$StartDate = (GET-DATE)
$tempDate = (GET-DATE).ToString()
$tempDate, $tmpTime = $tempDate.Split(' ')
datetime]$DateTime = $tempDate + " $($schedule.Time)"
$Timer = NEW-TIMESPAN -End $DateTime
}
Just need to understand how to use the timer now (all in seconds)

Efficient way to find and replace many strings in a large text file

The Text file contains a software output on a time domain analysis. 10800 seconds simulation and 50 nodes being considered. We have 540,000 strings to be replaced in 540 MB text file with 4.5 million lines.
Which is currently projected to take more than 4 days. Something is going wrong. Don't know what. Please suggest me a better efficient approach.
Below is the function which does the find and replace.
To replace the string the script goes through the original text file line by line at the same time it generates a duplicate file with replaced strings. So another 540 MB file with 4.5 million lines will be generated at the end of the script.
Function ReplaceStringsInTextFile
{
$OutputfilebyLine = New-Object -typename System.IO.StreamReader $inputFilePathFull
$uPreviousValue = 0
$time = 60
$u = 0; $LastStringWithoutFindResult = 0
$lineNumber = 0
while ($null -ne ($line = $OutputfilebyLine.ReadLine())) {
$lineNumber = $lineNumber + 1
if ($time -le $SimulationTimeSeconds) # time simulation start and end checks
{
# 10800 strings corresponds to one node
# there are 50 nodes.. Thus 540,000 values
# $StringsToFindFileContent contains strings to find 540,000 strings
# $StringsToReplaceFileContent contains strings to replace 540,000 strings
$StringToFindLineSplit = -split $StringsToFindFileContent[$time-60]
$StringToReplaceLineSplit = -split $StringsToReplaceFileContent[$time-60]
if($u -le $NumberofNodes-1)
{
$theNode = $Nodes_Ar[$u]
$StringToFindvalue = $StringToFindLineSplit[$u]
$StringToReplacevalue = $StringToReplaceLineSplit[$u]
if (($line -match $theNode) -And ($line -match $StringToFindvalue)){
$replacedLine = $line.replace($StringToFindvalue,$StringToReplacevalue)
add-content -path $WriteOutputfilePathFull -value "$replacedLine"
$uPreviousValue = $u
$checkLineMatched = 1
if (($line -match $LastNodeInArray)) {
$time = $time + 1
$LastStringWithoutFindResult = 0
}
} elseIf (($line -match $LastNodeInArray) -And ($checkLineMatched -eq 0)) {
$LastStringWithoutFindResult = $LastStringWithoutFindResult + 1
} else {
#"Printing lines without match"
add-content -path $WriteOutputfilePathFull -value "$line"
$checkLineMatched = 0
}
}
if ($checkLineMatched -eq 1) {
# incrementing the value of node index to next one in case the last node is found
$u = $uPreviousValue + 1
if ($u -eq $Nodes_Ar.count) {
$u = 0
$timeElapsed = (get-date -displayhint time) - $startTime
"$($timeElapsed.Hours) Hours $($timeElapsed.Minutes) Minutes $($timeElapsed.Seconds) Seconds"
}
}
}
# Checking if the search has failed for more than three cycles
if ($LastStringWithoutFindResult -ge 5) { # showing error dialog in case of search error
[System.Windows.Forms.MessageBox]::Show("StringToFind Search Fail. Please correct StringToFind values. Aborting now" , "Status" , 0)
$OutputfilebyLine.close()
}
}
$OutputfilebyLine.close()
}
The above function is the last part of the script. Which is taking the most time.
I had run the script in under 10 hours 1 year ago.
Update The script sped up running after 4 hours and suddenly time to complete projection reduced from 4 days to under 3 hours. The script finished running in 7 hours and 9 minutes. However i am not sure what made the sudden change in speed other than asking the question on stack overflow :)
As per the suggestion by https://stackoverflow.com/users/478656/tessellatingheckler
I have avoided writing one line at a time using
add-content -path $WriteOutputfilePathFull -value "$replacedLine"
Instead i am now writing ten thousand lines at a time using add-content
$tenThousandLines = $tenThousandLines + "`n" + $replacedLine
And at the appropriate time I am using add-content to write 10,000 lines at one go like below. The if block follows my methods logic
if ($lineNumber/10000 -gt $tenThousandCounter){
clear-host
add-content -path $WriteOffpipeOutputfilePathFull -value "$tenThousandLines"
$tenThousandLines = ""
$tenThousandCounter = $tenThousandCounter + 1
}
I have encountered system out of memmory exception error when trying to add 15,000 or 25,000 lines at a time. After using this the time required for the operation has reduced from 7 hours to 5 hours. And at another time to 2 hours and 36 minutes.

Date variable still contains todays date after assigning it the value of upcoming Friday

I have a little powershell script to find the date of the upcoming Friday. However after assigning the value of the Friday date, my date variable still prints out todays date.
$date = Get-Date
for($i=1; $i -le 7; $i++)
{
if($date.AddDays($i).DayOfWeek -eq 'Friday')
{
$date.AddDays($i)
break
}
}
Write-Host "$date"
AddDays seems to return new date object and not update the date object in $date. Try assigning $date to the new date inside the if statement as follows:
$date = $date.AddDays($i)
$date = Get-Date
for($i=1; $i -le 7; $i++)
{
if($date.AddDays($i).DayOfWeek -eq 'Friday')
{
$date = $date.AddDays($i)
break
}
}
Write-Host "$date"
You have to return the value you're assigning to $date back to itself.
You probably tested it yesterday (Wednesday) because it works today.
The problem in your code is that it reuses the same $Date each time and probably step over the expected date:
Date + 1
Date + 3 (1 + 2)
Date + 6 (1 + 2 + 3)
...
In other words, the $date = Get-Date should be in your loop.
(Get-Date).AddDays((1..7 | Where {(Get-Date).AddDays($_).DayOfWeek -eq 'Friday'}))

Random time selection logic is not working and loops forever

My input is a text file with license plate and date for tire repair. I am then prompted to write a time e.g like 12:30. Then the script checks if the time is good or not. It's good if there is 30 minutes after and before not being used.
Using the example won't work since the 12:00-13:00 block contains an entry. Then the time isn't good, so the script has to search a good date for me. I tried to check line by line minute per minute. After, if the $help variable didn't change then it's good, but if it did, then make a random time, to check. Any ideas why that logic is not working?
Edit: Since I edited, the only thing I saw that isn't working, is that my script generates a random time, but it didn't check if that time is right, just accept that time and goodbye. Somehow I should make it to check it more times, and don't stop at the first generated time, check it, and if its still wrong, then randomize it again.
Sample data
ABC-145 8:30
BDE-540 9:45
EDS-598 10:30
SDF-478 11:30
HUT-434 12:15
JEC-238 13:15
ASD-325 14:00
VRA-123 16:15
HGV-456 18:00
$file = Get-Content Desktop/database.txt
[datetime]$time = Read-Host "Time for date?"
$start = $time
$check = $time
$help = 0
echo $time
foreach ($line in $file) {
#here I check if the date is free
$out = $line.Split()
while ($true) {
#until I found a good time
for ($i=0; $i -lt 30; $i++) {
#check the next half hour if good
if ($time -eq $out[1]) {
$help = 1
}
$time = $time.AddMinutes(1)
}
for ($i=0; $i -lt 60; $i++) {
#back check the half hour
if ($time -eq $out[1]) {
$help = 1
}
$time = $time.AddMinutes(-1)
}
$time = $time.AddMinutes(30)
if ($help -eq 0) {
break
} else {
$hour = Get-Random -Minimum 8 -Maximum 20
$minute = Get-Random -Minimum 0 -Maximum 59
$start = "$ora`:$perc"
$time = $start
$help = 0
#echo $time
}
}
}
if ($start-ne $check) {
echo "There wasnt a free date, but there is a free spot at " + $start
} else {
echo "This is free date"
}
I would recommend a different approach. First extract the times from your input file and convert them to DateTime values:
$times = Get-Content 'Desktop/database.txt' |
ForEach-Object { $_.Split("`t")[1] | Get-Date }
From that list determine the intervals that are actually wide enough to fit another appointment (at least 30 min after the previous and before the next appointment respectively, hence a minimum time window of 60 minutes):
$available = 0..($times.Count-1) |
Where-Object { ($times[$_+1]-$times[$_]).TotalMinutes -ge 60 }
Pick a random index from that list and add 30 minutes to the corresponding time:
$index = $available[(Get-Random -Maximum $available.Count)]
$start = $times[$index].AddMinutes(30)
'{0:HH:mm}' -f $start
Or, if you want some more variation, calculate a new starting time from the timeframe 30 minutes after the previous appointment to 30 minutes before the next appointment like this:
$index = $available[(Get-Random -Maxmimum $available.Count)]
$delta = ($times[$index+1]-$times[$index]).TotalMinutes - 60
$offset = 30 + [int](Get-Random -Maximum ($delta + 1))
$start = $times[$index].AddMinutes($offset)
'{0:HH:mm}' -f $start
This approach ensures that you don't run into an infinite loop when there are no available timeslots left.

Powershell/Sharepoint anti flooding script

Extreme powershell newbie here. I appreciate any and all help.
I'm trying to put together a simple anti-flooding script to work with Sharepoint/Powershell. Need it to look at a datetime in a field and compare it to the current datetime then stop execution if within 5 seconds of the last submittal. The method im using now always seems to evaluate to true.
#get system datetime (output format - 06/12/2014 07:57:25)
$a = (Get-Date)
# Get current List Item
$ListItem = $List.GetItemById($ItemID)
$DateToCompare = $ListItem["baseline"].AddMilliseconds(5000)
if ($DateToCompare -gt $a)
{Break}
#set variable to field
$ListItem["baseline"] = $a
#write new item
$ListItem.Update()
Break
I don't have Sharepoint access so I cannot fully test.
Can you verify the datatype of "baseline" attribute?
($ListItem["baseline"]).getType().Name
Are you sure that 5000 millseconds is really being added?
Write-Output "NOW: $($curDate) BASELINE: $($DateToCompare) DIFF: $( ($curDate - $DateToCompare).TotalMilliseconds )"
Why use break rather than let the evaluation naturally end? Below is an alternative way you might restructure you code.
#The difference in Milliseconds acceptable
$threshold = 5000
#Get current date, the formatting depends on what you have defined for output.
$curDate = Get-Date
#Get current list item from SP
$listItem = $List.GetItemById($ItemID)
# Get current List Item's baseline
$DateToCompare = $listItem["baseline"]
Write-Output "NOW: $($curDate) BASELINE: $($DateToCompare) DIFF: $( ($curDate - $DateToCompare).TotalMilliseconds )"
if ( ($curDate - $DateToCompare).TotalMilliseconds -le $threshold ){
#set variable to field
$ListItem["baseline"] = $curDate
#write new item
$ListItem.Update()
} else {
#Outside of threshold
}
So it turns out the script as I gave it above was functional. The issue was the time I was pulling under the (Get-Date) function was the server time (central), rather than the local time (eastern).
#bring server time up to eastern time
$a = (Get-Date).AddMilliseconds(7200000)
# Get current List Item
$ListItem = $List.GetItemById($ItemID)
#take baseline time and add 5 seconds
$DateToCompare = $ListItem["baseline"].AddMilliseconds(5000)
#stop if script has run in the last 5 sec (loop prevention)
if ($DateToCompare -gt $a)
{Break}
#stop if the status hasnt changed
if ($ListItem["baselinestatus"] -eq $ListItem["Status"])
{Break}
#get current activity status
$currentstatus = $ListItem["Status"]
#get current contents of log
$log = $ListItem["Log"]
#append new entry to existing and write it to the log
$newentry = $log + "<br>" + $a + " - " + $currentstatus
#set variable to field
$ListItem["Log"] = $newentry
$ListItem["baseline"] = $a
$ListItem["baselinestatus"] = $currentstatus
#write new item
$ListItem.Update()