Convert Year/Month/Day to Day of Year in Python

I’m using the datetime module, i.e.:

>>> import datetime
>>> today = datetime.datetime.now()
>>> print(today)
2009-03-06 13:24:58.857946

and I would like to compute the day of year that takes leap years into account. e.g. today (March 6, 2009) is the 65th day of 2009.

I see a two options:

  1. Create a number_of_days_in_month = [31, 28, ...] array, decide if it’s a leap year and manually sum up the days.
  2. Use datetime.timedelta to make a guess & then binary search for the correct day of the year:
    >>> import datetime
    >>> YEAR = 2009
    >>> DAY_OF_YEAR = 62
    >>> d = datetime.date(YEAR, 1, 1) + datetime.timedelta(DAY_OF_YEAR - 1)

These both feel pretty clunky & I have a gut feeling that there’s a more “Pythonic” way of calculating the day of the year. Any ideas/suggestions?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Use datetime.timetuple() to convert your datetime object to a time.struct_time object then get its tm_yday property:

from datetime import datetime
day_of_year = datetime.now().timetuple().tm_yday  # returns 1 for January 1st

Method 2

You could use strftime with a %j format string:

>>> import datetime
>>> today = datetime.datetime.now()
>>> today.strftime('%j')
'065'

but if you wish to do comparisons or calculations with this number, you would have to convert it to int() because strftime() returns a string. If that is the case, you are better off using DzinX’s answer.

Method 3

DZinX’s answer is a great answer for the question. I found this question and used DZinX’s answer while looking for the inverse function: convert dates with the julian day-of-year into the datetimes.

I found this to work:

import datetime
datetime.datetime.strptime('1936-077T13:14:15','%Y-%jT%H:%M:%S')

>>>> datetime.datetime(1936, 3, 17, 13, 14, 15)

datetime.datetime.strptime('1936-077T13:14:15','%Y-%jT%H:%M:%S').timetuple().tm_yday

>>>> 77

Or numerically:

import datetime
year,julian = [1936,77]
datetime.datetime(year, 1, 1)+datetime.timedelta(days=julian -1)

>>>> datetime.datetime(1936, 3, 17, 0, 0)

Or with fractional 1-based jdates popular in some domains:

jdate_frac = (datetime.datetime(1936, 3, 17, 13, 14, 15)-datetime.datetime(1936, 1, 1)).total_seconds()/86400+1
display(jdate_frac)

>>>> 77.5515625

year,julian = [1936,jdate_frac]
display(datetime.datetime(year, 1, 1)+datetime.timedelta(days=julian -1))

>>>> datetime.datetime(1936, 3, 17, 13, 14, 15)

I’m not sure of etiquette around here, but I thought a pointer to the inverse functionality might be useful for others like me.

Method 4

If you have reason to avoid the use of the datetime module, then these functions will work.

def is_leap_year(year):
    """ if year is a leap year return True
        else return False """
    if year % 100 == 0:
        return year % 400 == 0
    return year % 4 == 0

def doy(Y,M,D):
    """ given year, month, day return day of year
        Astronomical Algorithms, Jean Meeus, 2d ed, 1998, chap 7 """
    if is_leap_year(Y):
        K = 1
    else:
        K = 2
    N = int((275 * M) / 9.0) - K * int((M + 9) / 12.0) + D - 30
    return N

def ymd(Y,N):
    """ given year = Y and day of year = N, return year, month, day
        Astronomical Algorithms, Jean Meeus, 2d ed, 1998, chap 7 """    
    if is_leap_year(Y):
        K = 1
    else:
        K = 2
    M = int((9 * (K + N)) / 275.0 + 0.98)
    if N < 32:
        M = 1
    D = N - int((275 * M) / 9.0) + K * int((M + 9) / 12.0) + 30
    return Y, M, D

Method 5

I want to present performance of different approaches, on Python 3.4, Linux x64. Excerpt from line profiler:

      Line #      Hits         Time  Per Hit   % Time  Line Contents
      ==============================================================
         (...)
         823      1508        11334      7.5     41.6          yday = int(period_end.strftime('%j'))
         824      1508         2492      1.7      9.1          yday = period_end.toordinal() - date(period_end.year, 1, 1).toordinal() + 1
         825      1508         1852      1.2      6.8          yday = (period_end - date(period_end.year, 1, 1)).days + 1
         826      1508         5078      3.4     18.6          yday = period_end.timetuple().tm_yday
         (...)

So most efficient is

yday = (period_end - date(period_end.year, 1, 1)).days + 1

Method 6

Just subtract january 1 from the date:

import datetime
today = datetime.datetime.now()
day_of_year = (today - datetime.datetime(today.year, 1, 1)).days + 1


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x