Milliseconds since midnight

Godot Version

4.3

Question

I’m trying to do something which was relatively easy in Javascript and in Lua, and which proves a bit tricky in GDScript and Godot: getting the number of milliseconds elapsed since midnight.

I’ve come up with the following code but since I run it every frame I was wondering if there was a more efficient way to achieve this.

Thanks for your help.

static func ms_since_midnight() -> float:
	# Get current time in unix epoch seconds,
	# with sub-second precision (hence a float)
	var unix_time: float = Time.get_unix_time_from_system()

	# Get midnight of current day in unix epoch seconds:
	# First get the current system time and date as dict and define midnight.
	var now: Dictionary = Time.get_datetime_dict_from_system(false)
	var d: Dictionary = {
		'year': now.year,
		'month': now.month,
		'day': now.day,
		'hour': 0,
		'minute': 0,
		'second': 0
	}
	# Then convert midnight to unix epoch seconds.
	# We use cast the returned int to a float,
	# in order to keep precision for the "now - midnight" delta later.
	var midnight: float = Time.get_unix_time_from_datetime_dict(d)
	
	# Offset by current timezone:
	# Multiply bias (in minutes) by 60 to get seconds
	midnight = midnight - Time.get_time_zone_from_system().bias * 60

	# Return milliseconds elapsed since midnight
	return (unix_time - midnight) * 1000

You could cache the midnight value, only have to update it if it’s over 24 hours

1 Like

Indeed I could. Thank you for confirming that there isn’t much else to optimise here, I was hoping it could be cleaner.

What about this version?


static func ms_since_midnight() -> float:
	var now: Dictionary = Time.get_datetime_dict_from_system(false)
	var midnight: Dictionary = {
		'year': now.year,
		'month': now.month,
		'day': now.day,
		'hour': 0,
		'minute': 0,
		'second': 0
	}
	var now_unix: float = Time.get_unix_time_from_system()
	var midnight_unix: float = Time.get_unix_time_from_datetime_dict(midnight)
	var timezone_offset: float = Time.get_time_zone_from_system().bias * 60
	return (now_unix - (midnight_unix - timezone_offset)) * 1000

Is that a tiny bit cleaner?

Your version looks great though and no, I can’t see any way to improve on it significantly.

The cache idea from @gertkeno was a good one though. If at the start of your game you log the ms since midnight, const STARTING_MS_SINCE_MIDNIGHT say, could you not just use something like this every frame?

func get_ms_since_midnight() -> int:
    return Time.get_ticks_msec() - STARTING_MS_SINCE_MIDNIGHT

Thanks. Still a lot of work for something I thought would be simpler.

I thought about using Time.get_ticks_msec(), yes, but this is a precise clock-based application and I don’t want to be concerned with drifting or hiccups. Also, it’s supposed to be able to run across midnight and if I do that I’ll have to check each frame if midnight has passed, which would amount to basically the same number of calculations.

1 Like