Search code examples
pythondatetimetimesimulation

How can i make the system time run faster in Python?


I wrote a simulation involving two scripts running in different consoles. The scripts are sending messages to each other via websocket. Messages are sent in defined intervals and contain a timestamp (currently I use datetime.utcnow). Now when I speed up the simulation,'naturally the datetime timestamps are unaffected by this which means they are out of sync with the simulation time. Is there a way to "speed up" the system time or do i have to write my own timestamp function where i can determine the speed?

Edit: Since i can't change the system time I wrote a script with a clock that runs at a speed I can determine and which can generate timestamps. However I have can't find a way to run this clock in the backgroud without blocking the console. I thought i could use asyncio but it is not working like i expected. This is the code:

import asyncio
import os
import math
simtime = 0
year = 2020
month = 10

try:
    TIMELAPSE = int(os.environ["TIMELAPSE"])
except KeyError:
    TIMELAPSE = 1


async def clock():
    global simtime
    while True:
        await asyncio.sleep(1)
        simtime += 1*TIMELAPSE


def timestamp():
    day = 1 + math.floor(simtime/86400)
    remaining = simtime % 86400
    hours = math.floor(remaining/3600)
    remaining = remaining % 3600
    minutes = math.floor(remaining/60)
    seconds = remaining % 60
    return f"{year}-{month}-{day}::{hours}:{minutes}:{seconds}"


loop = asyncio.get_event_loop()
loop.run_until_complete(clock())
loop.run_forever()

I thought the time would proceed in the background i can get the current time with the timestamp() function.

However, when i do import clock, it blocks the script


Solution

  • I think this is a two part question, first is about how to track the timelapse and second is about how to work with non blocking background tasks and asyncio.

    For the first, it looks like you're just adding simtime += 1*TIMELAPSE every second. It may be cleaner here to just set system time at the start of your script in a variable, and then when you want to check the current simtime you can check system time again and subtract the starting system time, then multiply the result by your TIMELAPSE. That should be equivalent to what's going on with simtime in this script, but is much simpler.

    For the second part of this question, dealing with asyncio and non-blocking, I think you should use asyncio.run_in_executor to execute the background task and then you can use run_until_complete to execute your foreground task as below. Note that I removed the async coroutine from the clock function here and just use time.sleep for the background task since the executor doesn't need it to be a coroutine for that synchronous part. You can fiddle with the sleep time in clock to verify it doesn't block on the async some_other_task coroutine.

    import asyncio
    import os
    import math
    import time
    simtime = 0
    year = 2020
    month = 10
    
    try:
        TIMELAPSE = int(os.environ["TIMELAPSE"])
    except KeyError:
        TIMELAPSE = 1
    
    
    def clock():
        global simtime
        while True:
            time.sleep(1)
            simtime += 1*TIMELAPSE
            print('Clock task simtime {}'.format(simtime))
    
    
    def timestamp():
        day = 1 + math.floor(simtime/86400)
        remaining = simtime % 86400
        hours = math.floor(remaining/3600)
        remaining = remaining % 3600
        minutes = math.floor(remaining/60)
        seconds = remaining % 60
        return f"{year}-{month}-{day}::{hours}:{minutes}:{seconds}"
    
    
    async def some_other_task():
        while True:
            print('Some other task using simtime {}'.format(simtime))
            await asyncio.sleep(1)
    
    loop = asyncio.get_event_loop()
    # run in background - None defaults to current loop executor
    loop.run_in_executor(None, clock)
    # run in foreground
    loop.run_until_complete(some_other_task())
    loop.run_forever()