Calculating julian date in python - python-2.7

I'm trying to create a julian date in python and having major struggles. Is there nothing out there as simple as:
jul = juliandate(year,month,day,hour,minute,second)
where jul would be something like 2457152.0 (the decimal changing with the time)?
I've tried jdcal, but can't figure out how to add the time component (jdcal.gcal2jd() only accepts year, month and day).

Not a pure Python solution but you can use the SQLite in memory db which has a julianday() function:
import sqlite3
con = sqlite3.connect(":memory:")
list(con.execute("select julianday('2017-01-01')"))[0][0]
which returns: 2457754.5

Here you go - a pure python solution with datetime and math library.
This is based on the the Navy's Astronomical Equation found here and verified with their own calculator: http://aa.usno.navy.mil/faq/docs/JD_Formula.php
import datetime
import math
def get_julian_datetime(date):
"""
Convert a datetime object into julian float.
Args:
date: datetime-object of date in question
Returns: float - Julian calculated datetime.
Raises:
TypeError : Incorrect parameter type
ValueError: Date out of range of equation
"""
# Ensure correct format
if not isinstance(date, datetime.datetime):
raise TypeError('Invalid type for parameter "date" - expecting datetime')
elif date.year < 1801 or date.year > 2099:
raise ValueError('Datetime must be between year 1801 and 2099')
# Perform the calculation
julian_datetime = 367 * date.year - int((7 * (date.year + int((date.month + 9) / 12.0))) / 4.0) + int(
(275 * date.month) / 9.0) + date.day + 1721013.5 + (
date.hour + date.minute / 60.0 + date.second / math.pow(60,
2)) / 24.0 - 0.5 * math.copysign(
1, 100 * date.year + date.month - 190002.5) + 0.5
return julian_datetime
Usage Example:
# Set the same example as the Naval site.
example_datetime = datetime.datetime(1877, 8, 11, 7, 30, 0)
print get_julian_datetime(example_datetime)

Answer one from Extract day of year and Julian day from a string date in python. No libraries required.
Answer two is a library from https://pypi.python.org/pypi/jdcal

The easiest: df['Julian_Dates']= df.index.to_julian_date(). you need to set your date time column to index (Use Pandas).
There is a way with using Astropy. First, change your time to a list (t). Second, change that list to astropy time (Time). Finally, compute your JD or MJD (t.jd t.mjd).
https://docs.astropy.org/en/stable/time/
For df:
t = Time(DF.JulianDates,format='jd',scale='utc')

A simple fudge the numbers script via wiki don't know either. Please note that this was written using Python 3.6 so I'm not sure it would work on Python 2.7 but this is also an old question.
def julian_day(now):
"""
1. Get current values for year, month, and day
2. Same for time and make it a day fraction
3. Calculate the julian day number via https://en.wikipedia.org/wiki/Julian_day
4. Add the day fraction to the julian day number
"""
year = now.year
month = now.month
day = now.day
day_fraction = now.hour + now.minute / 60.0 + now.second / 3600.0 / 24.0
# The value 'march_on' will be 1 for January and February, and 0 for other months.
march_on = math.floor((14 - month) / 12)
year = year + 4800 - march_on
# And 'month' will be 0 for March and 11 for February. 0 - 11 months
month = month + 12 * march_on - 3
y_quarter = math.floor(year / 4)
jdn = day + math.floor((month * 153 + 2) / 5) + 365 * year + y_quarter
julian = year < 1582 or year == (1582 and month < 10) or (month == 10 and day < 15)
if julian:
reform = 32083 # might need adjusting so needs a test
else:
reform = math.floor(year / 100) + math.floor(year / 400) + 32030.1875 # fudged this
return jdn - reform + day_fraction
Generally this was just to try for myself as the most common algorithm was giving me trouble. That works and if you search around for it and write your script using it as it comes in many languages. But this one has steps in the docs to try to keep it simple. The biggest decision is how often are you going to look for dates that are before Gregorian reform. That is why I never tested that yet but go ahead and play with it as it needs a lot of massaging. :-D At least I think is conforms to PEP8 even if it isn't up to best practices. Go ahead and pylint it.
You could just use source packages like PyEphem or whatever but you still would like to know what's going on with it so you could write your own tests. I'll link that PyEphem for you but there are lots of ready made packages that have Julian Day calculations.
Your best bet if you are doing lots of work with these types of numbers is to get a list of the constant ones such as J2000.
datetime.datetime(2000, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)
datetime.datetime.toordinal() + 1721425 - 0.5 # not tested
# or even
datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
It's not so hard to figure these out if you get familiar with what datetime library does. Just for fun did you notice the PyEphem logo? I suspect it comes from something like this
One post that I saw seems to work but has no tests is jiffyclub
Now here is the more common way to calculate two values using a datetime object.
def jdn(dto):
"""
Given datetime object returns Julian Day Number
"""
year = dto.year
month = dto.month
day = dto.day
not_march = month < 3
if not_march:
year -= 1
month += 12
fr_y = math.floor(year / 100)
reform = 2 - fr_y + math.floor(fr_y / 4)
jjs = day + (
math.floor(365.25 * (year + 4716)) + math.floor(30.6001 * (month + 1)) + reform - 1524)
if jjs < ITALY:
jjs -= reform
return jjs
# end jdn
def ajd(dto):
"""
Given datetime object returns Astronomical Julian Day.
Day is from midnight 00:00:00+00:00 with day fractional
value added.
"""
jdd = jdn(dto)
day_fraction = dto.hour / 24.0 + dto.minute / 1440.0 + dto.second / 86400.0
return jdd + day_fraction - 0.5
# end ajd
It may not be the best practice in Python but you did ask how to calculate it not just get it or extract it although if that is what you want those questions have been answered as of late.

Try https://www.egenix.com/products/python/mxBase/mxDateTime/
First construct a DateTime object via the syntax
DateTime(year,month=1,day=1,hour=0,minute=0,second=0.0)
Then you can use '.jdn' object method to get the value you are looking for.

Related

python - Difference between two unix timestamps

I have two timestamps in miliseconds and i want to compute the difference between the two in minutes:
d1 = 1502053449617
current_time_utc = int(round(time.time() * 1000))
The values for d1 are dynamically generated by a third party API and are in UTC . I am trying to get the difference between the current time in UTC and d1.
fmt = '%Y-%m-%d %H:%M:%S'
time1 = datetime.strptime(d1, fmt)
time2 = datetime.strptime(current_time_utc, fmt)
I want to be able to find the difference between the two (time1 - time2) . If i do the below , i get an error saying "string expected, long given"
print( time1-time2)
I want the difference between the two in minutes . Please help
You don't need to format the string, you just need to convert the timestamp directly, by first dividing it by 1000. Then its just a matter of printing out the differences (and calculating it in minutes):
from __future__ import division
import datetime
d1 = 1502053449617
converted_d1 = datetime.datetime.fromtimestamp(round(d1 / 1000))
current_time_utc = datetime.datetime.utcnow()
print((current_time_utc - converted_d1))
print((current_time_utc - converted_d1).total_seconds() / 60)
The above prints:
3 days, 5:08:14.087515
4628.234791916667
I had to calculate the difference between two unix timestamps - but in days, as follows:
create two unix timestamps:
import datetime
timestamp1 = datetime.datetime(2017, 12, 1).strftime('%s')
timestamp2 = datetime.datetime(2017, 11, 14).strftime('%s')
print(timestamp1)
print(timestamp2)
1512079200
1510610400
calculate the day difference:
print((float(timestamp1)-float(timestamp2))/(60*60*24))
output:
17.0

juliandate to normaldate in redshift

I have date like 117106, 117107 in an column which is of numeric type in redshift data base. Understood that the format is in Julian format. I wanted to change it to normal date format like yyyymmdd.
I tried applying the function to the column and it returns the value as below
select to_date(117106) - result 4393-07-10
Please help.
Thanks in advance
Here is how it is done.
The way it works is the first 3 digits is the century julian offset, and the last 3 are the day offset:
select dateadd(day,117106 % 1000,dateadd(year,(117106 /1000),convert(datetime,'01/01/1900')))-1
If I’ve made a bad assumption please comment and I’ll refocus my answer.
Thank you Rahul for the help.
I haven't tried the solution provided.However i have implemented the below solution as below to convert it into date format
trim(cast(to_char(dateadd(days,cast(right(x)as bigint)
+ datediff(days,'1900-01-02',to_date(cast(left((1900+(x/1000)),4) as char(4)) || '-01' || '-01','yyyy-mm-dd')),'1900-01-01'),
'YYYYMMDD')as decimal),0) as x
Can generate_series() to cover the julian day range you need and then use standard date functions
with julian_day as (
select generate_series as julian_day,
to_date(generate_series, 'J') as gregorian_date
from generate_series((2459865 - ( 10 * 365)), (2459865 + (10 * 365)), 1)
)
select
julian_day,
gregorian_date,
to_char(gregorian_date, 'IYYY') as iso_year,
date_part(year, gregorian_date) as year,
...
from julian_day

Is there a native way to convert to UTC time in UniVerse 11.2.4+?

The release notes for UniVerse version 11.2.4 mention local time zone configuration, but it is in the context of auditing. This is the quote:
Local time zone configuration
Prior to UniVerse 11.2.4, the date and time data stored in the audit log records was based on UTC only.
Beginning at UniVerse 11.2.4, UniVerse adds the date and time data
based on local timezone to audit log records. The data is stored in
location 19 for each record. The dictionary name for this data field
is TZINFO. For more information, see UniVerse Security Features.
Since UniVerse seems capable of working with time zones natively, does this mean there might be a way to easily generate UTC-formatted date/time stamps from my EST/EDT values?
I am sending data to a system that wants dates formatted in ISO-8601 Date/Time format yyyy-MMddTHH:mm:ssZ, like 2015-06-02T15:55:22Z, with the time zone and Daylight Saving Time offsets accounted for.
I dug through the Security Features guide, and found this:
UniVerse also adds a globally cataloged program to help users to
obtain date and time information from the audit log (which is called
by the above two I-descriptor fields):
SUBROUTINE GETLOCALTIME
(
RESULT ;* OUT: output
TZOFF ;* IN: time zone offset
DATE ;* IN: UTC date
TIME ;* IN: UTC time
OP ;* IN: operation
;* 1: get local date
;* 2: get local time
;* 3: get local timezone id
;* 4: get local timezone daylight saving flag
)
(Since I'm not using the auditing capabilities of UniVerse, I don't think I can do much with this, nor could I locate the subroutine.)
I have also played with the popular(?) DATE.UTILITY program from PickWiki, but its calculation of Daylight Saving Time start/end dates seem off. I will save those issues for another question.
This is getting long-winded but I'm hoping someone can point me in the right direction if there's a new OCONV() parameter or something I could use.
Just in case it matters, I'm running on Windows Server 2008 R2.
Thanks!
Time is a complicated thing. Socially we have accepted that it not only acceptable to alter it 2 times a year, we have mandated it! This is all well and good for us meat machines who only want to understand time when it is convenient for us however it does cause us to get grumpy when out reporting "looks funny".
The solution to your problem is not exceptionally easy, especially if you are working with already recorded dates. Dates and times in Universe are generally recorded based on local system time. If this is something that you are trying to do going forward you have to note what the offset is at the time of the transaction or simply stamp things SYSTEM(99), which complicated pretty much all other reporting you will need to do. Either way, this is a complicated matter and it still likely to be somewhat imperfect.
Here is a little something that might help you if you are the one in charge of recording dates, going forward.
SECONDS.SINCE.GMT.01.01.1970 = SYSTEM(99)
CRT SECONDS.SINCE.GMT.01.01.1970:" Seconds since GMT Epoch Began"
NUMBER.OF.DAYS.SINCE.01.01.1970 = DATE() -732
;* Day 0 in Pick is 12/31/1967 because Dick Pick so we subtract 732 from the pick date
SECONDS.SINCE.MIDNIGHT.LOCAL= TIME()
SECS.PER.DAY = 24 * 60 * 60
LOCAL.SECONDS.SINCE.GMT.01.01.1970 = NUMBER.OF.DAYS.SINCE.01.01.1970 * SECS.PER.DAY + FIELD(SECONDS.SINCE.MIDNIGHT.LOCAL,".",1)
;*I drop the precision
CRT LOCAL.SECONDS.SINCE.GMT.01.01.1970: " Seconds since 01/01/1970 in local time"
OFFSET = (LOCAL.SECONDS.SINCE.GMT.01.01.1970 - SECONDS.SINCE.GMT.01.01.1970)
CRT "CURRENT.OFFSET IS ":INT((OFFSET / 60 )/ 60)
END
Which outputs the following on my system which is currently PDT (even though OCONV(DATE(),'DZ') reports it as PST.
1434472817 Seconds since GMT Epoch Began
1434447617 Seconds since 01/01/1970 in local time
CURRENT.OFFSET IS -7
Hopefully you have found this helpful.
Thanks for the clues. Here's my implementation:
SUBROUTINE FORMAT.ISO.8601 (IDATE, ITIME, RESULT, ERR.TEXT)
* Don't step on the caller's variables.
IN.DATE = IDATE
IN.TIME = ITIME
* Initialize the outbound variable.
RESULT = ''
IF NOT(NUM(IN.DATE)) THEN
ERR.TEXT = 'Non-numeric internal date ' : DQUOTE(IN.DATE) : ' when numeric required.'
RETURN
END
IF NOT(NUM(IN.DATE)) THEN
ERR.TEXT = 'Non-numeric internal time ' : DQUOTE(IN.TIME) : ' when numeric required.'
RETURN
END
* SYSTEM(99) is based on 1/1/1970.
SECONDS.SINCE.GMT.01.01.1970 = SYSTEM(99)
* Day 0 in Pick is 12/31/1967
* Subtract 732 to equalize the starting dates.
NUMBER.OF.DAYS.SINCE.01.01.1970 = DATE() - 732
SECONDS.SINCE.MIDNIGHT.LOCAL= TIME()
SECS.PER.DAY = 24 * 60 * 60
LOCAL.SECONDS.SINCE.GMT.01.01.1970 = NUMBER.OF.DAYS.SINCE.01.01.1970 * SECS.PER.DAY + FIELD(SECONDS.SINCE.MIDNIGHT.LOCAL,".",1)
OFFSET = LOCAL.SECONDS.SINCE.GMT.01.01.1970 - SECONDS.SINCE.GMT.01.01.1970
OFFSET = INT((OFFSET / 60 )/ 60)
OTIME = OCONV(IN.TIME, 'MTS')
IF OTIME = '' THEN
ERR.TEXT = 'Bad internal time ' : DQUOTE(IN.TIME) : '.'
RETURN
END
HOURS = FIELD(OTIME, ':', 1)
MINUTES = FIELD(OTIME, ':', 2)
SECONDS = FIELD(OTIME, ':', 3)
HOURS -= OFFSET
IF HOURS >= 24 THEN
IN.DATE += 1
HOURS = HOURS - 24
END
HOURS = HOURS 'R%2'
ODATE = OCONV(IN.DATE, 'D4/')
IF ODATE = '' THEN
ERR.TEXT = 'Bad internal date ' : DQUOTE(IN.DATE) : '.'
RETURN
END
DMONTH = FIELD(ODATE, '/', 1)
DDAY = FIELD(ODATE, '/',2)
DYEAR = FIELD(ODATE, '/',3)
RESULT = DYEAR : '-' : DMONTH : '-' : DDAY : 'T' : HOURS : ':' : MINUTES : ':' : SECONDS : 'Z'
RETURN
END
Here's my test harness:
CRT 'Testing right now.'
IDATE = DATE()
ITIME = TIME()
CALL FORMAT.ISO.8601 (IDATE, ITIME, RESULT, ERR.TEXT)
IF ERR.TEXT THEN
CRT 'ERR.TEXT: ' : ERR.TEXT
END ELSE
CRT 'RESULT: ' : RESULT
END
CRT
CRT 'Testing an hour ago.'
IDATE = DATE()
ITIME = TIME()
ITIME = ITIME - (60*60)
IF ITIME < 0 THEN
ITIME += (24*60*60)
IDATE -= 1
END
CALL FORMAT.ISO.8601 (IDATE, ITIME, RESULT, ERR.TEXT)
IF ERR.TEXT THEN
CRT 'ERR.TEXT: ' : ERR.TEXT
END ELSE
CRT 'RESULT: ' : RESULT
END
CRT
CRT 'Testing an hour from now.'
IDATE = DATE()
ITIME = TIME()
ITIME = ITIME + (60*60)
IF ITIME > (24*60*60) THEN
ITIME -= (24*60*60)
IDATE += 1
END
CALL FORMAT.ISO.8601 (IDATE, ITIME, RESULT, ERR.TEXT)
IF ERR.TEXT THEN
CRT 'ERR.TEXT: ' : ERR.TEXT
END ELSE
CRT 'RESULT: ' : RESULT
END
END
Here's my test run:
>T$FORMAT.ISO.8601
Testing right now.
RESULT: 2017-03-29T00:47:22Z
Testing an hour ago.
RESULT: 2017-03-28T23:47:22Z
Testing an hour from now.
RESULT: 2017-03-29T01:47:22Z

Getting upcoming birthdays using 'date of birth' DateField

I'm trying to get the birthdays in the upcoming 20 days, given the below Person model:
class Person(models.Model):
dob = models.DateField() # date of birth
There are similar questions on SO already (here and here), but these do not cover my use case, as I'm storing a date of birth instead of the next birthday or a timefield.
I've tried to do some things like the following:
from datetime import timedelta, date
today = date.today()
next_20_days = today+timedelta(days=20)
Person.objects.filter(dob__month=today.month, dob__day__range=[today.day, next_20_days.day])
... but I get FieldError: Unsupported lookup 'day' for DateField or join on the field not permitted.
When I do e.g. Person.objects.filter(dob__month=today.month, dob__day=next_20_days.day), I do get the results for exactly 20 days from now. So I potentially could go over each of the 20 days in a loop, but that seems rather ineffective.
Any idea on how to do this the proper way?
FYI, I ended up doing the following which works for me and which does not require raw SQL.
Any improvements would be welcomed :-)
# Get the upcoming birthdays in a list (which is ordered) for the amount of days specified
def get_upcoming_birthdays(person_list, days):
person_list= person_list.distinct() # ensure persons are only in the list once
today = date.today()
doblist = []
doblist.extend(list(person_list.filter(dob__month=today.month, dob__day=today.day)))
next_day = today + timedelta(days=1)
for day in range(0, days):
doblist.extend(list(person_list.filter(dob__month=next_day.month, dob__day=next_day.day, dod__isnull=True)))
next_day = next_day + timedelta(days=1)
return doblist
Caveat: I believe calendars and time is hard. As a result, I feel obligated to warn you that I haven't rigorously tested my proposal. But of course, I think it should work. :)
Unfortunately, I think you should abandon date objects as the additional complication of year data precludes easy selects. Rather, I propose storing the birthday as a MMDD string (comparison of strings works, as long as you format them consistently). You can then compute your next_20_days and convert that to a similar MMDD string, as well as today, then use them as values to compare against.
I have three edge cases you should definitely make sure work:
Normal month rollover. (e.g., June to July)
Leap days -- don't forget to check presence as well as absence of Feb 29.
Year boundary -- you'll need to either do two queries and union the results, or do an OR query using Q objects.
Edit: See also:
How to store birthdays without a year part?
SQL Select Upcoming Birthdays
mySQL SELECT upcoming birthdays
and so on. I just did a Google search for "stack overflow birthday select".
I have been struggling with the same issue for the past days. I think I assembled a pretty solid solution that should allow you easily to derive all the birthdays to come up for the next X days. This query runs against the database-table geburtstage (birthdays) with the following 4 fields: ID (set as primary key) vorname (firstname), nachname (lastname) and geburtstag (birthday). Just create the table, fill in some records and run the query below:
select * FROM (
select curdate() AS today, DAY(CURDATE()) AS d_T, MONTH(CURDATE()) AS m_T, DAY(geburtstag) AS d_G, MONTH(geburtstag) AS m_G, subdate(CURDATE(),-20) AS date_20, DAY(subdate(CURDATE(),-20)) AS d_20, MONTH(subdate(CURDATE(),-20)) AS m_20, vorname, nachname, geburtstag, (YEAR(CURRENT_TIMESTAMP) - YEAR(geburtstag) +1 - CASE WHEN MONTH(CURRENT_TIMESTAMP) < MONTH(geburtstag) THEN 1 WHEN MONTH(CURRENT_TIMESTAMP) > MONTH(geburtstag) THEN 0 WHEN DAY(CURRENT_TIMESTAMP) <= DAY(geburtstag) THEN 1 ELSE 0 END) AS age, datediff(DATE_FORMAT(geburtstag,concat('%',YEAR(CURDATE()),'-%m-%d')),NOW()) AS no_of_days FROM geburtstage
union
select curdate() AS today, DAY(CURDATE()) AS d_T, MONTH(CURDATE()) AS m_T, DAY(geburtstag) AS d_G, MONTH(geburtstag) AS m_G, subdate(CURDATE(),-20) AS date_20, DAY(subdate(CURDATE(),-20)) AS d_20, MONTH(subdate(CURDATE(),-20)) AS m_20, vorname, nachname, geburtstag, (YEAR(CURRENT_TIMESTAMP) - YEAR(geburtstag) +1 - CASE WHEN MONTH(CURRENT_TIMESTAMP) < MONTH(geburtstag) THEN 1 WHEN MONTH(CURRENT_TIMESTAMP) > MONTH(geburtstag) THEN 0 WHEN DAY(CURRENT_TIMESTAMP) <= DAY(geburtstag) THEN 1 ELSE 0 END) AS age, datediff(DATE_FORMAT(geburtstag,concat('%',(YEAR(CURDATE())+1),'-%m-%d')),NOW()) AS no_of_days FROM geburtstage) AS upcomingbirthday
WHERE no_of_days >=0 AND no_of_days <= 20 GROUP BY ID
ORDER BY (m_G, d_G) < (m_T, d_T), m_G, d_G, geburtstag desc, age

Struggling with syntax with If....Then statements in model, Rails 3.2.13

I'm trying to write an equation that calculates how long an employee has been hired to determine how much Vacation time they are eligible for. New hires get 10, after six years of employment they get an extra day a year, capping off at 10 extra days (on their 16th year). Some of these equations worked individually, but they don't work all together. So I'm having a syntax problem I think.
undefined method `-' for nil:NilClass
The vacation_days section is what is breaking my app.
class Employee < ActiveRecord::Base
def years_employed
(DateTime.now - hire_date).round / 365
end
def vacation_days
if years_employed <= 6
10
end
if years_employed > 6
(years_employed.to_i - 6) + 10
end
if years_employed > 16
(years_employed * 0) + 20
end
end
end
Also, if you have any advice on a better way to go about this, please instruct me!
You don't want ends, you want elses, otherwise it's going to keep evaluating–so you were returning nil sometimes. Roughly:
def vacation_days
if years_employed <= 6
10
elsif years_employed <= 16
years_employed + 4
else
20
end
end
just as an alternative you can use a case statement along with ranges
def vacation_days
case years_employed
when 0..6 then 10
when 7..16 then years_employed+4
else 20
end
end