In [179]:
#
# genNRFCalendar - generate Calendar as per National Retail Federation Specs.
# Author: Murali Krishnan
# version: v1.0
# Date: May 16, 2016
#
# Specification at: https://nrf.com/resources/4-5-4-calendar

program ="genNRFCalendar";
version = "v1.0";
author  = "Murali Krishnan";

import datetime;
import csv;
retailEpocStart = datetime.datetime( 2000, 1, 30);

print program, " ", version, " by", author;
print "\n"
print "Generates Retail 4-5-4 calendar starting at Januar 30, 2000 (Gregorian).";
print " Jan 30, 2000 is Day 1, Week 1 of Year 2000 Retail."
print retailEpocStart;


genNRFCalendar   v1.0  by Murali Krishnan


Generates Retail 4-5-4 calendar starting at Januar 30, 2000 (Gregorian).
 Jan 30, 2000 is Day 1, Week 1 of Year 2000 Retail.
2000-01-30 00:00:00

In [180]:
# The output shows the Gregorian year, month, and date on the far left and
#  the Retail 4-5-4 year, week of year, and day of week in the next three columns
class NRFCalendarDay:
    Retail454dayofweek = 1;
    Retail454dayofmonth = 1;
    Retail454month = 1;
    Retail454weekofmonth = 1;
    Retail454weeksinmonth = 4; 
    Retail454weekofyear  = 1;
    Retail454year = 2000;
    
    def __init__( self):
        print "Initialized NRF Calendar Day";

    def Clone( self):
        cloneFrom = self;
        newNRFC = NRFCalendarDay();
        newNRFC.Retail454dayofweek = cloneFrom.Retail454dayofweek;
        newNRFC.Retail454dayofmonth = cloneFrom.Retail454dayofmonth;
        newNRFC.Retail454month = cloneFrom.Retail454month;
        newNRFC.Retail454weekofmonth = cloneFrom.Retail454weekofmonth;
        newNRFC.Retail454weeksinmonth = cloneFrom.Retail454weeksinmonth; 
        newNRFC.Retail454weekofyear  = cloneFrom.Retail454weekofyear;
        newNRFC.Retail454year = cloneFrom.Retail454year;
        return newNRFC;
        
    #  calculate number of weeks in 454 month
    def NumWeeksInMonth( self, givenMonth):
        numWeeks = 4;
        if ( ( self.Retail454month == 2) or ( self.Retail454month == 5) or ( self.Retail454month == 8) or ( self.Retail454month == 11)):
            numWeeks = 5;
        elif ( self.Retail454month == 12) :
            if ( givenMonth == 12):
                numWeeks = 5;

        return numWeeks;

    #  calculate Week of Year given other data
    def calcWeekOfYear( self):
        weekOfYear = self.Retail454weekofyear;
        # check if there is a rollover. if so, reset week of year.
        # ToDo: How do i distinguish between years with 52 weeks and 53 weeks?
        if ( self.Retail454dayofweek == 1):
            weekOfYear = self.Retail454weekofyear + 1;
            if ((self.Retail454month == 1) and (self.Retail454weekofyear >= 50)):
                weekOfYear = 1;

        return weekOfYear;


    def IncrementRetail454( self, givenMonth):
        # NRFCalendarDay newDay = NRFCalendarDay;

        # 1. Calculate the day of the week
        self.Retail454dayofweek = (self.Retail454dayofweek % 7) + 1;

        # 2. Calculate day of the month
        if ( ( self.Retail454weeksinmonth == 4) and (self.Retail454dayofmonth == 28) or ( self.Retail454weeksinmonth == 5) and (self.Retail454dayofmonth == 35)):
            # roll forward the month
            self.Retail454month = ((self.Retail454month % 12) + 1);
            self.Retail454dayofmonth = 1;
        else:
            self.Retail454dayofmonth += 1;

        # 3. set the week of month number
        if (self.Retail454dayofweek == 1):
            if (self.Retail454weekofmonth == self.Retail454weeksinmonth):
                # reset the week to be the starting week of a month
                self.Retail454weekofmonth = 1;
            else:
                # roll forward the week to next week
                self.Retail454weekofmonth += 1;

        # 4. Calculate number of weeks in month if it is a new month
        if ( self.Retail454dayofmonth == 1):
            self.Retail454weeksinmonth = self.NumWeeksInMonth( givenMonth);

        # 5. Calculate week of year and udpate accordingly
        self.Retail454weekofyear = self.calcWeekOfYear();

        # 6. Update year only if there is a roll over. 
        #   i.e. only if day == 1 and week of year == 1;
        if ((self.Retail454dayofweek == 1) and (self.Retail454weekofyear == 1)):
            self.Retail454year += 1;

        return self;
    
    def Print(self):
        formatString = "Retail Day of Week: {0}, Day Of Month {1}, Month {2}, Week Of Month {3}, Weeks In Month {4}, Week Of Year {5}, Year {6}";
        print formatString.format( self.Retail454dayofweek, self.Retail454dayofmonth, self.Retail454month, self.Retail454weekofmonth, self.Retail454weeksinmonth, self.Retail454weekofyear, self.Retail454year);

In [181]:
N = NRFCalendarDay;
n = N();
print n.Retail454year;
n.Print();


Initialized NRF Calendar Day
2000
Retail Day of Week: 1, Day Of Month 1, Month 1, Week Of Month 1, Weeks In Month 4, Week Of Year 1, Year 2000

In [182]:
b= n.IncrementRetail454(1);
print b.Retail454dayofweek;
b.Print();


2
Retail Day of Week: 2, Day Of Month 2, Month 1, Week Of Month 1, Weeks In Month 4, Week Of Year 1, Year 2000

In [183]:
retailEpocStart = datetime.datetime( 2000, 1, 30);
print retailEpocStart;


2000-01-30 00:00:00

In [184]:
dNow = datetime.datetime.now();
print dNow;


2016-05-17 01:56:48.417390

In [192]:
# - local helper function
def writeCalendarForDate( csvWriter, dtCur, rc):
    rowToWrite = [  dtCur.year, dtCur.month, dtCur.day, 
                    rc.Retail454dayofweek, rc.Retail454dayofmonth, rc.Retail454month, 
                    rc.Retail454weekofmonth, rc.Retail454weeksinmonth, 
                    rc.Retail454weekofyear, rc.Retail454year];
    csvWriter.writerow( rowToWrite);


class NRFGenerator:
    retailEpocStart = None;
    outputFormat = [ "GregorianYear", "GregorianMonth", "GregorianDay",
                    "Retail454dayofweek", "Retail454dayofmonth", "Retail454month",
                    "Retail454weekofmonth", "Retail454weeksinmonth", 
                    "Retail454weekofyear", "Retail454year" ];

    def __init__(self):
        self.retailEpocStart = datetime.datetime( 2000, 1, 30);
        print "Initialized NRF Generator";

    def NumDaysFromRetailEpoch( self, dtStart):
        tdStart = dtStart - self.retailEpocStart; # gives the time delta between the new start date and epoch
        print "Number of days from epoch for start date: {0}".format( tdStart.days);
        return tdStart.days;

    def RunTillNumDays( self, dtFrom, dtTill, nrfCalendarDay, csvWriter):
        tdStart = dtTill - dtFrom; # gives the time delta between the new start date and epoch
        numDaysTill = tdStart.days;
        dtIter = dtFrom;
        r = nrfCalendarDay;
        if ( csvWriter != None):
            writeCalendarForDate( csvWriter, dtIter, r);

        for i in range(0, numDaysTill):
            dtIter = dtIter + datetime.timedelta( days = 1);
            if ( i %100 == 0):
                print " ... Iterating for date: {0}".format( dtIter);
            r = r.IncrementRetail454( dtIter.month);
            if ( csvWriter != None):
                writeCalendarForDate( csvWriter, dtIter, r);
        return r;

    def SetupStartDatesTill( self, dtStart, csvWriter):
        numDaysFromEpoch = self.NumDaysFromRetailEpoch( dtStart);

        # setup a dictionary with Gregorian Calendar Year # and equivalent Retail454CalendarDay
        dictForYearStart = {}; # dict.fromkeys( range( 2000, dtStart.year + 1));
        yearsList = range( 2001, dtStart.year + 1);

        dtFrom = self.retailEpocStart;
        nrfcFrom = NRFCalendarDay();

        if (csvWriter != None):
            csvWriter.writerow( self.outputFormat);

        for i in yearsList:

            dtNextYear = datetime.datetime( i, 1, 1);
            nrfCalendarForNextYear = self.RunTillNumDays( dtFrom, dtNextYear, nrfcFrom, csvWriter);
            dictForYearStart[ dtNextYear] = nrfCalendarForNextYear;

            dtFrom = dtNextYear;
            nrfcFrom = nrfCalendarForNextYear.Clone();
        return dictForYearStart;


    def Print( self):
        print "NRF Generator Ready to go. Epoch Set to: {0}".format( self.retailEpocStart);

In [193]:
def TestGeneration( tillYear, csvOutputFile):
    csvWriter = None;
    outputFileHandle  = None;
    ng = NRFGenerator();
    ng.Print();

    if (csvOutputFile != None):
        outputFileHandle = open( csvOutputFile, "wb");
        csvWriter = csv.writer( outputFileHandle, delimiter=',');

    dy = ng.SetupStartDatesTill( datetime.datetime( tillYear, 1, 1), csvWriter);

    print "\n\nFinished generating the Retail Calendar. Writing summary output."
    keys = sorted(dy.keys());
    vals = dy.values();
    for k in keys:
        print "\nFor Date: {0}".format(k);
        dy[k].Print();

    if (outputFileHandle != None):
        outputFileHandle.close();

In [194]:
TestGeneration(2001, None);


Initialized NRF Generator
NRF Generator Ready to go. Epoch Set to: 2000-01-30 00:00:00
Number of days from epoch for start date: 337
Initialized NRF Calendar Day
 ... Iterating for date: 2000-01-31 00:00:00
 ... Iterating for date: 2000-05-10 00:00:00
 ... Iterating for date: 2000-08-18 00:00:00
 ... Iterating for date: 2000-11-26 00:00:00
Initialized NRF Calendar Day


Finished generating the Retail Calendar. Writing summary output.

For Date: 2001-01-01 00:00:00
Retail Day of Week: 2, Day Of Month 2, Month 12, Week Of Month 1, Weeks In Month 5, Week Of Year 49, Year 2000

In [195]:
TestGeneration(2003, "test2003.csv");


Initialized NRF Generator
NRF Generator Ready to go. Epoch Set to: 2000-01-30 00:00:00
Number of days from epoch for start date: 1067
Initialized NRF Calendar Day
 ... Iterating for date: 2000-01-31 00:00:00
 ... Iterating for date: 2000-05-10 00:00:00
 ... Iterating for date: 2000-08-18 00:00:00
 ... Iterating for date: 2000-11-26 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2001-01-02 00:00:00
 ... Iterating for date: 2001-04-12 00:00:00
 ... Iterating for date: 2001-07-21 00:00:00
 ... Iterating for date: 2001-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2002-01-02 00:00:00
 ... Iterating for date: 2002-04-12 00:00:00
 ... Iterating for date: 2002-07-21 00:00:00
 ... Iterating for date: 2002-10-29 00:00:00
Initialized NRF Calendar Day


Finished generating the Retail Calendar. Writing summary output.

For Date: 2001-01-01 00:00:00
Retail Day of Week: 2, Day Of Month 2, Month 12, Week Of Month 1, Weeks In Month 5, Week Of Year 49, Year 2000

For Date: 2002-01-01 00:00:00
Retail Day of Week: 3, Day Of Month 31, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2001

For Date: 2003-01-01 00:00:00
Retail Day of Week: 4, Day Of Month 32, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2002

In [196]:
TestGeneration(2017, "test2017.csv");


Initialized NRF Generator
NRF Generator Ready to go. Epoch Set to: 2000-01-30 00:00:00
Number of days from epoch for start date: 6181
Initialized NRF Calendar Day
 ... Iterating for date: 2000-01-31 00:00:00
 ... Iterating for date: 2000-05-10 00:00:00
 ... Iterating for date: 2000-08-18 00:00:00
 ... Iterating for date: 2000-11-26 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2001-01-02 00:00:00
 ... Iterating for date: 2001-04-12 00:00:00
 ... Iterating for date: 2001-07-21 00:00:00
 ... Iterating for date: 2001-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2002-01-02 00:00:00
 ... Iterating for date: 2002-04-12 00:00:00
 ... Iterating for date: 2002-07-21 00:00:00
 ... Iterating for date: 2002-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2003-01-02 00:00:00
 ... Iterating for date: 2003-04-12 00:00:00
 ... Iterating for date: 2003-07-21 00:00:00
 ... Iterating for date: 2003-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2004-01-02 00:00:00
 ... Iterating for date: 2004-04-11 00:00:00
 ... Iterating for date: 2004-07-20 00:00:00
 ... Iterating for date: 2004-10-28 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2005-01-02 00:00:00
 ... Iterating for date: 2005-04-12 00:00:00
 ... Iterating for date: 2005-07-21 00:00:00
 ... Iterating for date: 2005-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2006-01-02 00:00:00
 ... Iterating for date: 2006-04-12 00:00:00
 ... Iterating for date: 2006-07-21 00:00:00
 ... Iterating for date: 2006-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2007-01-02 00:00:00
 ... Iterating for date: 2007-04-12 00:00:00
 ... Iterating for date: 2007-07-21 00:00:00
 ... Iterating for date: 2007-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2008-01-02 00:00:00
 ... Iterating for date: 2008-04-11 00:00:00
 ... Iterating for date: 2008-07-20 00:00:00
 ... Iterating for date: 2008-10-28 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2009-01-02 00:00:00
 ... Iterating for date: 2009-04-12 00:00:00
 ... Iterating for date: 2009-07-21 00:00:00
 ... Iterating for date: 2009-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2010-01-02 00:00:00
 ... Iterating for date: 2010-04-12 00:00:00
 ... Iterating for date: 2010-07-21 00:00:00
 ... Iterating for date: 2010-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2011-01-02 00:00:00
 ... Iterating for date: 2011-04-12 00:00:00
 ... Iterating for date: 2011-07-21 00:00:00
 ... Iterating for date: 2011-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2012-01-02 00:00:00
 ... Iterating for date: 2012-04-11 00:00:00
 ... Iterating for date: 2012-07-20 00:00:00
 ... Iterating for date: 2012-10-28 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2013-01-02 00:00:00
 ... Iterating for date: 2013-04-12 00:00:00
 ... Iterating for date: 2013-07-21 00:00:00
 ... Iterating for date: 2013-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2014-01-02 00:00:00
 ... Iterating for date: 2014-04-12 00:00:00
 ... Iterating for date: 2014-07-21 00:00:00
 ... Iterating for date: 2014-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2015-01-02 00:00:00
 ... Iterating for date: 2015-04-12 00:00:00
 ... Iterating for date: 2015-07-21 00:00:00
 ... Iterating for date: 2015-10-29 00:00:00
Initialized NRF Calendar Day
 ... Iterating for date: 2016-01-02 00:00:00
 ... Iterating for date: 2016-04-11 00:00:00
 ... Iterating for date: 2016-07-20 00:00:00
 ... Iterating for date: 2016-10-28 00:00:00
Initialized NRF Calendar Day


Finished generating the Retail Calendar. Writing summary output.

For Date: 2001-01-01 00:00:00
Retail Day of Week: 2, Day Of Month 2, Month 12, Week Of Month 1, Weeks In Month 5, Week Of Year 49, Year 2000

For Date: 2002-01-01 00:00:00
Retail Day of Week: 3, Day Of Month 31, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2001

For Date: 2003-01-01 00:00:00
Retail Day of Week: 4, Day Of Month 32, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2002

For Date: 2004-01-01 00:00:00
Retail Day of Week: 5, Day Of Month 33, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2003

For Date: 2005-01-01 00:00:00
Retail Day of Week: 7, Day Of Month 35, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2004

For Date: 2006-01-01 00:00:00
Retail Day of Week: 1, Day Of Month 1, Month 12, Week Of Month 1, Weeks In Month 4, Week Of Year 49, Year 2005

For Date: 2007-01-01 00:00:00
Retail Day of Week: 2, Day Of Month 2, Month 12, Week Of Month 1, Weeks In Month 5, Week Of Year 49, Year 2006

For Date: 2008-01-01 00:00:00
Retail Day of Week: 3, Day Of Month 31, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2007

For Date: 2009-01-01 00:00:00
Retail Day of Week: 5, Day Of Month 33, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2008

For Date: 2010-01-01 00:00:00
Retail Day of Week: 6, Day Of Month 34, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2009

For Date: 2011-01-01 00:00:00
Retail Day of Week: 7, Day Of Month 35, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2010

For Date: 2012-01-01 00:00:00
Retail Day of Week: 1, Day Of Month 1, Month 12, Week Of Month 1, Weeks In Month 4, Week Of Year 49, Year 2011

For Date: 2013-01-01 00:00:00
Retail Day of Week: 3, Day Of Month 3, Month 12, Week Of Month 1, Weeks In Month 5, Week Of Year 49, Year 2012

For Date: 2014-01-01 00:00:00
Retail Day of Week: 4, Day Of Month 32, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2013

For Date: 2015-01-01 00:00:00
Retail Day of Week: 5, Day Of Month 33, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2014

For Date: 2016-01-01 00:00:00
Retail Day of Week: 6, Day Of Month 34, Month 11, Week Of Month 5, Weeks In Month 5, Week Of Year 48, Year 2015

For Date: 2017-01-01 00:00:00
Retail Day of Week: 1, Day Of Month 1, Month 12, Week Of Month 1, Weeks In Month 4, Week Of Year 49, Year 2016

In [ ]: