I'm trying to write some code to sleep until the start of the next minute in the local timezone, but am having great difficulty doing so. The time library has always been one of my weak points, so I'm assuming there's some easy way to do this.
I thought of just computing a new TimeOfDay, but that wouldn't handle 23:59 to 00:00, and would presumably do very confusing things with daylight savings time switch-overs.
Handling leap seconds would be a nice bonus, too.
Using Control.Concurrent.threadDelay to do the sleeping seems like the simplest method to me, so an alternate question would be: How can I get the number of microseconds until the start of the next minute? DiffTime and NominalDiffTime would be perfectly acceptable ways to achieve this.
I worry this may not be what you want given your later comments. I think this would tolerate leap years, timezone changes, and day changes, but not leap seconds.
import Control.Concurrent (threadDelay)
import Data.Time.Clock
sleepToNextMinute :: IO ()
sleepToNextMinute = do t <- getCurrentTime
let secs = round (realToFrac $ utctDayTime t) `rem` 60
threadDelay $ 1000000 * (60 - secs)
main = do putStrLn "Starting..."
sleepToNextMinute
putStrLn "Minute 1"
sleepToNextMinute
putStrLn "Minute 2"
Maybe this will help you: PLEAC-Haskell: Dates and Times.
With the help of it you should be able to get the current minute with which you can create the time of the start of the next minute. Then just take the difference as sleep time.
I'm no expert on the time package, but how about something like this:
import Data.Time -- need Clock and LocalTime
curTime <- getCurrentTime
let curTOD = timeToTimeOfDay $ utctDayTime curTime
last = TimeOfDay (todHour curTOD) (todMin curTOD) 0
diff = timeOfDayToTime last + 60 - utctDayTime curTime
This will result in diff :: DiffTime with the correct difference in seconds; all boundaries and leap years should be accounted for. I'm not sure about leap seconds; you'd probably need to add them in manually.
This doesn't account for any time-zone specific mangling, but as getCurrentTime returns a UTCTime I think it will work generally. You can try using utcToLocalTimeOfDay instead of timeToTimeOfDay to manage timezone specific stuff, but then you'd have to do extra work to manage day offsets.
Related
I'm trying to convert a string containing a time ("%H:%M:%S.%f") to an int of the equivalent milliseconds. The complication is, the time is the output from FFmpeg, it's a point in the audio file. I need to get the number of milliseconds the time in the string represents. The timestamp method in DateTime is milliseconds from epoche, without another time stamp from when I began, this is no good.
For example:
t = "00:05:52.654321"
should be converted to:
i = 352654321
What is the best way to accomplish this?
This is how I figured out to do it.
def _convert_string_to_int(self, s) -> int:
begin = datetime.datetime(1900,1,1)
end = datetime.datetime.strptime(s, self._ffmpeg_format_string)
return int((end - begin).total_seconds() * 1000000)
It just feels really unnecessary to use timedelta like that.
Since timestamps are relative to the Unix Epoch (1970-01-01) you can make a datetime object from your time by prepending that date to it and then getting the timestamp of the resultant object to get the time string converted to seconds . Since python timestamps are floating point representations of seconds since the epoch, you will need to multiply by 1000 and convert to integer to get the number of milliseconds:
from datetime import datetime
t = "00:05:52.654321"
d = datetime.strptime('1970-01-01 ' + t, '%Y-%m-%d %H:%M:%S.%f')
print(int(d.timestamp()*1000))
Output:
352654
If you actually want microseconds, multiply by 1000000 instead.
As an alternative, you can split the time string on : and sum the parts, multiplying by 60 or 3600 to convert the hour and minute parts to seconds:
t = "00:05:52.654321"
millisecs = int(sum([float(v) * 1000 * 60 ** (2 - i) for i, v in enumerate(t.split(':'))]))
print(millisecs)
Output:
352654
Again, if you want microseconds, just multiply by 1000000 instead of 1000.
A number of milliseconds is inherently a time interval, so there is good reason why datetime.timedelta instances have a total_seconds method while datetime.datetime, datetime.date and datetime.time do not have one.
In principle you could use datetime.datetime.time(end) to get an object with properties including hour, minute, second and microsecond, and then use these to construct an arithmetic expression for the elapsed time since midnight on the same day. However, the supported way to handle time intervals like this is precisely the timedelta approach that you are already using.
I got the code to do what I want, yet I feel its hardcoded than an actual solution. Any suggestions on how I could adjust the Hours, Minutes, Seconds variables to be more clear to the reader?
Input = int(input("Seconds: "))
Hours = Input // (60*60)
Minutes = Input//(60) - (Hours*60)
Seconds = Input - (Minutes*60) - (Hours*60*60)
print(Hours,"Hours",Minutes,"Minutes",Seconds,"Seconds")
Use modulos instead of division. They're a little confusing at first, but they're really awesome.
def convert(seconds):
secs = seconds%60
mins = (seconds//60)%60
hrs = (seconds//3600)
return (secs,mins,hrs)
From a code optimization standpoint, my code does a total of four arithmetic operations, whereas yours runs through 10. Additionally, the whole (Hours * 60) thing is a little difficult to understand.
That's not to say your code is bad, just a little unclear. Though readability counts, your code is not so illegible as to be impossible to understand.
Constants help readability. Also using modulo helps:
SEC_PER_MIN = 60
SEC_PER_HOUR = SEC_PER_MIN * 60
secs = int(input("Seconds: "))
hours = secs // SEC_PER_HOUR
remaining_seconds = secs % SEC_PER_HOUR
mins = remaining_seconds // SEC_PER_MIN
remaining_seconds %= SEC_PER_MIN
print(f"{hours} Hours, {mins} Minutes, and {remaining_seconds} Seconds")
or you can abuse the time module and have it handle all the logic:
import time
secs = int(input("Seconds: "))
result = time.strftime('%H Hours, %M Minutes, %S Seconds', time.gmtime(secs))
print(result)
syncClockTime :: TimeZone -> UTCTime -> Pico -> Pico
syncClockTime zone time secondTo = do
let (TimeOfDay hour minute secondFrom) = localTimeOfDay $ utcToLocalTime zone time
if secondTo > secondFrom then
secondTo - secondFrom
else
60 + secondTo - secondFrom
I have a couple of questions related to the above code
is there a way to directly extract the seconds secondFrom from the UTCTime without converting it to local time and without specifying a time zone? (if yes: how? if no: why?)
How to actually make the running thread/task to asynchronously sleep for the above found number of seconds?
Question 2 is simple: you can make your Haskell process sleep for a specified number of microseconds using delay.
If I understand your Question 1, you want to be able to specify, say 30 seconds, and wait until the next time the system clock is 30 seconds past the minute. So if you call your function at 13:23:27 it will wait 3 seconds until 13:23:30, but if you called it at 13:23:33 then it will wait 57 seconds until 13:24:30.
That is a matter of doing arithmetic on the number of seconds. I would suggest you take the current Posix time using getPOSIXTime. This returns a NominalDiffTime, which is an instance of Real and RealFrac, so normal arithmetic works fine. The code you want is:
t <- getPOSIXTime
let secs = t - fromIntegral (floor (t/60) * 60)
Once you have the number of seconds past the minute you can figure out how long to wait until your target time.
Should one use setInterval via Javascript, or use some more idiomatic solution based on threads?
Using setInterval posed some challenges and comments from Alexander, Erik and Luite himself led me to try threads. This worked seamlessly, with very clean code similar to the following:
import Control.Concurrent( forkIO, threadDelay )
import Control.Monad( forever )
... within an IO block
threadId <- forkIO $ forever $ do
threadDelay (60 * 1000 * 1000) -- one minute in microseconds, not milliseconds like in Javascript!
doWhateverYouLikeHere
Haskell has the concept of lightweight threads so this is the idiomatic Haskell way to run an action in an asynchronous way as you would do with a Javascript setInterval or setTimeout.
Hackage
Real world Haskell
If you don't care about the motivation, just scroll to my best solution runPeriodicallyConstantDrift below. If you prefer a simpler solution with worse results, then see runPeriodicallySmallDrift.
My answer is not GHCJS specific, and has not been tested on GHCJS, only GHC, but it illustrates problems with the OP's naive solution.
First Strawman Solution: runPeriodicallyBigDrift
Here's my version of the OP's solution, for comparison below:
import Control.Concurrent ( threadDelay )
import Control.Monad ( forever )
-- | Run #action# every #period# seconds.
runPeriodicallyBigDrift :: Double -> IO () -> IO ()
runPeriodicallyBigDrift period action = forever $ do
action
threadDelay (round $ period * 10 ** 6)
Assuming "execute an action periodically" means the action starts every period many seconds, the OP's solution is problematic because the threadDelay doesn't take into account the time the action itself takes. After n iterations, the start time of the action will have drifted by at least the time it takes to run the action n times!
Second Strawman Solution: runPeriodicallySmallDrift
So, we if we actually want to start a new action every period, we need to take into account the time it takes the action to run. If the period is relatively large compared to the time it takes to spawn a thread, then this simple solution may work for you:
import Control.Concurrent ( threadDelay )
import Control.Concurrent.Async ( async, link )
import Control.Monad ( forever )
-- | Run #action# every #period# seconds.
runPeriodicallySmallDrift :: Double -> IO () -> IO ()
runPeriodicallySmallDrift period action = forever $ do
-- We reraise any errors raised by the action, but
-- we don't check that the action actually finished within one
-- period. If the action takes longer than one period, then
-- multiple actions will run concurrently.
link =<< async action
threadDelay (round $ period * 10 ** 6)
In my experiments (more details below), it takes about 0.001 seconds to spawn a thread on my system, so the drift for runPeriodicallySmallDrift after n iterations is about n thousandths of a second, which may be negligible in some use cases.
Final Solution: runPeriodicallyConstantDrift
Finally, suppose we require only constant drift, meaning the drift is always less than some constant, and does not grow with the number of iterations of the periodic action. We can achieve constant drift by keeping track of the total time since we started, and starting the nth iteration when the total time is n times the period:
import Control.Concurrent ( threadDelay )
import Data.Time.Clock.POSIX ( getPOSIXTime )
import Text.Printf ( printf )
-- | Run #action# every #period# seconds.
runPeriodicallyConstantDrift :: Double -> IO () -> IO ()
runPeriodicallyConstantDrift period action = do
start <- getPOSIXTime
go start 1
where
go start iteration = do
action
now <- getPOSIXTime
-- Current time.
let elapsed = realToFrac $ now - start
-- Time at which to run action again.
let target = iteration * period
-- How long until target time.
let delay = target - elapsed
-- Fail loudly if the action takes longer than one period. For
-- some use cases it may be OK for the action to take longer
-- than one period, in which case remove this check.
when (delay < 0 ) $ do
let msg = printf "runPeriodically: action took longer than one period: delay = %f, target = %f, elapsed = %f"
delay target elapsed
error msg
threadDelay (round $ delay * microsecondsInSecond)
go start (iteration + 1)
microsecondsInSecond = 10 ** 6
Based on experiments below, the drift is always about 1/1000th of a second, independent of the number of iterations of the action.
Comparison Of Solutions By Testing
To compare these solutions, we create an action that keeps track of its own drift and tells us, and run it in each of the runPeriodically* implementations above:
import Control.Concurrent ( threadDelay )
import Data.IORef ( newIORef, readIORef, writeIORef )
import Data.Time.Clock.POSIX ( getPOSIXTime )
import Text.Printf ( printf )
-- | Use a #runPeriodically# implementation to run an action
-- periodically with period #period#. The action takes
-- (approximately) #runtime# seconds to run.
testRunPeriodically :: (Double -> IO () -> IO ()) -> Double -> Double -> IO ()
testRunPeriodically runPeriodically runtime period = do
iterationRef <- newIORef 0
start <- getPOSIXTime
startRef <- newIORef start
runPeriodically period $ action startRef iterationRef
where
action startRef iterationRef = do
now <- getPOSIXTime
start <- readIORef startRef
iteration <- readIORef iterationRef
writeIORef iterationRef (iteration + 1)
let drift = (iteration * period) - (realToFrac $ now - start)
printf "test: iteration = %.0f, drift = %f\n" iteration drift
threadDelay (round $ runtime * 10**6)
Here are the test results. In each case test an action that runs for 0.05 seconds, and use a period of twice that, i.e. 0.1 seconds.
For runPeriodicallyBigDrift, the drift after n iterations is about n times the runtime of a single iteration, as expected. After 100 iterations the drift is -5.15, and the predicted drift just from runtime of the action is -5.00:
ghci> testRunPeriodically runPeriodicallyBigDrift 0.05 0.1
...
test: iteration = 98, drift = -5.045410253
test: iteration = 99, drift = -5.096661091
test: iteration = 100, drift = -5.148137684
test: iteration = 101, drift = -5.199764033999999
test: iteration = 102, drift = -5.250980596
...
For runPeriodicallySmallDrift, the drift after n iterations is about 0.001 seconds, presumably the time it takes to spawn a thread on my system:
ghci> testRunPeriodically runPeriodicallySmallDrift 0.05 0.1
...
test: iteration = 98, drift = -0.08820333399999924
test: iteration = 99, drift = -0.08908210599999933
test: iteration = 100, drift = -0.09006684400000076
test: iteration = 101, drift = -0.09110764399999915
test: iteration = 102, drift = -0.09227584299999947
...
For runPeriodicallyConstantDrift, the drift remains constant (plus noise) at about 0.001 seconds:
ghci> testRunPeriodically runPeriodicallyConstantDrift 0.05 0.1
...
test: iteration = 98, drift = -0.0009586619999986112
test: iteration = 99, drift = -0.0011010979999994674
test: iteration = 100, drift = -0.0011610369999992542
test: iteration = 101, drift = -0.0004908619999977049
test: iteration = 102, drift = -0.0009897379999994627
...
If we cared about that level of constant drift, then a more sophisticiated solution could track the average constant drift and adjust for it.
Generalization To Stateful Periodic Loops
In practice I realized that some of my loops have state that passes from one iteration to the next. Here's a slight generalization of runPeriodicallyConstantDrift to support that:
import Control.Concurrent ( threadDelay )
import Data.IORef ( newIORef, readIORef, writeIORef )
import Data.Time.Clock.POSIX ( getPOSIXTime )
import Text.Printf ( printf )
-- | Run a stateful #action# every #period# seconds.
--
-- Achieves uniformly bounded drift (i.e. independent of the number of
-- iterations of the action) of about 0.001 seconds,
runPeriodicallyWithState :: Double -> st -> (st -> IO st) -> IO ()
runPeriodicallyWithState period st0 action = do
start <- getPOSIXTime
go start 1 st0
where
go start iteration st = do
st' <- action st
now <- getPOSIXTime
let elapsed = realToFrac $ now - start
let target = iteration * period
let delay = target - elapsed
-- Warn if the action takes longer than one period. Originally I
-- was failing in this case, but in my use case we sometimes,
-- but very infrequently, take longer than the period, and I
-- don't actually want to crash in that case.
when (delay < 0 ) $ do
printf "WARNING: runPeriodically: action took longer than one period: delay = %f, target = %f, elapsed = %f"
delay target elapsed
threadDelay (round $ delay * microsecondsInSecond)
go start (iteration + 1) st'
microsecondsInSecond = 10 ** 6
-- | Run a stateless #action# every #period# seconds.
--
-- Achieves uniformly bounded drift (i.e. independent of the number of
-- iterations of the action) of about 0.001 seconds,
runPeriodically :: Double -> IO () -> IO ()
runPeriodically period action =
runPeriodicallyWithState period () (const action)
I want to make rhythms with Haskell's printf. The following should produce a repeating rhythm in which one note is twice as long as the other two. (That rhythm is encoded by the list [1,1,2].)
import Control.Concurrent
import Text.Printf
import Control.Monad
main = mapM_ note (cycle [1,1,2])
beat = round (10^6 / 4) -- measured in miliseconds
note :: Int -> IO ()
note n = do
threadDelay $ beat * n
printf "\BEL\n"
When I run it the long note sounds roughly three times as long as the others, rather than twice. If I speed it up, by changing the number 4 to a 10, the rhythm is destroyed completely: the notes all have the same length.
Is there a refresh rate to change? Is threadDelay not the service to use if I want precise timing?
Is threadDelay not the service to use if I want precise timing?
No, not at all:
threadDelay :: Int -> IO () Source
Suspends the current thread for a given number of microseconds (GHC only).
There is no guarantee that the thread will be rescheduled promptly when the delay has expired, but the thread will never continue to run earlier than specified.
However, on my machine (Win 8.1 x64 i5-3570k#3.4GHz) the rhythm runs fine. That being said, \BEL isn't really a good way to create a beat:
the \BEL sound depends on the operating system (sound dreadful in Windows 8 if played at that frequency),
it isn't clear whether \BEL blocks.
If the latter happens you end up with roughly the same length, since every \BEL will block and the threadDelay is shorter than the actual \BEL sound.
The problem appears to have been print, not threading. Rohan Drape at Haskell-Cafe showed me how to use OSC instead of print. The timing of the following test, which uses OSC, is to my ears indistinguishable from perfect. I had it send instructions to a sine wave oscillator in Max/MSP.
import Control.Concurrent
import Control.Monad
import System.IO
import Sound.OSC
main = do
hSetBuffering stdout NoBuffering
mapM_ note (cycle [1,1,2])
withMax = withTransport (openUDP "127.0.0.1" 9000)
beat = 60000 -- 60 ms, measured in µs
note :: Int -> IO ()
note n = do
withMax (sendMessage (Message "sin0 frq 100" []))
-- set sine wave 0 to frequency 100
withMax (sendMessage (Message "sin0 amp 1" []))
-- set sine wave 0 to amplitude 1
threadDelay $ beat * n
withMax (sendMessage (Message "sin0 amp 0" []))
-- set sine wave 0 to amplitude 0
threadDelay $ beat * n
Thanks, everyone!
Most likely you will have to rely on OS support or GHC internals. For example, I have used GHC.Event for this purpose with the
registerTimeout :: TimerManager -> Int -> TimeoutCallback -> IO TimeoutKey
funtion. But then it will also be asynchronous with callbacks. Also this is GHC specific.
Other options are timer libraries on hackage, not sure though if they are all portable or if they can be used on Windows, but most use OS support as for precise timing you need hardware timers.