Adding a timedelta to a skyfield Time - python

The skyfield Almanach documentation
uses this code to define the points in time between which to compute sunrise & sunset:
t0 = ts.utc(2018, 9, 12, 4)
t1 = ts.utc(2018, 9, 13, 4)
What if I just wanted to use one (start) date and set the next date to be exactly one day after? I can't just add one to the day argument since this would not be correct at the end of the month.
Using Python's datetime I could do this using
from datetime import datetime, timedelta
datetime(2019, 1, 31, 12) + timedelta(days=1)
# datetime.datetime(2019, 2, 1, 12, 0)
but I can't find anything like timedelta in the skyfield API documentation.

What if I just wanted to use one (start) date and set the next date to be exactly one day after? I can't just add one to the day argument since this would not be correct at the end of the month.
Happily, you can just add one to the day! As the documentation says:
https://rhodesmill.org/skyfield/time.html
"you are free to provide out-of-range values and leave it to Skyfield to work out the correct result"
>>> from skyfield.api import load
>>> ts = load.timescale()
[#################################] 100% deltat.data
>>> t = ts.utc(2018, 2, 28 + 1)
>>> t.utc_jpl()
'A.D. 2018-Mar-01 00:00:00.0000 UT'

You can use datetime's timedelta and convert back between datetime and skyfield's Time objects like this:
t0 = ts.utc(2019, 1, 31, 12)
t1 = ts.utc(t0.utc_datetime() + timedelta(days=1))
# Print
t1.utc_iso()
# '2019-02-01T12:00:00Z'
While certainly not beautiful, this allows you to use all the features of Python's datetime.

Related

conditional python; Time format newer than 30 minutes

I have a variable that has a stored created date as:
2022-09-01T19:40:17.268980742Z
In python, if i wanted to look at that time and say if 'created' is within than the last 30 minutes, do X.
EDIT
I have another command I can use (working within Palo XSOAR), that will give me the current date time in ISO.
So really want I'm trying to do is say:
if created is within the last 30 minutes:
do X
Assume I have to capture current time as ISO variable (can do)
Set a variable less than 30 minutes of the current time (not sure)
then if create time is between those two values do X (not sure)
Any help is appreciated -
Thanks,
You can use datetime.now() to get the current datetime. We can then coerce your datetime string into a datetime object, too. Then, we can look at the difference and apply some logic.
import datetime
some_string = '2022-09-01T19:40:17.268980742Z'
some_string = some_string.split('.')[0]
timestamp = datetime.datetime.fromisoformat(some_string)
current_time = datetime.datetime.now()
if (current_time - timestamp) < timedelta(minutes=30):
print('x')
else:
print('y')
Here are how the variables look:
>>> print(timestamp)
datetime.datetime(2022, 9, 1, 19, 40, 17)
>>> print(current_time)
datetime.datetime(2022, 9, 5, 4, 26, 14, 345147)
>>> print(current_time - timestamp)
datetime.timedelta(days=3, seconds=31557, microseconds=345147)
Note, I wasn't able to convert the provided timestamp of 2022-09-01T19:40:17.268980742Z to a datetime object using the fromisoformat. Trimming down the microseconds six decimal places worked fine, but seven throws an error. This is expected for datetime objects as the permissable resolution is Between 0 and 999999 inclusive (src: https://docs.python.org/3/library/datetime.html).
This is why I split the string.
Works:
some_string = '2022-09-01T19:40:17.268980'
timestamp = datetime.datetime.fromisoformat(some_string)
Error:
some_string = '2022-09-01T19:40:17.2689801'
timestamp = datetime.datetime.fromisoformat(some_string)

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

How to get current time in python and break up into year, month, day, hour, minute?

I would like to get the current time in Python and assign them into variables like year, month, day, hour, minute. How can this be done in Python 2.7?
The datetime module is your friend:
import datetime
now = datetime.datetime.now()
print(now.year, now.month, now.day, now.hour, now.minute, now.second)
# 2015 5 6 8 53 40
You don't need separate variables, the attributes on the returned datetime object have all you need.
Here's a one-liner that comes in just under the 80 char line max.
import time
year, month, day, hour, min = map(int, time.strftime("%Y %m %d %H %M").split())
The datetime answer by tzaman is much cleaner, but you can do it with the original python time module:
import time
strings = time.strftime("%Y,%m,%d,%H,%M,%S")
t = strings.split(',')
numbers = [ int(x) for x in t ]
print numbers
Output:
[2016, 3, 11, 8, 29, 47]
By unpacking timetuple of datetime object, you should get what you want:
from datetime import datetime
n = datetime.now()
t = n.timetuple()
y, m, d, h, min, sec, wd, yd, i = t
Let's see how to get and print day,month,year in python from current time:
import datetime
now = datetime.datetime.now()
year = '{:02d}'.format(now.year)
month = '{:02d}'.format(now.month)
day = '{:02d}'.format(now.day)
hour = '{:02d}'.format(now.hour)
minute = '{:02d}'.format(now.minute)
day_month_year = '{}-{}-{}'.format(year, month, day)
print('day_month_year: ' + day_month_year)
result:
day_month_year: 2019-03-26
For python 3
import datetime
now = datetime.datetime.now()
print(now.year, now.month, now.day, now.hour, now.minute, now.second)
import time
year = time.strftime("%Y") # or "%y"
You can use gmtime
from time import gmtime
detailed_time = gmtime()
#returns a struct_time object for current time
year = detailed_time.tm_year
month = detailed_time.tm_mon
day = detailed_time.tm_mday
hour = detailed_time.tm_hour
minute = detailed_time.tm_min
Note: A time stamp can be passed to gmtime, default is current time as
returned by time()
eg.
gmtime(1521174681)
See struct_time
Three libraries for accessing and manipulating dates and times, namely datetime, arrow and pendulum, all make these items available in namedtuples whose elements are accessible either by name or index. Moreover, the items are accessible in precisely the same way. (I suppose if I were more intelligent I wouldn't be surprised.)
>>> YEARS, MONTHS, DAYS, HOURS, MINUTES = range(5)
>>> import datetime
>>> import arrow
>>> import pendulum
>>> [datetime.datetime.now().timetuple()[i] for i in [YEARS, MONTHS, DAYS, HOURS, MINUTES]]
[2017, 6, 16, 19, 15]
>>> [arrow.now().timetuple()[i] for i in [YEARS, MONTHS, DAYS, HOURS, MINUTES]]
[2017, 6, 16, 19, 15]
>>> [pendulum.now().timetuple()[i] for i in [YEARS, MONTHS, DAYS, HOURS, MINUTES]]
[2017, 6, 16, 19, 16]
This is an older question, but I came up with a solution I thought others might like.
def get_current_datetime_as_dict():
n = datetime.now()
t = n.timetuple()
field_names = ["year",
"month",
"day",
"hour",
"min",
"sec",
"weekday",
"md",
"yd"]
return dict(zip(field_names, t))
timetuple() can be zipped with another array, which creates labeled tuples. Cast that to a dictionary and the resultant product can be consumed with get_current_datetime_as_dict()['year'].
This has a little more overhead than some of the other solutions on here, but I've found it's so nice to be able to access named values for clartiy's sake in the code.

With the parsedatetime library in Python, is it possible to restrict a date to the current year?

Using parsedatetime, I'd like to pass a value like Jan 1 to the calendar parser and have it return Jan 1st of the current year (which, as I post this, would be 2014-01-01).
By default, parsedatetime returns the next occurrence of the date (i.e. 2015-01-01):
>>> import parsedatetime as pdt
>>> from datetime import datetime
>>> from time import mktime
>>> cal = pdt.Calendar()
>>> datetime.now()
datetime.datetime(2014, 8, 1, 15, 41, 7, 486294)
>>> str(datetime.fromtimestamp(mktime(cal.parse('Jan 1')[0])))
'2015-01-01 14:41:13'
>>> str(datetime.fromtimestamp(mktime(cal.parse('Dec 31')[0])))
'2014-12-31 14:41:17'
I've tried inputs like last Jan 1 and Jan 1 this year without success.
Is there a way to tell the parser to return the current year's value?
Editing to add a couple requirements that weren't specified with original question:
Supports natural language processing (that's why I'm using parsedatetime)
Doesn't compromise other parsedatetime parsing functionality (like years other than current and values like yesterday and 6 months before 3/1)
Bear here - have no idea how to get my original SO profile back as I used to use ClaimID...
anywho - you can set a flag to cause parsedatetime to never go forward a year when parsing only month/day values...
import parsedatetime as pdt
ptc = pdt.Constants()
ptc.YearParseStyle = 0
cal = pdt.Calendar(ptc)
print cal.parse('Jan 1')
# ((2014, 1, 1, 15, 57, 32, 5, 214, 1), 1)
The parse function appears to take a sourceTime parameter that you can set to the 1st of the current year.
See https://bear.im/code/parsedatetime/docs/index.html
I would replace the year on your datetime object. For example :
str(datetime.fromtimestamp(mktime(cal.parse('Dec 31')[0])))
would become:
str(datetime.fromtimestamp(mktime(cal.parse('Dec 31')[0])).replace(year=datetime.today().year))
If you aren't tied to using that library (and maybe you are?) you could do it like this:
>>> import datetime
>>> datetime.datetime.now().replace(month=1, day=1).strftime("%Y-%m-%d %H:%M:%S")
'2014-01-01 22:55:56'
>>>
from datetime import date, datetime
d = datetime.strptime('Jan 1', '%b %d')
d = date(datetime.now().year, d.month, d.day)
gives datetime.date(2014, 1, 1) for d, which you can then format with
print d.strftime('%Y-%m-%d')
2014-01-01
An improved implementation based on Bear's answer.
Again this is constrained by the fact that, since this is being implemented within another DSL parser, natural_date can only accept a single string:
import parsedatetime as pdt
from datetime import datetime, date, timedelta
from time import mktime
def natural_date(human_readable):
human_readable = human_readable.lower()
# Flag to cause parsedatetime to never go forward
# https://stackoverflow.com/a/25098991/1093087
ptc = pdt.Constants()
ptc.YearParseStyle = 0
cal = pdt.Calendar(ptc)
result, parsed_as = cal.parse(human_readable)
if not parsed_as:
raise ValueError("Unable to parse %s" % (human_readable))
return date.fromtimestamp(mktime(result))
def test_natural_date():
cases = [
# input, expect
('jan 1', date(date.today().year, 1, 1)),
('dec 31', date(date.today().year, 12, 31)),
('yesterday', date.today() - timedelta(days=1)),
('3 months before 12/31', date(date.today().year, 9, 30))
]
for human_readable, expect in cases:
result = natural_date(human_readable)
print("%s -> %s" % (human_readable, result))
assert result == expect, human_readable
test_natural_date()
Credit also goes to Mark Ransom, who unearthed sourceTime parameter, which provided another way to solve this issue, although that solution was complicated by this issue.

Cleanest and most Pythonic way to get tomorrow's date?

What is the cleanest and most Pythonic way to get tomorrow's date? There must be a better way than to add one to the day, handle days at the end of the month, etc.
datetime.date.today() + datetime.timedelta(days=1) should do the trick
timedelta can handle adding days, seconds, microseconds, milliseconds, minutes, hours, or weeks.
>>> import datetime
>>> today = datetime.date.today()
>>> today
datetime.date(2009, 10, 1)
>>> today + datetime.timedelta(days=1)
datetime.date(2009, 10, 2)
>>> datetime.date(2009,10,31) + datetime.timedelta(hours=24)
datetime.date(2009, 11, 1)
As asked in a comment, leap days pose no problem:
>>> datetime.date(2004, 2, 28) + datetime.timedelta(days=1)
datetime.date(2004, 2, 29)
>>> datetime.date(2004, 2, 28) + datetime.timedelta(days=2)
datetime.date(2004, 3, 1)
>>> datetime.date(2005, 2, 28) + datetime.timedelta(days=1)
datetime.date(2005, 3, 1)
No handling of leap seconds tho:
>>> from datetime import datetime, timedelta
>>> dt = datetime(2008,12,31,23,59,59)
>>> str(dt)
'2008-12-31 23:59:59'
>>> # leap second was added at the end of 2008,
>>> # adding one second should create a datetime
>>> # of '2008-12-31 23:59:60'
>>> str(dt+timedelta(0,1))
'2009-01-01 00:00:00'
>>> str(dt+timedelta(0,2))
'2009-01-01 00:00:01'
darn.
EDIT - #Mark: The docs say "yes", but the code says "not so much":
>>> time.strptime("2008-12-31 23:59:60","%Y-%m-%d %H:%M:%S")
(2008, 12, 31, 23, 59, 60, 2, 366, -1)
>>> time.mktime(time.strptime("2008-12-31 23:59:60","%Y-%m-%d %H:%M:%S"))
1230789600.0
>>> time.gmtime(time.mktime(time.strptime("2008-12-31 23:59:60","%Y-%m-%d %H:%M:%S")))
(2009, 1, 1, 6, 0, 0, 3, 1, 0)
>>> time.localtime(time.mktime(time.strptime("2008-12-31 23:59:60","%Y-%m-%d %H:%M:%S")))
(2009, 1, 1, 0, 0, 0, 3, 1, 0)
I would think that gmtime or localtime would take the value returned by mktime and given me back the original tuple, with 60 as the number of seconds. And this test shows that these leap seconds can just fade away...
>>> a = time.mktime(time.strptime("2008-12-31 23:59:60","%Y-%m-%d %H:%M:%S"))
>>> b = time.mktime(time.strptime("2009-01-01 00:00:00","%Y-%m-%d %H:%M:%S"))
>>> a,b
(1230789600.0, 1230789600.0)
>>> b-a
0.0
Even the basic time module can handle this:
import time
time.localtime(time.time() + 24*3600)
For people who are dealing with servers Time Stamp
To get yesterday Time Stamp:
yesterdaytimestamp = datetime.datetime.today() + datetime.timedelta(days=-1)
To get Today Time Stamp:
currenttimestamp = datetime.datetime.now().timestamp()
To get Tomorrow Time Stamp:
tomorrowtimestamp = datetime.datetime.today() + datetime.timedelta(days=1)
To print:
print('\n Yesterday TimeStamp is : ', yesterdaytimestamp.timestamp(),
'\n Today TimeStamp is :', currenttimestamp,
'\n Tomorrow TimeStamp is: ', tomorrowtimestamp.timestamp())
The output:
Yesterday TimeStamp is : 1632842904.110993
Today TimeStamp is : 1632929304.111022
Tomorrow TimeStamp is : 1633015704.11103
There's nothing at all wrong with using today() as shown in the selected answer if that is the extent of your needs.
datetime.date.today() + datetime.timedelta(days=1)
Alternatively, if you or someone else working with your code might need more precision in handling tomorrow's date, consider using datetime.now() instead of today(). This will certainly allow for simpler, more readable code:
datetime.datetime.now() + datetime.timedelta(days=1)
This returns something like:
datetime.datetime(2022, 2, 17, 19, 50, 19, 984925)
The advantage is that you can now work with datetime attributes in a concise, human readable way:
class datetime.datetime
A combination of a date and a time. Attributes: year, month, day, hour, minute, second, microsecond, and tzinfo.
Examples
You can easily convert this to a date object withdate():
import datetime
tomorrow = datetime.datetime.now() + datetime.timedelta(days=1)
print(f"Tomorrow's date is {tomorrow.date()}")
tomorrow.date() is easy to use and it is very clear to anyone reading your code that it is returning the date for tomorrow. The output for the above looks like so:
Tomorrow's date is 2022-02-17
If later in your code you only need the date number for the day, you can now use tomorrow.day:
print(f"Tomorrow is the {tomorrow.day}rd")
Which will return something like:
Tomorrow is the 17rd
That's a silly example, but you can see how having access to these attributes can be useful and keep your code readable as well. It can be easily understood that tomorrow.day returns the day number.
Need to work with the exact time tomorrow's date begins? You can now replace the hours, minutes, seconds, and microseconds:
# Replace all attributes except day with 0.
midnight = tomorrow.replace(
hour=0,
minute=0,
second=0,
microsecond=0)
# Print midnight as the beginning of tomorrow's date.
print(f"{midnight}")
Reading the above code, it should be apparent which attributes of tomorrow are being replaced. When midnight is printed, it will output:
2022-02-17 00:00:00
Need to know the time left until tomorrow's date? Now something like that is possible, simple, and readable:
print(f"{midnight - datetime.datetime.now()}")
The output is the time to the microsecond that tomorrow's date begins:
3:14:28.158331
There are many ways people might wish to handle tomorrow's date. By ensuring these attributes are available from the beginning, you can write more readable code and avoid unnecessary work later.
For the case you only want to calculate the timestamp
import time
tomorrow = (int(time.time() / 86400) + 1) * 86400

Categories