Create a datetime.time object just from seconds - python

I need to convert to convert an integer into a datetime.time instance. I get the time in elapsed seconds so it may well be larger than 59.
I know that I can create a time object by using:
import datetime
def to_time(seconds):
hours = seconds / 3600
mins = (seconds%3600)/60
sec = (seconds%3600)%60
return datetime.time(hours,mins,sec)
and I could hand this over to a mapping function if a have list of timevalues to convert. But I think it is ugly. Isn't there a better way to do so?
Actually, the problem is a bit more complex. I get a floating point as time where datetime.date.fromordinal(int(time)) returns the date and to_time(time - int(time)*86400) would return the time. I can than combine them to my datetime-object. So the input would be for example 734869.00138889, which should result in 2013-01-01 00:02
I would definitely prefer a less crowded method.

The simplest method is to use a datetime.timedelta() object with your time value as the days argument, and add that to datetime.datetime.min; that'll be off by one day so you have to subtract 1 from the value:
from datetime import datetime, timedelta
dt = datetime.min + timedelta(days=time - 1)
This avoids having to cast the value to an integer for the ordinal, then just the fraction for the time portion.
Demo:
>>> from datetime import datetime, timedelta
>>> t = 734869.00138889
>>> datetime.min + timedelta(days=t - 1)
datetime.datetime(2013, 1, 1, 0, 2, 0, 93)

Not sure if I understood how to convert the number after the decimal point to seconds, but I would try something along this line:
def to_datetime(time):
return datetime.datetime.fromordinal(int(time)) + \
datetime.timedelta(time % 1)
[update]
Is this the result you want?
>>> to_datetime(734869.00138889)
datetime.datetime(2013, 1, 1, 0, 2, 0, 93)

Related

Best way to convert a string to milliseconds in Python

To convert the amount of milliseconds represented by a string I created the following function:
time_str = '1:16.435'
def milli(time_str):
m, s = time_str.split(':')
return int(int(m) * 60000 + float(s) * 1000)
milli(time_str)
But I'm wondering if there is a native Python function to do this directly.
You can easily make it longer and more complicated with datetime:
import datetime
dateobj=datetime.datetime.strptime("1:16.435","%M:%S.%f")
timeobj=dateobj.time()
print(timeobj.minute*60000+timeobj.second*1000+timeobj.microsecond/1000)
76435.0
Now you have 2 additions, 2 multiplications, and even a division. And the bonus points for loading a package, of course. I like your original code more.
Since you're looking for functions to do this for you, you can take advantage of TimeDelta object which has .total_seconds(). This way you don't have to do that calculation. Just create your datetime objects then subtract them:
from datetime import datetime
datetime_obj = datetime.strptime("1:16.435", "%M:%S.%f")
start_time = datetime(1900, 1, 1)
print((datetime_obj - start_time).total_seconds() * 1000)
output:
76435.0
The reason for choosing datetime(1900, 1, 1) is that when you use strptime with that format it fills the rest to make this form: 1900-01-01 00:01:16.435000.
If your string changes to have Hour for example, you just need to change your format and it works as expected. No need to change your formula and add another calculation:
datetime.strptime("1:1:16.435", "%H:%M:%S.%f")
start_time = datetime(1900, 1, 1)
print((datetime_obj - start_time).total_seconds() * 1000)

how to convert datetime-like string into milliseconds

I have a user-defined function (return_times) that takes json file and returns two datetime-like strings.
time_1, time_2= return_times("file.json")
print(time_1, time_2) # outputs: 00:00:11.352 00:01:51.936
By datetime-like string I mean 00:00:11.352 which suits '%H:%M:%S.%f' formatting. However, when I try to convert them into milliseconds, I get negative values.
from datetime import datetime
dt_obj_1 = datetime.strptime(time_1, '%H:%M:%S.%f')
start_ms = dt_obj_1.timestamp() * 1000
dt_obj_2 = datetime.strptime(time_2, '%H:%M:%S.%f')
end_ms = dt_obj_2.timestamp() * 1000
print(start_ms, end_ms ) # outputs: -2209019260648.0 -2209019160064.0
If I success I would like to trim a video with the following command:
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
ffmpeg_extract_subclip("long_video.mp4", start_ms, end_ms, targetname="video_trimmed.mp4"), so just delete ` * 1000` part.
Note that ffmpeg_extract_subclip requires its t1 and t2 parameters to be in seconds, not in milliseconds as I initially thought.
Because of those negative integers I am not able to successfully run the trimming process.
I searched the web that mainly discusses several formats for the year, month and day, but not '%H:%M:%S.%f'.
What may I be overlooking?
What may I be overlooking?
time.strptime docs
The default values used to fill in any missing data when more accurate
values cannot be inferred are (1900, 1, 1, 0, 0, 0, 0, 1, -1).
whilst start of epoch is 1970. You might get what you want by computing delta between what you parsed and default strptime as follows:
import datetime
time1 = "00:00:11.352"
delta = datetime.datetime.strptime(time1, "%H:%M:%S.%f") - datetime.datetime.strptime("", "")
time_s = delta.total_seconds()
print(time_s)
output
11.352
You need to add the year date (year, month, day) to datetime, else this will default to 1 January 1900.
What you do is this:
from datetime import datetime
s = "00:00:11.352"
f = '%H:%M:%S.%f'
datetime.strptime(s, f) # datetime.datetime(1900, 1, 1, 0, 0, 11, 352000)
One way to do this is to append the date-string to the time-string you receive from return_times
From https://stackoverflow.com/a/59200108/2681662
The year 1900 was before the beginning of the UNIX epoch, which
was in 1970, so the number of seconds returned by timestamp must be
negative.
What to do?
It's better to use a time object instead of a datetime object.
from datetime import time
time_1 = "00:00:11.352"
hours, minutes, seconds = time_1.split(":")
print(time(hour=int(hours), minute=int(minutes), second=int(float(seconds)),
microsecond=int(float(seconds) % 1 * 1000000)))
You can split the time string into hours, minutes, seconds and miliseconds and with some simple math calculations, you get the whole time in miliseconds

Python - how to convert and determine a type of a 5 digit date?

I have some of the date fields represented as 5-digit numbers. And there is a mapping from the numbers to an actual date. However I can't figure out what logic should be applied to convert the numbers to dates in the "%Y-%m-%d" format?
13581 -> 2007-03-09
12784 -> 2005-01-01
The numbers shown are the numbers of days since 1st January 1970, which is the origin of Unix time.
They can be converted using for example:
from datetime import datetime, timedelta
n = 13581
print((datetime.utcfromtimestamp(0) + timedelta(n)).strftime("%Y-%m-%d"))
gives:
2007-03-09
Here timedelta is called with a single argument, being the offset in days. In general it is called with timedelta(days, seconds, microseconds) but all of these arguments default to zero.
As shown by
import datetime
datetime.date(2005, 1, 1)-datetime.timedelta(days=12784)
# datetime.date(1970, 1, 1)
your number is the number of days since 1970-01-01.
So, you can get the date by:
datetime.date(1970, 1, 1) + datetime.timedelta(days=12784)
# datetime.date(2005, 1, 1)
Here you go:
>>> from datetime import date, timedelta
>>> (date(1970,1,1) + timedelta(days=12784)).strftime("%Y-%m-%d")
'2005-01-01'
Just in case for a column in dataframe while using the accepted answer, you can use .apply:
df["column"].apply(lambda x: (datetime.utcfromtimestamp(0) + timedelta(int(x))).strftime("%Y-%m-%d"))

Convert TLE times (decimal days) to seconds after epoch

The standard two line element (TLE) format contains times as 2-digit year plus decimal days, so 16012.375 would be January 12, 2016 at 09:00. Using python's time or datatime modules, how can I convert this to seconds after epoch? I think I should use structured time but I am not sure how. seconds_of is a fictitious function - need to replace with something real.
EDIT: It will be most helpful if the answer is long (verbose) - like one step per line or so, so I can understand what is happening.
EDIT 2: After seeing the comments from #J.F.Sebastian I looked at the link for TLE and found it nowhere states "UTC". So I should point out the initial information and final information are UTC. There is no reference to local time, time zone, or system time.
e.g.
tim = "16012.375"
year = 2000 + int(tim[0:2])
decimal_days = float(tim[2:])
print year, decimal_days
2016, 12.375
# seconds_of is a fictitious function - need to replace with something real
seconds_after_epoch = seconds_of(2016,1,1) + (3600. * 24.) * decimal_days
You could try something like this [EDIT according to the comments].
import datetime
import time
# get year 2 digit and floating seconds days
y_d, nbs = "16012.375".split('.')
# parse to datetime (since midnight and add the seconds) %j Day of the year as a zero-padded decimal number.
d = datetime.datetime.strptime(y_d, "%y%j") + datetime.timedelta(seconds=float("." + nbs) * 24 * 60 * 60)
# 1.0 => 1 day
# from time tuple get epoch time.
time.mktime(d.timetuple())
#1481896800.0
It is easy to get datetime object given year and decimal_days:
>>> from datetime import datetime, timedelta
>>> year = 2016
>>> decimal_days = 12.375
>>> datetime(year, 1, 1) + timedelta(decimal_days - 1)
datetime.datetime(2016, 1, 12, 9, 0)
How to convert the datetime object into "seconds since epoch" depends on the timezone (local, utc, etc). See Converting datetime.date to UTC timestamp in Python e.g., if your input is in UTC then it is simple to get "seconds since the Epoch":
>>> utc_time = datetime(2016, 1, 12, 9, 0)
>>> (utc_time - datetime(1970, 1, 1)).total_seconds()
1452589200.0

Python fraction of seconds

What is the best way to handle portions of a second in Python? The datetime library is excellent, but as far as I can tell it cannot handle any unit less than a second.
In the datetime module, the datetime, time, and timedelta classes all have the smallest resolution of microseconds:
>>> from datetime import datetime, timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2009, 12, 4, 23, 3, 27, 343000)
>>> now.microsecond
343000
if you want to display a datetime with fractional seconds, just insert a decimal point and strip trailing zeros:
>>> now.strftime("%Y-%m-%d %H:%M:%S.%f").rstrip('0')
'2009-12-04 23:03:27.343'
the datetime and time classes only accept integer input and hours, minutes and seconds must be between 0 to 59 and microseconds must be between 0 and 999999. The timedelta class, however, will accept floating point values with fractions and do all the proper modulo arithmetic for you:
>>> span = timedelta(seconds=3662.567)
>>> span
datetime.timedelta(0, 3662, 567000)
The basic components of timedelta are day, second and microsecond (0, 3662, 567000 above), but the constructor will also accept milliseconds, hours and weeks. All inputs may be integers or floats (positive or negative). All arguments are converted to the base units and then normalized so that 0 <= seconds < 60 and 0 <= microseconds < 1000000.
You can add or subtract the span to a datetime or time instance or to another span. Fool around with it, you can probably easily come up with some functions or classes to do exaxtly what you want. You could probably do all your date/time processing using timedelta instances relative to some fixed datetime, say basetime = datetime(2000,1,1,0,0,0), then convert to a datetime or time instance for display or storage.
A different, non mentioned approach which I like:
from datetime import datetime
from time import sleep
t0 = datetime.now()
sleep(3)
t1 = datetime.now()
tdelta = t1 - t0
print(tdelta.total_seconds())
# will print something near (but not exactly 3)
# 3.0067
To get a better answer you'll need to specify your question further, but this should show at least how datetime can handle microseconds:
>>> from datetime import datetime
>>> t=datetime.now()
>>> t.microsecond
519943
NumPy 1.4 (in release candidate stage) has support for its own Date and DateArray objects. The one advantage is that it supports frequencies smaller than femtoseconds: http://projects.scipy.org/numpy/browser/trunk/doc/neps/datetime-proposal.rst
Otherwise I would go with the regular datetime subsecond frequencies.

Categories