Determine season given timestamp in Python using datetime
Asked Answered
C

12

14

I'd like to extract only the month and day from a timestamp using the datetime module (not time) and then determine if it falls within a given season (fall, summer, winter, spring) based on the fixed dates of the solstices and equinoxes.

For instance, if the date falls between March 21 and June 20, it is spring. Regardless of the year. I want it to just look at the month and day and ignore the year in this calculation.

I've been running into trouble using this because the month is not being extracted properly from my data, for this reason.

Centra answered 22/4, 2013 at 4:10 Comment(2)
What are you having trouble with?Await
For instance, if the date falls between March 21 and June 20, it is spring. Regardless of the year. I want it to just look at the month and day and ignore the year in this calculation.Centra
S
23

if the date falls between March 21 and June 20, it is spring. Regardless of the year. I want it to just look at the month and day and ignore the year in this calculation.

#!/usr/bin/env python
from datetime import date, datetime

Y = 2000 # dummy leap year to allow input X-02-29 (leap day)
seasons = [('winter', (date(Y,  1,  1),  date(Y,  3, 20))),
           ('spring', (date(Y,  3, 21),  date(Y,  6, 20))),
           ('summer', (date(Y,  6, 21),  date(Y,  9, 22))),
           ('autumn', (date(Y,  9, 23),  date(Y, 12, 20))),
           ('winter', (date(Y, 12, 21),  date(Y, 12, 31)))]

def get_season(now):
    if isinstance(now, datetime):
        now = now.date()
    now = now.replace(year=Y)
    return next(season for season, (start, end) in seasons
                if start <= now <= end)

print(get_season(date.today()))

It is an extended version of @Manuel G answer to support any year.

Scaife answered 24/2, 2015 at 5:25 Comment(1)
Yes, split the season that intersects the newyear (=season.start.year != season.end.year), set all year numbers to 2000, now you can easily compare the datesEmigrate
R
19

It might be easier just to use the day of year parameter. It's not much different than your approach, but possibly easier to understand than the magic numbers.

# get the current day of the year
doy = datetime.today().timetuple().tm_yday

# "day of year" ranges for the northern hemisphere
spring = range(80, 172)
summer = range(172, 264)
fall = range(264, 355)
# winter = everything else

if doy in spring:
  season = 'spring'
elif doy in summer:
  season = 'summer'
elif doy in fall:
  season = 'fall'
else:
  season = 'winter'
Rampageous answered 5/7, 2014 at 3:8 Comment(1)
tm_yday is off by one after 1 March on leap yearsScaife
B
10

I came here looking how to map dates to seasons, and based on this answer I finally solved it in the following way:

def season_of_date(date):
    year = str(date.year)
    seasons = {'spring': pd.date_range(start='21/03/'+year, end='20/06/'+year),
               'summer': pd.date_range(start='21/06/'+year, end='22/09/'+year),
               'autumn': pd.date_range(start='23/09/'+year, end='20/12/'+year)}
    if date in seasons['spring']:
        return 'spring'
    if date in seasons['summer']:
        return 'summer'
    if date in seasons['autumn']:
        return 'autumn'
    else:
        return 'winter'

# Assuming df has a date column of type `datetime`
df['season'] = df.date.map(season_of_date)

So in principle it works for any year, given a datetime.

Beeson answered 9/12, 2018 at 17:2 Comment(3)
unfortunately, it did not work on my case. As much as I tried to adapt it, it made a lot of mistakes and never ran. This was one of the most persistent errors: TypeError: Cannot convert input [0 2012-03-21 Name: insemDate, Length: 145855, dtype: object] of type <class 'pandas.core.series.Series'> to Timestamp Any hint?Disavowal
In what line do you get that error? How does you df look like? What did you adapt? My guess is that you are passing the whole datetime column to the function, and that is not the idea. The idea is to use map to pass a single datetime (one per row) at a time. See the doc.Beeson
Thank you for your answer @iipr. Yeah, your guess is indeed correct. Thanks for the hint, however, I was not successful on adapt it to my code and I ended up using the option that jfs suggested.Disavowal
B
5

There is no need to deal with years, it is enough to compare the month and day as tuples like this:

import datetime


def get_season(date: datetime.datetime, north_hemisphere: bool = True) -> str:
    now = (date.month, date.day)
    if (3, 21) <= now < (6, 21):
        season = 'spring' if north_hemisphere else 'fall'
    elif (6, 21) <= now < (9, 21):
        season = 'summer' if north_hemisphere else 'winter'
    elif (9, 21) <= now < (12, 21):
        season = 'fall' if north_hemisphere else 'spring'
    else:
        season = 'winter' if north_hemisphere else 'summer'

    return season
Birthplace answered 2/5, 2022 at 19:23 Comment(0)
E
3

The hemisphere that you are in must be taken into account. You must determine the hemisphere using geolocation yourself.

def season(self, HEMISPHERE):
    date = self.now()
    md = date.month * 100 + date.day

    if ((md > 320) and (md < 621)):
        s = 0 #spring
    elif ((md > 620) and (md < 923)):
        s = 1 #summer
    elif ((md > 922) and (md < 1223)):
        s = 2 #fall
    else:
        s = 3 #winter

    if not HEMISPHERE == 'north':
        s = (s + 2) % 3
    return s
Edp answered 19/4, 2014 at 1:1 Comment(1)
I believe the hemisphere calculation should use modulus 4, not 3? For example, in your calculation summer (1) results in 0 (spring) in the southern hemisphere, which is incorrect.Dusa
C
2

This is how I finally solved it. I doubt this is the best solution, but it works. Feel free to offer better solutions.

import datetime

def get_season(date):
    """
    convert date to month and day as integer (md), e.g. 4/21 = 421, 11/17 = 1117, etc.
    """
    m = date.month * 100
    d = date.day
    md = m + d

    if ((md >= 301) and (md <= 531)):
        s = 0  # spring
    elif ((md > 531) and (md < 901)):
        s = 1  # summer
    elif ((md >= 901) and (md <= 1130)):
        s = 2  # fall
    elif ((md > 1130) and (md <= 0229)):
        s = 3  # winter
    else:
        raise IndexError("Invalid date")

    return s

season = get_season(dt.date())
Centra answered 22/4, 2013 at 5:18 Comment(0)
M
2

I'm too new to comment, and my edit was rejected, so here is corrected code for @adsf reponse.

def season(date, hemisphere):
    ''' date is a datetime object
        hemisphere is either 'north' or 'south', dependent on long/lat.
    '''
    md = date.month * 100 + date.day

    if ((md > 320) and (md < 621)):
        s = 0 #spring
    elif ((md > 620) and (md < 923)):
        s = 1 #summer
    elif ((md > 922) and (md < 1223)):
        s = 2 #fall
    else:
        s = 3 #winter

    if hemisphere != 'north':
        if s < 2:
            s += 2 
        else:
            s -= 2

    return s
Monmouth answered 7/11, 2015 at 5:20 Comment(2)
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post - you can always comment on your own posts, and once you have sufficient reputation you will be able to comment on any post.Ptah
Thanks Lance. This post improves upon a previous answer which I believe is the best because it takes geography into account.Monmouth
C
2

I think you can use pandas.Series.dt.quarter? For example,

datetime = pd.Series(pd.to_datetime(['2010-09-30', '2010-04-25', '2010-01-25', '2010-10-29', '2010-12-25']))
seasons = datetime.dt.quarter

seasons:
0    3
1    2
2    1
3    4
4    4

seasons would be what you want?

Creodont answered 28/7, 2020 at 22:9 Comment(0)
P
1

This is what i normally use:

seasons = {'Summer':(datetime(2014,6,21), datetime(2014,9,22)),
           'Autumn':(datetime(2014,9,23), datetime(2014,12,20)),
           'Spring':(datetime(2014,3,21), datetime(2014,6,20))}

def get_season(date):
    for season,(season_start, season_end) in seasons.items():
        if date>=season_start and date<= season_end:
            return season
    else:
        return 'Winter'
Penates answered 24/2, 2015 at 1:24 Comment(1)
just a matter of changing the seasons.Penates
J
1

This is a follow up to the answer by @iipr

The function posted does work, but will have to be adapted to the DateTime format you have in your dataset so keep that in mind!

Mine is in the format YYYY-MM-DD, while the original function assumes DD-MM-YYYY.

The function will also not work if the DateTime column includes timestamps (which mine did), which necessitates some more adjustments (see below).

def season_of_date(date):
year = str(date.year)

# Extract a string of only the date of the DateTime column, use that in the if statements.
date_only = str(date.date())

# Changed the format of the date_range from DD-MM-YYYY to YYYY-MM-DD.
seasons = {'spring': pandas.date_range(start=year+'/03/21', end=year+'/06/20'),
           'summer': pandas.date_range(start=year+'/06/21', end=year+'/09/22'),
           'autumn': pandas.date_range(start=year+'/09/23', end=year+'/12/20')}
if date_only in seasons['spring']:
    return 'spring'
if date_only in seasons['summer']:
    return 'summer'
if date_only in seasons['autumn']:
    return 'autumn'
else:
    return 'winter'

# Assuming df has a date column of type `datetime`
df['season'] = df.date.map(season_of_date)

This should hopefully be a bit more robust.

Jughead answered 22/11, 2022 at 9:45 Comment(0)
D
0

using python datetime and simple dictionary

import datetime
date = datetime.datetime.strptime('01/05/2015 01:30:00 PM', "%m/%d/%Y %H:%M:%S %p")

//using above created date for the code below
seasons = {1:[12,1,2],2:[3,4,5],3:[6,7,8],4:[9,10,11]}
ss={}
for k,v in seasons.items():
    for e in v:
        ss[e] = k
print(ss[date.month])
Dispeople answered 25/11, 2021 at 8:28 Comment(0)
P
0

This is how I was able to use it, disregarding year with preset season times. I created a tuple using the int value of month and day and compared it to the tuple value of each month.

def get_season(x):
    if (x.month, x.day) < (3,20) or (x.month, x.day) > (12,20): 
        return 'winter'
    elif (x.month,x.day) < (6,21): 
        return 'spring'
    elif (x.month,x.day) < (9,23):     
        return 'summer'
    else: return 'fall'

df['season'] = df.timestamp.apply(get_season)
Peckham answered 6/2, 2023 at 15:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.