• 12
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

name Punditsdkoslkdosdkoskdo

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

I'm using the Python 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 is sensitive of leap years. e.g. today (March 6, 2009) is the 65th day of 2009. Here's web-based DateTime calculator.

Anyway, I see a two options:

A. Create a number_of_days_in_month array = [31, 28, ...], decide if it's a leap year, manually sum up the days.

B. Use datetime.timedelta to make a guess & then binary search for the correct day of 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 day of year. Any ideas/suggestions?

There is a very simple solution:

from datetime import datetime
day_of_year = datetime.now().timetuple().tm_yday
  • 254
Reply Report
    • A very minor and arguably pedantic addition, but using date.today() rather than datetime.now() also works and emphasizes the nature of the operation a bit more.
      • 1
    • Better yet: time.localtime().tm_yday No need to convert a datetime to a timetuple since that's what localtime() yields.
      • 1
    • what if i want to do the reverse, I have the number lets say "26th day of the year 2020", and I want to have it converted into a date "26-01-2020"?

Couldn't you use strftime?

>>> import datetime
>>> today = datetime.datetime.now()
>>> print today
2009-03-06 15:37:02.484000
>>> today.strftime('%j')
'065'

Edit

As noted in the comments, 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.

  • 43
Reply Report
    • -1: icky. Better is the "today minus january 1st" algorithm. Much cleaner and perfectly obvious without looking up a piece of trivia about strftime's '%j'.
      • 2
    • Seriously? How is this icky and substracting january 1st isn't? strftime is there for a reason. I just don't agree. This is WAY cleaner in my opinion.
    • Using strftime is indirect, because it produces a string from a number: the timetuple.tm_yday member. Read the source. The produced string should be converted to a number before any calculations/comparisons, so why bother?

DZinX's answer is a great answer for the question. I found this question while looking for the inverse function. 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

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

  • 13
Reply Report

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
  • 6
Reply Report
      • 1
    • How did you calculate the performance? I tried using time.time and those are my results: def f(): (today - datetime.datetime(today.year, 1, 1)).days + 1 start = time.time(); f(); print('Lapsed {}'.format( time.time() - start)); Lapsed 5.221366882324219e-05 def f(): int(today.strftime('%j')); start = time.time(); f(); print('Lapsed {}'.format( time.time() - start)); Lapsed 7.462501525878906e-05

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
  • 5
Reply Report
      • 2
    • Nothing against Paolo, but datetime.timedelta is denominated in days (as well as seconds and microseconds, of course), so it's a natural choice---certainly more direct than string formatting
      • 1
    • d.toordinal() - date(d.year, 1, 1).toordinal() + 1 is more standard according to the documentation. It is equivalent to the accepted answer without producing the whole time tuple.

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
  • 5
Reply Report

This code takes a date as an input argument and returns the day of the year.

from datetime import datetime
date = raw_input("Enter date: ")  ## format is 02-02-2016
adate = datetime.datetime.strptime(date,"%d-%m-%Y")
day_of_year = adate.timetuple().tm_yday
day_of_year
  • 1
Reply Report

Trending Tags