Repository: pandas-dev/pandas

Issue:
ENH: Support for "52–53-week fiscal year" / "4–4–5 calendar"
See: http://en.wikipedia.org/wiki/4%E2%80%934%E2%80%935_calendar about the referenced calendars.

Certain companies, for example Green Mountain Coffee Roasters, Inc. (GMCR) use this calendar. See for example: http://files.shareholder.com/downloads/GMCR/1456137416x0x436353/9E2D04D9-79DE-4C08-A7C6-7B161E23E586/gmcr_2010_annual_report_lo.pdf
where it states:

> "The Company’s fiscal year ends on the last Saturday in September."

Here is some more information on this calendar: http://www.nrf.com/modules.php?name=Pages&sp_id=391
Walmart releases its sales numbers on this calendar: http://stock.walmart.com/financial-reporting/comparable-store-sales



Discussion:
Here is an academic paper on these fiscal calendars: http://www3.nd.edu/~carecob/April2010Conference/LeonePaper.pdf

Home Depot (HD) is another company which uses a retail calendar (different from GMCR, however) (http://phx.corporate-ir.net/External.File?item=UGFyZW50SUQ9MTc3ODU4fENoaWxkSUQ9LTF8VHlwZT0z&t=1 ):

> The Company’s fiscal year is a 52- or 53-week period ending on the Sunday nearest to January 31. The fiscal year ended February 3, 2013 ("fiscal 2012") includes 53 weeks and fiscal years ended January 29, 2012 ("fiscal 2011") and January 30, 2011 ("fiscal 2010") include 52 weeks.

look at `CustomBusinessDay` (you prob just need the `DateOffset` not the index) for an example of how to subclass offsets; essentially you just have it do custom `apply`

@jreback I posted an [SO question about this](http://stackoverflow.com/questions/18327347/pandas-dateoffset-periods-and-tslib-period-ordinal)

I know, much better to ask here :)

Okay, so once I have created a subclass of `DateOffset`, how do I got about making `Period`s that are on that frequency?

Create a test for it, then step thru until it fails, then see what else you need to do. I haven't created a new frequency so I don't have a roadmap, but this is how TDD works.  

`p = Period(ordinal=-1, freq='NEW_FREQ')`

@jreback I have found a couple oddities. 
This:

```
pd.Period("2013-12", freq=CustomBusinessDay())
```

leads to:

```
Traceback (most recent call last):
  File "/home/alex/git/pandas/pandas/tseries/tests/test_52.py", line 15, in testName
    a = pd.Period("2013-12", freq=CustomBusinessDay())
  File "/home/alex/git/pandas/pandas/tseries/period.py", line 121, in __init__
    base, mult = _gfc(freq)
  File "/home/alex/git/pandas/pandas/tseries/frequencies.py", line 95, in get_freq_code
    code = _period_str_to_code(freqstr[1])
  File "/home/alex/git/pandas/pandas/tseries/frequencies.py", line 742, in _period_str_to_code
    freqstr = _rule_aliases.get(freqstr.lower(), freqstr)
AttributeError: 'int' object has no attribute 'lower'
```

because in `tseries.frequencies`, freqstr.n is an int for a  `DateOffset` on which `lower` called:

```
    if isinstance(freqstr, DateOffset):
        freqstr = (get_offset_name(freqstr), freqstr.n)

    if isinstance(freqstr, tuple):
        if (com.is_integer(freqstr[0]) and
                com.is_integer(freqstr[1])):
            # e.g., freqstr = (2000, 1)
            return freqstr
        else:
            # e.g., freqstr = ('T', 5)
            try:
                code = _period_str_to_code(freqstr[0])
                stride = freqstr[1]
            except:
                code = _period_str_to_code(freqstr[1])
                stride = freqstr[0]
            return code, stride
```

The other is this great line:

```
get_offset_name = get_offset_name
```

Currently I am getting stuck. 

```
Period(ordinal=-1, freq='NEW_FREQ')
```

eventually leads to a lookup in the `_period_code_map` (or the `_period_alias_dict`), which goes back to my question as to what those codes mean.

freq is a string, you need to actually define new codes that eventually instantiate your class

NEW_FREQ (pick a better name!)

will be recognized as a valid code in the code maps then your class created 

Of course it will have a better name.

It actual does not need to be a string. The method should take an offset.
See the comment above. Can I choose any code that is currently unused? What
is the point of the int code as opposed to a reference to an offset? .
On Aug 21, 2013 9:50 PM, "jreback" notifications@github.com wrote:

> freq is a string, you need to actually define new codes that eventually
> instantiate your class
> 
> NEW_FREQ (pick a better name!)
> 
> will be recognized as a valid code in the code maps then your class
> created
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/pydata/pandas/issues/4511#issuecomment-23063558
> .

needs to be a string (well hashable), easiest to keep the current standard

the number have to do with non-conflicting with other periods

periods are ultimately represented by integers, so the reverse translation needs to happen too 

Ultimately there has to be a string, but I can pass an offset in for freq
which then can be called to return it's string representation.

That being said, is the reason historic for the int representation rather
than using an object reference as to avoid maintaining two sets of lookup
tables?
On Aug 21, 2013 9:57 PM, "jreback" notifications@github.com wrote:

> needs to be a string (well hashable), easiest to keep the current standard
> 
> the number have to do with non-conflicting with other periods
> 
> periods are ultimately represented by integers, so the reverse translation
> needs to happen too
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/pydata/pandas/issues/4511#issuecomment-23063826
> .

why would you not use a string like everything else?

In [1]: pr = period_range('2000',periods=5,freq='A')

```
In [2]: pr
Out[2]: 
<class 'pandas.tseries.period.PeriodIndex'>
freq: A-DEC
[2000, ..., 2004]
length: 5

In [3]: pr.values
Out[3]: array([30, 31, 32, 33, 34])
```

the lookup table is just used to compute the offsets that govern the backing representation for the period index (e.g. the number that represents a unique period)

so they are not optional; they are stored as numpy arrays; objects don't work here (well they work but you lose all efficiency) and are not a good idea in general

@jreback What I mean about not having to use strings for `freq` was that either of these work:

```
pd.Period("2013-12-01", freq=Day())
pd.Period("2013-12-01", freq="D")
```

They both lead to:

```
Period('2013-12-01', 'D')
```

@jreback As for ints, I wasn't referring to the int used to represent a `Period`, but rather the int to represent a frequency/`DateOffset`:

```
get_freq_code(Day())
(6000, 1)
```

or

```
get_freq_code(Week(weekday=1))
(4002, 1)
```

which should need to be stored only once for a given vector.

I just read it; these correspond to the original scikit-timeseries codes. So I guess it IS historic. But for back compat, follow what its doing (and maybe note that; using a code that is available), maybe start at 10000 or something

Any idea what the reason to keep using those codes? If they are only used for internal representations (i.e. the user never sees them, etc), it might make sense to tear them out and replace with a more meaningful object (perhaps the a reference to the Offset). An accessor on that object that provides those codes can still be kept around if needed.

Having to keep two lookup tables up to date looks like it will cause maintenance issues and errors down the road. Further it looks to complicate adding new Offsets. For the time being I will just grab an int and use it, but it might be a good project to migrate away from the legacy ints. That being said, I have only done a precursory examination of how those codes are used.

I haven't played around with this so I don't know

that said, it's easy to try things to break tests (though have to be careful that u break something that not being tested!)

and I suspect we should drop support for scikit-timeseries in any event

Any recommendations for what I should call these new `DateOffset`s? The two styles that I plan to implement:
1) Under this method the company's fiscal year is defined as the final Saturday (or other day selected) in the fiscal year end month.
2) Under this method the company's fiscal year is defined as the Saturday (or other day selected) that falls closest to the last day of the fiscal year end month.

these are 'Annual' right?

Quarters. They are like the `QuarterEnd` or `BQuarterEnd` bur rather than the quarter being anchored to the last day in the month specified, they are to the rules I outlined.

how about `FQ` for Fiscal Quarter?

It isn't just any fiscal quarter, but rather one that tends to be used in retail.

how about FQ52 and FQ4_4_5 ?

not sure if there is a abbrev for 4_4_5 type


Resolving patch:
diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py
--- a/pandas/tseries/frequencies.py
+++ b/pandas/tseries/frequencies.py
@@ -95,6 +95,8 @@ def get_freq_code(freqstr):
                 code = _period_str_to_code(freqstr[0])
                 stride = freqstr[1]
             except:
+                if com.is_integer(freqstr[1]):
+                    raise
                 code = _period_str_to_code(freqstr[1])
                 stride = freqstr[0]
             return code, stride
@@ -227,10 +229,10 @@ def get_period_alias(offset_str):
     'us': 'U'
 }
 
+#TODO: Can this be killed?
 for _i, _weekday in enumerate(['MON', 'TUE', 'WED', 'THU', 'FRI']):
     for _iweek in range(4):
         _name = 'WOM-%d%s' % (_iweek + 1, _weekday)
-        _offset_map[_name] = offsets.WeekOfMonth(week=_iweek, weekday=_i)
         _rule_aliases[_name.replace('-', '@')] = _name
 
 # Note that _rule_aliases is not 1:1 (d[BA]==d[A@DEC]), and so traversal
@@ -301,7 +303,7 @@ def to_offset(freqstr):
 
 
 # hack to handle WOM-1MON
-opattern = re.compile(r'([\-]?\d*)\s*([A-Za-z]+([\-@]\d*[A-Za-z]+)?)')
+opattern = re.compile(r'([\-]?\d*)\s*([A-Za-z]+([\-@][\dA-Za-z\-]+)?)')
 
 
 def _base_and_stride(freqstr):
@@ -356,16 +358,16 @@ def get_offset(name):
     else:
         if name in _rule_aliases:
             name = _rule_aliases[name]
-    try:
-        if name not in _offset_map:
+            
+    if name not in _offset_map:
+        try:
             # generate and cache offset
             offset = _make_offset(name)
-            _offset_map[name] = offset
-        return _offset_map[name]
-    except (ValueError, TypeError, KeyError):
-        # bad prefix or suffix
-        pass
-    raise ValueError('Bad rule name requested: %s.' % name)
+        except (ValueError, TypeError, KeyError):
+            # bad prefix or suffix
+            raise ValueError('Bad rule name requested: %s.' % name)
+        _offset_map[name] = offset
+    return _offset_map[name]
 
 
 getOffset = get_offset
@@ -401,9 +403,6 @@ def get_legacy_offset_name(offset):
     name = offset.name
     return _legacy_reverse_map.get(name, name)
 
-get_offset_name = get_offset_name
-
-
 def get_standard_freq(freq):
     """
     Return the standardized frequency string
@@ -621,8 +620,12 @@ def _period_str_to_code(freqstr):
     try:
         freqstr = freqstr.upper()
         return _period_code_map[freqstr]
-    except:
-        alias = _period_alias_dict[freqstr]
+    except KeyError:
+        try:
+            alias = _period_alias_dict[freqstr]
+        except KeyError:
+            raise ValueError("Unknown freqstr: %s" % freqstr)
+        
         return _period_code_map[alias]
 
 
@@ -839,16 +842,21 @@ def _get_monthly_rule(self):
                 'ce': 'M', 'be': 'BM'}.get(pos_check)
 
     def _get_wom_rule(self):
-        wdiffs = unique(np.diff(self.index.week))
-        if not lib.ismember(wdiffs, set([4, 5])).all():
-            return None
+#         wdiffs = unique(np.diff(self.index.week))
+        #We also need -47, -49, -48 to catch index spanning year boundary
+#         if not lib.ismember(wdiffs, set([4, 5, -47, -49, -48])).all():
+#             return None
 
         weekdays = unique(self.index.weekday)
         if len(weekdays) > 1:
             return None
+        
+        week_of_months = unique((self.index.day - 1) // 7)
+        if len(week_of_months) > 1:
+            return None
 
         # get which week
-        week = (self.index[0].day - 1) // 7 + 1
+        week = week_of_months[0] + 1
         wd = _weekday_rule_aliases[weekdays[0]]
 
         return 'WOM-%d%s' % (week, wd)
diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py
--- a/pandas/tseries/offsets.py
+++ b/pandas/tseries/offsets.py
@@ -6,7 +6,7 @@
 from pandas.tseries.tools import to_datetime
 
 # import after tools, dateutil check
-from dateutil.relativedelta import relativedelta
+from dateutil.relativedelta import relativedelta, weekday
 import pandas.tslib as tslib
 from pandas.tslib import Timestamp
 from pandas import _np_version_under1p7
@@ -15,6 +15,7 @@
            'MonthBegin', 'BMonthBegin', 'MonthEnd', 'BMonthEnd',
            'YearBegin', 'BYearBegin', 'YearEnd', 'BYearEnd',
            'QuarterBegin', 'BQuarterBegin', 'QuarterEnd', 'BQuarterEnd',
+           'LastWeekOfMonth', 'FY5253Quarter', 'FY5253', 
            'Week', 'WeekOfMonth',
            'Hour', 'Minute', 'Second', 'Milli', 'Micro', 'Nano']
 
@@ -108,8 +109,8 @@ def _should_cache(self):
 
     def _params(self):
         attrs = [(k, v) for k, v in compat.iteritems(vars(self))
-                 if k not in ['kwds', '_offset', 'name', 'normalize',
-                              'busdaycalendar', '_named']]
+                 if (k not in ['kwds', 'name', 'normalize',
+                 'busdaycalendar']) and (k[0] != '_')]
         attrs.extend(list(self.kwds.items()))
         attrs = sorted(set(attrs))
 
@@ -701,15 +702,23 @@ def _from_name(cls, suffix=None):
             weekday = _weekday_to_int[suffix]
         return cls(weekday=weekday)
 
+class WeekDay(object):
+    MON = 0
+    TUE = 1
+    WED = 2
+    THU = 3
+    FRI = 4
+    SAT = 5
+    SUN = 6   
 
 _int_to_weekday = {
-    0: 'MON',
-    1: 'TUE',
-    2: 'WED',
-    3: 'THU',
-    4: 'FRI',
-    5: 'SAT',
-    6: 'SUN'
+    WeekDay.MON: 'MON',
+    WeekDay.TUE: 'TUE',
+    WeekDay.WED: 'WED',
+    WeekDay.THU: 'THU',
+    WeekDay.FRI: 'FRI',
+    WeekDay.SAT: 'SAT',
+    WeekDay.SUN: 'SUN'
 }
 
 _weekday_to_int = dict((v, k) for k, v in _int_to_weekday.items())
@@ -800,6 +809,80 @@ def _from_name(cls, suffix=None):
         week = int(suffix[0]) - 1
         weekday = _weekday_to_int[suffix[1:]]
         return cls(week=week, weekday=weekday)
+    
+class LastWeekOfMonth(CacheableOffset, DateOffset):
+    """
+    Describes monthly dates in last week of month like "the last Tuesday of each month"
+
+    Parameters
+    ----------
+    n : int
+    weekday : {0, 1, ..., 6}
+        0: Mondays
+        1: Tuesdays
+        2: Wednesdays
+        3: Thursdays
+        4: Fridays
+        5: Saturdays
+        6: Sundays
+    """
+    def __init__(self, n=1, **kwds):
+        self.n = n
+        self.weekday = kwds['weekday']
+
+        if self.n == 0:
+            raise ValueError('N cannot be 0')
+
+        if self.weekday < 0 or self.weekday > 6:
+            raise ValueError('Day must be 0<=day<=6, got %d' %
+                            self.weekday)
+
+        self.kwds = kwds
+
+    def apply(self, other):
+        offsetOfMonth = self.getOffsetOfMonth(other)
+
+        if offsetOfMonth > other:
+            if self.n > 0:
+                months = self.n - 1
+            else:
+                months = self.n
+        elif offsetOfMonth == other:
+            months = self.n
+        else:
+            if self.n > 0:
+                months = self.n
+            else:
+                months = self.n + 1
+
+        return self.getOffsetOfMonth(other + relativedelta(months=months, day=1))
+
+    def getOffsetOfMonth(self, dt):
+        m =  MonthEnd()
+        d = datetime(dt.year, dt.month, 1)
+
+        eom = m.rollforward(d)
+        
+        w = Week(weekday=self.weekday)
+        
+        return w.rollback(eom)
+
+    def onOffset(self, dt):
+        return dt == self.getOffsetOfMonth(dt)
+
+    @property
+    def rule_code(self):
+        return '%s-%s' % (self._prefix, _int_to_weekday.get(self.weekday, ''))
+
+    _prefix = 'LWOM'
+
+    @classmethod
+    def _from_name(cls, suffix=None):
+        if not suffix:
+            raise ValueError("Prefix %r requires a suffix." % (cls._prefix))
+        # TODO: handle n here...
+        weekday = _weekday_to_int[suffix]
+        return cls(weekday=weekday)
 
 
 class QuarterOffset(DateOffset):
@@ -876,7 +959,319 @@ def onOffset(self, dt):
         modMonth = (dt.month - self.startingMonth) % 3
         return BMonthEnd().onOffset(dt) and modMonth == 0
 
+class FY5253(CacheableOffset, DateOffset):
+    """
+    Describes 52-53 week fiscal year. This is also known as a 4-4-5 calendar.
+    
+    It is used by companies that desire that their 
+    fiscal year always end on the same day of the week.
+    
+    It is a method of managing accounting periods. 
+    It is a common calendar structure for some industries, 
+    such as retail, manufacturing and parking industry.
+    
+    For more information see: 
+    http://en.wikipedia.org/wiki/4%E2%80%934%E2%80%935_calendar
+    
+    
+    The year may either: 
+    - end on the last X day of the Y month.
+    - end on the last X day closest to the last day of the Y month.
+     
+    X is a specific day of the week.
+    Y is a certain month of the year
+
+    Parameters
+    ----------
+    n : int
+    weekday : {0, 1, ..., 6}
+        0: Mondays
+        1: Tuesdays
+        2: Wednesdays
+        3: Thursdays
+        4: Fridays
+        5: Saturdays
+        6: Sundays
+    startingMonth : The month in which fiscal years end. {1, 2, ... 12}
+    variation : str
+        {"nearest", "last"} for "LastOfMonth" or "NearestEndMonth"
+    """
+    
+    _prefix = 'RE'
+    _suffix_prefix_last = 'L'
+    _suffix_prefix_nearest = 'N'
+        
+    def __init__(self, n=1, **kwds):
+        self.n = n
+        self.startingMonth = kwds['startingMonth']
+        self.weekday = kwds["weekday"]
+        
+        self.variation = kwds["variation"]
+        
+        self.kwds = kwds
+        
+        if self.n == 0:
+            raise ValueError('N cannot be 0')
+        
+        if self.variation not in ["nearest", "last"]:
+            raise ValueError('%s is not a valid variation' % self.variation)
+        
+        if self.variation == "nearest":
+            self._rd_forward = relativedelta(weekday=weekday(self.weekday))
+            self._rd_backward = relativedelta(weekday=weekday(self.weekday)(-1))
+        else:
+            self._offset_lwom = LastWeekOfMonth(n=1, weekday=self.weekday)            
+        
+    def isAnchored(self):
+        return self.n == 1 \
+                and self.startingMonth is not None \
+                and self.weekday is not None 
+        
+    def onOffset(self, dt):
+        year_end = self.get_year_end(dt)
+        return year_end == dt
+    
+    def apply(self, other):
+        n = self.n
+        if n > 0:
+            year_end = self.get_year_end(other)
+            if other < year_end:
+                other = year_end
+                n -= 1
+            elif other > year_end:
+                other = self.get_year_end(other + relativedelta(years=1))
+                n -= 1
+            
+            return self.get_year_end(other + relativedelta(years=n))
+        else:
+            n = -n
+            year_end = self.get_year_end(other)
+            if other > year_end:
+                other = year_end
+                n -= 1
+            elif other < year_end:
+                other = self.get_year_end(other + relativedelta(years=-1))
+                n -= 1
+            
+            return self.get_year_end(other + relativedelta(years=-n))
+
+    def get_year_end(self, dt):
+        if self.variation == "nearest":
+            return self._get_year_end_nearest(dt)
+        else:
+            return self._get_year_end_last(dt)
+
+    def get_target_month_end(self, dt):
+        target_month = datetime(year=dt.year, month=self.startingMonth, day=1)
+        next_month_first_of = target_month + relativedelta(months=+1)
+        return next_month_first_of + relativedelta(days=-1)
+        
+    def _get_year_end_nearest(self, dt):
+        target_date = self.get_target_month_end(dt)
+        if target_date.weekday() == self.weekday:
+            return target_date
+        else:
+            forward = target_date + self._rd_forward
+            backward = target_date + self._rd_backward
+            
+            if forward - target_date < target_date - backward:
+                return forward
+            else:
+                return backward
+    
+    def _get_year_end_last(self, dt):
+        current_year = datetime(year=dt.year, month=self.startingMonth, day=1)
+        return current_year + self._offset_lwom
+    
+    @property
+    def rule_code(self):
+        suffix = self.get_rule_code_suffix()
+        return "%s-%s" % (self._get_prefix(), suffix)
+    
+    def _get_prefix(self):
+        return self._prefix
+
+    def _get_suffix_prefix(self):
+        if self.variation == "nearest":
+            return self._suffix_prefix_nearest
+        else:
+            return self._suffix_prefix_last
+        
+    def get_rule_code_suffix(self):
+        return '%s-%s-%s' % (self._get_suffix_prefix(), \
+                           _int_to_month[self.startingMonth], \
+                         _int_to_weekday[self.weekday])
+    
+    @classmethod
+    def _parse_suffix(cls, varion_code, startingMonth_code, weekday_code):
+        if varion_code == "N":
+            variation = "nearest"
+        elif varion_code == "L":
+            variation = "last"
+        else:
+            raise ValueError("Unable to parse varion_code: %s" % (varion_code,))
+        
+        startingMonth = _month_to_int[startingMonth_code]
+        weekday = _weekday_to_int[weekday_code]
+        
+        return {
+                "weekday":weekday, 
+                "startingMonth":startingMonth,
+                "variation":variation,
+                }
+    
+    @classmethod
+    def _from_name(cls, *args):
+        return cls(**cls._parse_suffix(*args))
+    
+class FY5253Quarter(CacheableOffset, DateOffset):
+    """
+    DateOffset increments between business quarter dates
+    for 52-53 week fiscal year (also known as a 4-4-5 calendar).
+    
+    It is used by companies that desire that their 
+    fiscal year always end on the same day of the week.
+    
+    It is a method of managing accounting periods. 
+    It is a common calendar structure for some industries, 
+    such as retail, manufacturing and parking industry.
+    
+    For more information see: 
+    http://en.wikipedia.org/wiki/4%E2%80%934%E2%80%935_calendar
+    
+    The year may either: 
+    - end on the last X day of the Y month.
+    - end on the last X day closest to the last day of the Y month.
+    
+    X is a specific day of the week.
+    Y is a certain month of the year
+    
+    startingMonth = 1 corresponds to dates like 1/31/2007, 4/30/2007, ...
+    startingMonth = 2 corresponds to dates like 2/28/2007, 5/31/2007, ...
+    startingMonth = 3 corresponds to dates like 3/30/2007, 6/29/2007, ...
+
+    Parameters
+    ----------
+    n : int
+    weekday : {0, 1, ..., 6}
+        0: Mondays
+        1: Tuesdays
+        2: Wednesdays
+        3: Thursdays
+        4: Fridays
+        5: Saturdays
+        6: Sundays
+    startingMonth : The month in which fiscal years end. {1, 2, ... 12}
+    qtr_with_extra_week : The quarter number that has the leap 
+        or 14 week when needed. {1,2,3,4}
+    variation : str
+        {"nearest", "last"} for "LastOfMonth" or "NearestEndMonth"        
+    """
+            
+    _prefix = 'REQ'
+        
+    def __init__(self, n=1, **kwds):
+        self.n = n
+        
+        self.qtr_with_extra_week = kwds["qtr_with_extra_week"]
+
+        self.kwds = kwds
+
+        if self.n == 0:
+            raise ValueError('N cannot be 0')
+        
+        self._offset = FY5253( \
+                            startingMonth=kwds['startingMonth'], \
+                            weekday=kwds["weekday"],
+                            variation=kwds["variation"])
+        
+    def isAnchored(self):
+        return self.n == 1 and self._offset.isAnchored()
+
+    def apply(self, other):
+        n = self.n
+        
+        if n > 0:
+            while n > 0:
+                if not self._offset.onOffset(other):
+                    qtr_lens = self.get_weeks(other)
+                    start = other - self._offset
+                else:
+                    start = other
+                    qtr_lens = self.get_weeks(other + self._offset)
+                    
+                for weeks in qtr_lens:
+                    start += relativedelta(weeks=weeks)
+                    if start > other:
+                        other = start
+                        n -= 1
+                        break
+                
+        else:
+            n = -n
+            while n > 0:
+                if not self._offset.onOffset(other):
+                    qtr_lens = self.get_weeks(other)
+                    end = other + self._offset
+                else:
+                    end = other
+                    qtr_lens = self.get_weeks(other)
+                    
+                for weeks in reversed(qtr_lens):
+                    end -= relativedelta(weeks=weeks)
+                    if end < other:
+                        other = end
+                        n -= 1
+                        break
 
+        return other
+    
+    def get_weeks(self, dt):
+        ret = [13] * 4
+
+        year_has_extra_week = self.year_has_extra_week(dt)
+        
+        if year_has_extra_week:
+            ret[self.qtr_with_extra_week-1] = 14
+            
+        return ret
+    
+    def year_has_extra_week(self, dt):
+        if self._offset.onOffset(dt):
+            prev_year_end = dt - self._offset
+            next_year_end = dt
+        else:    
+            next_year_end = dt + self._offset
+            prev_year_end = dt - self._offset
+        
+        week_in_year = (next_year_end - prev_year_end).days/7
+
+        return week_in_year == 53     
+    
+    def onOffset(self, dt):
+        if self._offset.onOffset(dt):
+            return True
+        
+        next_year_end = dt - self._offset
+
+        qtr_lens = self.get_weeks(dt)
+            
+        current = next_year_end
+        for qtr_len in qtr_lens[0:4]:
+            current += relativedelta(weeks=qtr_len)
+            if dt == current:
+                return True
+        return False
+    
+    @property
+    def rule_code(self):
+        suffix = self._offset.get_rule_code_suffix()
+        return "%s-%s" %(self._prefix, "%s-%d" % (suffix, self.qtr_with_extra_week))
+    
+    @classmethod
+    def _from_name(cls, *args):
+        return cls(**dict(FY5253._parse_suffix(*args[:-1]), qtr_with_extra_week=int(args[-1])))
+                    
 _int_to_month = {
     1: 'JAN',
     2: 'FEB',
@@ -1452,6 +1847,8 @@ def generate_range(start=None, end=None, periods=None,
     Hour,                # 'H'
     Day,                 # 'D'
     WeekOfMonth,         # 'WOM'
+    FY5253,
+    FY5253Quarter,    
 ])
 
 if not _np_version_under1p7:


Test patch:
diff --git a/pandas/tseries/tests/test_frequencies.py b/pandas/tseries/tests/test_frequencies.py
--- a/pandas/tseries/tests/test_frequencies.py
+++ b/pandas/tseries/tests/test_frequencies.py
@@ -148,17 +148,22 @@ def _check_tick(self, base_delta, code):
         self.assert_(infer_freq(index) is None)
 
     def test_weekly(self):
-        days = ['MON', 'TUE', 'WED', 'THU', 'FRI']
+        days = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN']
 
         for day in days:
             self._check_generated_range('1/1/2000', 'W-%s' % day)
 
     def test_week_of_month(self):
-        days = ['MON', 'TUE', 'WED', 'THU', 'FRI']
+        days = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN']
 
         for day in days:
             for i in range(1, 5):
                 self._check_generated_range('1/1/2000', 'WOM-%d%s' % (i, day))
+    
+    def test_week_of_month_fake(self):
+        #All of these dates are on same day of week and are 4 or 5 weeks apart
+        index = DatetimeIndex(["2013-08-27","2013-10-01","2013-10-29","2013-11-26"])
+        assert infer_freq(index) != 'WOM-4TUE'
 
     def test_monthly(self):
         self._check_generated_range('1/1/2000', 'M')
@@ -195,7 +200,7 @@ def _check_generated_range(self, start, freq):
         gen = date_range(start, periods=7, freq=freq)
         index = _dti(gen.values)
         if not freq.startswith('Q-'):
-            self.assert_(infer_freq(index) == gen.freqstr)
+            self.assertEqual(infer_freq(index), gen.freqstr)
         else:
             inf_freq = infer_freq(index)
             self.assert_((inf_freq == 'Q-DEC' and
diff --git a/pandas/tseries/tests/test_offsets.py b/pandas/tseries/tests/test_offsets.py
--- a/pandas/tseries/tests/test_offsets.py
+++ b/pandas/tseries/tests/test_offsets.py
@@ -1,4 +1,5 @@
 from datetime import date, datetime, timedelta
+from dateutil.relativedelta import relativedelta
 from pandas.compat import range
 from pandas import compat
 import unittest
@@ -24,7 +25,8 @@
 from pandas.lib import Timestamp
 from pandas.util.testing import assertRaisesRegexp
 import pandas.util.testing as tm
-from pandas.tseries.offsets import BusinessMonthEnd, CacheableOffset
+from pandas.tseries.offsets import BusinessMonthEnd, CacheableOffset, \
+    LastWeekOfMonth, FY5253, FY5253Quarter, WeekDay
 
 from pandas import _np_version_under1p7
 
@@ -524,7 +526,9 @@ def test_weekmask_and_holidays(self):
 
 def assertOnOffset(offset, date, expected):
     actual = offset.onOffset(date)
-    assert actual == expected
+    assert actual == expected, ("\nExpected: %s\nActual: %s\nFor Offset: %s)"
+                                "\nAt Date: %s" %
+                                (expected, actual, offset, date))
 
 
 class TestWeek(unittest.TestCase):
@@ -674,6 +678,75 @@ def test_onOffset(self):
             offset = WeekOfMonth(week=week, weekday=weekday)
             self.assert_(offset.onOffset(date) == expected)
 
+class TestLastWeekOfMonth(unittest.TestCase):
+    def test_constructor(self):
+        assertRaisesRegexp(ValueError, "^N cannot be 0", \
+                           LastWeekOfMonth, n=0, weekday=1)
+        
+        assertRaisesRegexp(ValueError, "^Day", LastWeekOfMonth, n=1, weekday=-1)
+        assertRaisesRegexp(ValueError, "^Day", LastWeekOfMonth, n=1, weekday=7)
+
+    def test_offset(self):
+        #### Saturday
+        last_sat = datetime(2013,8,31)
+        next_sat = datetime(2013,9,28)
+        offset_sat = LastWeekOfMonth(n=1, weekday=5)
+        
+        one_day_before = (last_sat + timedelta(days=-1))        
+        self.assert_(one_day_before + offset_sat == last_sat)
+        
+        one_day_after = (last_sat + timedelta(days=+1))
+        self.assert_(one_day_after + offset_sat == next_sat)
+        
+        #Test On that day
+        self.assert_(last_sat + offset_sat == next_sat)
+        
+        #### Thursday
+        
+        offset_thur = LastWeekOfMonth(n=1, weekday=3)
+        last_thurs = datetime(2013,1,31)
+        next_thurs = datetime(2013,2,28)
+        
+        one_day_before = last_thurs + timedelta(days=-1)
+        self.assert_(one_day_before + offset_thur == last_thurs)
+        
+        one_day_after = last_thurs + timedelta(days=+1)
+        self.assert_(one_day_after + offset_thur == next_thurs)
+        
+        # Test on that day
+        self.assert_(last_thurs + offset_thur == next_thurs)
+        
+        three_before = last_thurs + timedelta(days=-3)
+        self.assert_(three_before + offset_thur == last_thurs)
+        
+        two_after = last_thurs + timedelta(days=+2)
+        self.assert_(two_after + offset_thur == next_thurs)
+        
+        offset_sunday = LastWeekOfMonth(n=1, weekday=WeekDay.SUN)
+        self.assert_(datetime(2013,7,31) + offset_sunday == datetime(2013,8,25))
+        
+    def test_onOffset(self):
+        test_cases = [
+            (WeekDay.SUN, datetime(2013, 1, 27), True),
+            (WeekDay.SAT, datetime(2013, 3, 30), True),
+            (WeekDay.MON, datetime(2013, 2, 18), False), #Not the last Mon
+            (WeekDay.SUN, datetime(2013, 2, 25), False), #Not a SUN
+            (WeekDay.MON, datetime(2013, 2, 25), True),
+            (WeekDay.SAT, datetime(2013, 11, 30), True),
+            
+            (WeekDay.SAT, datetime(2006, 8, 26), True),
+            (WeekDay.SAT, datetime(2007, 8, 25), True),
+            (WeekDay.SAT, datetime(2008, 8, 30), True),
+            (WeekDay.SAT, datetime(2009, 8, 29), True),
+            (WeekDay.SAT, datetime(2010, 8, 28), True),
+            (WeekDay.SAT, datetime(2011, 8, 27), True),
+            (WeekDay.SAT, datetime(2019, 8, 31), True),
+        ]
+ 
+        for weekday, date, expected in test_cases:
+            offset = LastWeekOfMonth(weekday=weekday)
+            self.assert_(offset.onOffset(date) == expected, date)
+
 
 class TestBMonthBegin(unittest.TestCase):
     def test_offset(self):
@@ -1101,7 +1174,379 @@ def test_onOffset(self):
         for offset, date, expected in tests:
             assertOnOffset(offset, date, expected)
 
+def makeFY5253LastOfMonthQuarter(*args, **kwds):
+    return FY5253Quarter(*args, variation="last", **kwds)
 
+def makeFY5253NearestEndMonthQuarter(*args, **kwds):
+    return FY5253Quarter(*args, variation="nearest", **kwds)
+
+def makeFY5253NearestEndMonth(*args, **kwds):
+    return FY5253(*args, variation="nearest", **kwds)
+
+def makeFY5253LastOfMonth(*args, **kwds):
+    return FY5253(*args, variation="last", **kwds)
+            
+class TestFY5253LastOfMonth(unittest.TestCase):
+    def test_onOffset(self):
+
+        offset_lom_sat_aug = makeFY5253LastOfMonth(1, startingMonth=8, weekday=WeekDay.SAT)
+        offset_lom_sat_sep = makeFY5253LastOfMonth(1, startingMonth=9, weekday=WeekDay.SAT)
+         
+        tests = [
+            #From Wikipedia (see: http://en.wikipedia.org/wiki/4%E2%80%934%E2%80%935_calendar#Last_Saturday_of_the_month_at_fiscal_year_end)
+            (offset_lom_sat_aug, datetime(2006, 8, 26), True),
+            (offset_lom_sat_aug, datetime(2007, 8, 25), True),
+            (offset_lom_sat_aug, datetime(2008, 8, 30), True),
+            (offset_lom_sat_aug, datetime(2009, 8, 29), True),
+            (offset_lom_sat_aug, datetime(2010, 8, 28), True),
+            (offset_lom_sat_aug, datetime(2011, 8, 27), True),
+            (offset_lom_sat_aug, datetime(2012, 8, 25), True),
+            (offset_lom_sat_aug, datetime(2013, 8, 31), True),
+            (offset_lom_sat_aug, datetime(2014, 8, 30), True),
+            (offset_lom_sat_aug, datetime(2015, 8, 29), True),
+            (offset_lom_sat_aug, datetime(2016, 8, 27), True),
+            (offset_lom_sat_aug, datetime(2017, 8, 26), True),
+            (offset_lom_sat_aug, datetime(2018, 8, 25), True),
+            (offset_lom_sat_aug, datetime(2019, 8, 31), True),
+            
+            (offset_lom_sat_aug, datetime(2006, 8, 27), False),
+            (offset_lom_sat_aug, datetime(2007, 8, 28), False),
+            (offset_lom_sat_aug, datetime(2008, 8, 31), False),
+            (offset_lom_sat_aug, datetime(2009, 8, 30), False),
+            (offset_lom_sat_aug, datetime(2010, 8, 29), False),
+            (offset_lom_sat_aug, datetime(2011, 8, 28), False),
+            
+            (offset_lom_sat_aug, datetime(2006, 8, 25), False),
+            (offset_lom_sat_aug, datetime(2007, 8, 24), False),
+            (offset_lom_sat_aug, datetime(2008, 8, 29), False),
+            (offset_lom_sat_aug, datetime(2009, 8, 28), False),
+            (offset_lom_sat_aug, datetime(2010, 8, 27), False),
+            (offset_lom_sat_aug, datetime(2011, 8, 26), False),
+            (offset_lom_sat_aug, datetime(2019, 8, 30), False),    
+
+            #From GMCR (see for example: http://yahoo.brand.edgar-online.com/Default.aspx?companyid=3184&formtypeID=7)
+            (offset_lom_sat_sep, datetime(2010, 9, 25), True), 
+            (offset_lom_sat_sep, datetime(2011, 9, 24), True),    
+            (offset_lom_sat_sep, datetime(2012, 9, 29), True),       
+            
+        ]
+
+        for offset, date, expected in tests:
+            assertOnOffset(offset, date, expected)
+            
+    def test_apply(self):
+        offset_lom_aug_sat = makeFY5253LastOfMonth(startingMonth=8, weekday=WeekDay.SAT)
+        offset_lom_aug_sat_1 = makeFY5253LastOfMonth(n=1, startingMonth=8, weekday=WeekDay.SAT)
+        
+        date_seq_lom_aug_sat = [datetime(2006, 8, 26), datetime(2007, 8, 25), 
+                                datetime(2008, 8, 30), datetime(2009, 8, 29), 
+                                datetime(2010, 8, 28), datetime(2011, 8, 27), 
+                                datetime(2012, 8, 25), datetime(2013, 8, 31), 
+                                datetime(2014, 8, 30), datetime(2015, 8, 29), 
+                                datetime(2016, 8, 27)]
+        
+        tests = [
+                 (offset_lom_aug_sat, date_seq_lom_aug_sat),
+                 (offset_lom_aug_sat_1, date_seq_lom_aug_sat),
+                 (offset_lom_aug_sat, [datetime(2006, 8, 25)] + date_seq_lom_aug_sat),
+                 (offset_lom_aug_sat_1, [datetime(2006, 8, 27)] + date_seq_lom_aug_sat[1:]),
+                 (makeFY5253LastOfMonth(n=-1, startingMonth=8, weekday=WeekDay.SAT), list(reversed(date_seq_lom_aug_sat))),
+                ]
+        for test in tests:
+            offset, data = test
+            current = data[0]
+            for datum in data[1:]:
+                current = current + offset
+                self.assertEqual(current, datum)
+            
+class TestFY5253NearestEndMonth(unittest.TestCase):
+    def test_get_target_month_end(self):
+        self.assertEqual(makeFY5253NearestEndMonth(startingMonth=8, weekday=WeekDay.SAT).get_target_month_end(datetime(2013,1,1)), datetime(2013,8,31))
+        self.assertEqual(makeFY5253NearestEndMonth(startingMonth=12, weekday=WeekDay.SAT).get_target_month_end(datetime(2013,1,1)), datetime(2013,12,31))
+        self.assertEqual(makeFY5253NearestEndMonth(startingMonth=2, weekday=WeekDay.SAT).get_target_month_end(datetime(2013,1,1)), datetime(2013,2,28))
+        
+    def test_get_year_end(self):
+        self.assertEqual(makeFY5253NearestEndMonth(startingMonth=8, weekday=WeekDay.SAT).get_year_end(datetime(2013,1,1)), datetime(2013,8,31))
+        self.assertEqual(makeFY5253NearestEndMonth(startingMonth=8, weekday=WeekDay.SUN).get_year_end(datetime(2013,1,1)), datetime(2013,9,1))
+        self.assertEqual(makeFY5253NearestEndMonth(startingMonth=8, weekday=WeekDay.FRI).get_year_end(datetime(2013,1,1)), datetime(2013,8,30))
+        
+    def test_onOffset(self):
+        offset_lom_aug_sat = makeFY5253NearestEndMonth(1, startingMonth=8, weekday=WeekDay.SAT)
+        offset_lom_aug_thu = makeFY5253NearestEndMonth(1, startingMonth=8, weekday=WeekDay.THU)
+        
+        tests = [
+#             From Wikipedia (see: http://en.wikipedia.org/wiki/4%E2%80%934%E2%80%935_calendar#Saturday_nearest_the_end_of_month)
+#             2006-09-02   2006 September 2
+#             2007-09-01   2007 September 1
+#             2008-08-30   2008 August 30    (leap year)
+#             2009-08-29   2009 August 29
+#             2010-08-28   2010 August 28
+#             2011-09-03   2011 September 3
+#             2012-09-01   2012 September 1  (leap year)
+#             2013-08-31   2013 August 31
+#             2014-08-30   2014 August 30
+#             2015-08-29   2015 August 29
+#             2016-09-03   2016 September 3  (leap year)
+#             2017-09-02   2017 September 2
+#             2018-09-01   2018 September 1
+#             2019-08-31   2019 August 31
+            (offset_lom_aug_sat, datetime(2006, 9, 2), True),
+            (offset_lom_aug_sat, datetime(2007, 9, 1), True),
+            (offset_lom_aug_sat, datetime(2008, 8, 30), True),
+            (offset_lom_aug_sat, datetime(2009, 8, 29), True),
+            (offset_lom_aug_sat, datetime(2010, 8, 28), True),
+            (offset_lom_aug_sat, datetime(2011, 9, 3), True),
+            
+            (offset_lom_aug_sat, datetime(2016, 9, 3), True),
+            (offset_lom_aug_sat, datetime(2017, 9, 2), True),
+            (offset_lom_aug_sat, datetime(2018, 9, 1), True),            
+            (offset_lom_aug_sat, datetime(2019, 8, 31), True),
+            
+            (offset_lom_aug_sat, datetime(2006, 8, 27), False),
+            (offset_lom_aug_sat, datetime(2007, 8, 28), False),
+            (offset_lom_aug_sat, datetime(2008, 8, 31), False),
+            (offset_lom_aug_sat, datetime(2009, 8, 30), False),
+            (offset_lom_aug_sat, datetime(2010, 8, 29), False),
+            (offset_lom_aug_sat, datetime(2011, 8, 28), False),
+            
+            (offset_lom_aug_sat, datetime(2006, 8, 25), False),
+            (offset_lom_aug_sat, datetime(2007, 8, 24), False),
+            (offset_lom_aug_sat, datetime(2008, 8, 29), False),
+            (offset_lom_aug_sat, datetime(2009, 8, 28), False),
+            (offset_lom_aug_sat, datetime(2010, 8, 27), False),
+            (offset_lom_aug_sat, datetime(2011, 8, 26), False),
+            (offset_lom_aug_sat, datetime(2019, 8, 30), False),
+            
+            #From Micron, see: http://google.brand.edgar-online.com/?sym=MU&formtypeID=7
+            (offset_lom_aug_thu, datetime(2012, 8, 30), True),
+            (offset_lom_aug_thu, datetime(2011, 9, 1), True),
+            
+        ]
+
+        for offset, date, expected in tests:
+            assertOnOffset(offset, date, expected)
+            
+    def test_apply(self):
+        date_seq_nem_8_sat = [datetime(2006, 9, 2), datetime(2007, 9, 1), datetime(2008, 8, 30), datetime(2009, 8, 29), datetime(2010, 8, 28), datetime(2011, 9, 3)]
+        
+        tests = [
+                 (makeFY5253NearestEndMonth(startingMonth=8, weekday=WeekDay.SAT), date_seq_nem_8_sat),
+                 (makeFY5253NearestEndMonth(n=1, startingMonth=8, weekday=WeekDay.SAT), date_seq_nem_8_sat),
+                 (makeFY5253NearestEndMonth(startingMonth=8, weekday=WeekDay.SAT), [datetime(2006, 9, 1)] + date_seq_nem_8_sat),
+                 (makeFY5253NearestEndMonth(n=1, startingMonth=8, weekday=WeekDay.SAT), [datetime(2006, 9, 3)] + date_seq_nem_8_sat[1:]),
+                 (makeFY5253NearestEndMonth(n=-1, startingMonth=8, weekday=WeekDay.SAT), list(reversed(date_seq_nem_8_sat))),
+                ]
+        for test in tests:
+            offset, data = test
+            current = data[0]
+            for datum in data[1:]:
+                current = current + offset
+                self.assertEqual(current, datum)
+
+class TestFY5253LastOfMonthQuarter(unittest.TestCase):
+
+    def test_isAnchored(self):
+        self.assert_(makeFY5253LastOfMonthQuarter(startingMonth=1, weekday=WeekDay.SAT, qtr_with_extra_week=4).isAnchored())
+        self.assert_(makeFY5253LastOfMonthQuarter(weekday=WeekDay.SAT, startingMonth=3, qtr_with_extra_week=4).isAnchored())
+        self.assert_(not makeFY5253LastOfMonthQuarter(2, startingMonth=1, weekday=WeekDay.SAT, qtr_with_extra_week=4).isAnchored())
+        
+    def test_equality(self):
+        self.assertEqual(makeFY5253LastOfMonthQuarter(startingMonth=1, weekday=WeekDay.SAT, qtr_with_extra_week=4), makeFY5253LastOfMonthQuarter(startingMonth=1, weekday=WeekDay.SAT, qtr_with_extra_week=4))
+        self.assertNotEqual(makeFY5253LastOfMonthQuarter(startingMonth=1, weekday=WeekDay.SAT, qtr_with_extra_week=4), makeFY5253LastOfMonthQuarter(startingMonth=1, weekday=WeekDay.SUN, qtr_with_extra_week=4))
+        self.assertNotEqual(makeFY5253LastOfMonthQuarter(startingMonth=1, weekday=WeekDay.SAT, qtr_with_extra_week=4), makeFY5253LastOfMonthQuarter(startingMonth=2, weekday=WeekDay.SAT, qtr_with_extra_week=4))
+        
+    def test_offset(self):
+        offset = makeFY5253LastOfMonthQuarter(1, startingMonth=9, weekday=WeekDay.SAT, qtr_with_extra_week=4)
+        offset2 = makeFY5253LastOfMonthQuarter(2, startingMonth=9, weekday=WeekDay.SAT, qtr_with_extra_week=4)
+        offset4 = makeFY5253LastOfMonthQuarter(4, startingMonth=9, weekday=WeekDay.SAT, qtr_with_extra_week=4)
+        
+        offset_neg1 = makeFY5253LastOfMonthQuarter(-1, startingMonth=9, weekday=WeekDay.SAT, qtr_with_extra_week=4)
+        offset_neg2 = makeFY5253LastOfMonthQuarter(-2, startingMonth=9, weekday=WeekDay.SAT, qtr_with_extra_week=4)
+        
+        GMCR = [datetime(2010, 3, 27), 
+                datetime(2010, 6, 26), 
+                datetime(2010, 9, 25), 
+                datetime(2010, 12, 25), 
+                datetime(2011, 3, 26), 
+                datetime(2011, 6, 25), 
+                datetime(2011, 9, 24), 
+                datetime(2011, 12, 24), 
+                datetime(2012, 3, 24), 
+                datetime(2012, 6, 23), 
+                datetime(2012, 9, 29), 
+                datetime(2012, 12, 29), 
+                datetime(2013, 3, 30), 
+                datetime(2013, 6, 29)]
+
+        
+        assertEq(offset, base=GMCR[0], expected=GMCR[1])  
+        assertEq(offset, base=GMCR[0] + relativedelta(days=-1), expected=GMCR[0])
+        assertEq(offset, base=GMCR[1], expected=GMCR[2])  
+        
+        assertEq(offset2, base=GMCR[0], expected=GMCR[2])
+        assertEq(offset4, base=GMCR[0], expected=GMCR[4])
+        
+        assertEq(offset_neg1, base=GMCR[-1], expected=GMCR[-2])
+        assertEq(offset_neg1, base=GMCR[-1] + relativedelta(days=+1), expected=GMCR[-1])
+        assertEq(offset_neg2, base=GMCR[-1], expected=GMCR[-3])
+        
+        date = GMCR[0] + relativedelta(days=-1)
+        for expected in GMCR:
+            assertEq(offset, date, expected)
+            date = date + offset
+            
+        date = GMCR[-1] + relativedelta(days=+1)
+        for expected in reversed(GMCR):
+            assertEq(offset_neg1, date, expected)
+            date = date + offset_neg1
+            
+
+    def test_onOffset(self):
+        lomq_aug_sat_4 = makeFY5253LastOfMonthQuarter(1, startingMonth=8, weekday=WeekDay.SAT, qtr_with_extra_week=4)
+        lomq_sep_sat_4 = makeFY5253LastOfMonthQuarter(1, startingMonth=9, weekday=WeekDay.SAT, qtr_with_extra_week=4)
+        
+        tests = [
+            #From Wikipedia
+            (lomq_aug_sat_4, datetime(2006, 8, 26), True),
+            (lomq_aug_sat_4, datetime(2007, 8, 25), True),
+            (lomq_aug_sat_4, datetime(2008, 8, 30), True),
+            (lomq_aug_sat_4, datetime(2009, 8, 29), True),
+            (lomq_aug_sat_4, datetime(2010, 8, 28), True),
+            (lomq_aug_sat_4, datetime(2011, 8, 27), True),
+            (lomq_aug_sat_4, datetime(2019, 8, 31), True),
+            
+            (lomq_aug_sat_4, datetime(2006, 8, 27), False),
+            (lomq_aug_sat_4, datetime(2007, 8, 28), False),
+            (lomq_aug_sat_4, datetime(2008, 8, 31), False),
+            (lomq_aug_sat_4, datetime(2009, 8, 30), False),
+            (lomq_aug_sat_4, datetime(2010, 8, 29), False),
+            (lomq_aug_sat_4, datetime(2011, 8, 28), False),
+            
+            (lomq_aug_sat_4, datetime(2006, 8, 25), False),
+            (lomq_aug_sat_4, datetime(2007, 8, 24), False),
+            (lomq_aug_sat_4, datetime(2008, 8, 29), False),
+            (lomq_aug_sat_4, datetime(2009, 8, 28), False),
+            (lomq_aug_sat_4, datetime(2010, 8, 27), False),
+            (lomq_aug_sat_4, datetime(2011, 8, 26), False),
+            (lomq_aug_sat_4, datetime(2019, 8, 30), False),    
+
+            #From GMCR
+            (lomq_sep_sat_4, datetime(2010, 9, 25), True), 
+            (lomq_sep_sat_4, datetime(2011, 9, 24), True),    
+            (lomq_sep_sat_4, datetime(2012, 9, 29), True),       
+            
+            (lomq_sep_sat_4, datetime(2013, 6, 29), True),        
+            (lomq_sep_sat_4, datetime(2012, 6, 23), True),  
+            (lomq_sep_sat_4, datetime(2012, 6, 30), False), 
+                 
+            (lomq_sep_sat_4, datetime(2013, 3, 30), True),        
+            (lomq_sep_sat_4, datetime(2012, 3, 24), True),  
+            
+            (lomq_sep_sat_4, datetime(2012, 12, 29), True),        
+            (lomq_sep_sat_4, datetime(2011, 12, 24), True),  
+            
+            #INTC (extra week in Q1)
+            #See: http://www.intc.com/releasedetail.cfm?ReleaseID=542844
+            (makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1), datetime(2011, 4, 2), True),
+            
+            #see: http://google.brand.edgar-online.com/?sym=INTC&formtypeID=7
+            (makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1), datetime(2012, 12, 29), True),
+            (makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1), datetime(2011, 12, 31), True),
+            (makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1), datetime(2010, 12, 25), True),
+            
+        ]
+
+        for offset, date, expected in tests:
+            assertOnOffset(offset, date, expected)
+            
+    def test_year_has_extra_week(self):
+        #End of long Q1
+        self.assertTrue(makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1).year_has_extra_week(datetime(2011, 4, 2)))
+        
+        #Start of long Q1
+        self.assertTrue(makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1).year_has_extra_week(datetime(2010, 12, 26)))
+        
+        #End of year before year with long Q1
+        self.assertFalse(makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1).year_has_extra_week(datetime(2010, 12, 25)))
+        
+        for year in [x for x in range(1994, 2011+1) if x not in [2011, 2005, 2000, 1994]]: 
+            self.assertFalse(makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1).year_has_extra_week(datetime(year, 4, 2)))
+        
+        #Other long years
+        self.assertTrue(makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1).year_has_extra_week(datetime(2005, 4, 2)))
+        self.assertTrue(makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1).year_has_extra_week(datetime(2000, 4, 2)))
+        self.assertTrue(makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1).year_has_extra_week(datetime(1994, 4, 2)))
+        
+    def test_get_weeks(self):
+        self.assertEqual(makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1).get_weeks(datetime(2011, 4, 2)), [14, 13, 13, 13])
+        self.assertEqual(makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=4).get_weeks(datetime(2011, 4, 2)), [13, 13, 13, 14])
+        self.assertEqual(makeFY5253LastOfMonthQuarter(1, startingMonth=12, weekday=WeekDay.SAT, qtr_with_extra_week=1).get_weeks(datetime(2010, 12, 25)), [13, 13, 13, 13])
+
+class TestFY5253NearestEndMonthQuarter(unittest.TestCase):
+    
+    def test_onOffset(self):
+        
+        offset_nem_sat_aug_4 = makeFY5253NearestEndMonthQuarter(1, startingMonth=8, weekday=WeekDay.SAT, qtr_with_extra_week=4)
+        offset_nem_thu_aug_4 = makeFY5253NearestEndMonthQuarter(1, startingMonth=8, weekday=WeekDay.THU, qtr_with_extra_week=4)
+        tests = [
+            #From Wikipedia
+            (offset_nem_sat_aug_4, datetime(2006, 9, 2), True),
+            (offset_nem_sat_aug_4, datetime(2007, 9, 1), True),
+            (offset_nem_sat_aug_4, datetime(2008, 8, 30), True),
+            (offset_nem_sat_aug_4, datetime(2009, 8, 29), True),
+            (offset_nem_sat_aug_4, datetime(2010, 8, 28), True),
+            (offset_nem_sat_aug_4, datetime(2011, 9, 3), True),
+            
+            (offset_nem_sat_aug_4, datetime(2016, 9, 3), True),
+            (offset_nem_sat_aug_4, datetime(2017, 9, 2), True),
+            (offset_nem_sat_aug_4, datetime(2018, 9, 1), True),            
+            (offset_nem_sat_aug_4, datetime(2019, 8, 31), True),
+            
+            (offset_nem_sat_aug_4, datetime(2006, 8, 27), False),
+            (offset_nem_sat_aug_4, datetime(2007, 8, 28), False),
+            (offset_nem_sat_aug_4, datetime(2008, 8, 31), False),
+            (offset_nem_sat_aug_4, datetime(2009, 8, 30), False),
+            (offset_nem_sat_aug_4, datetime(2010, 8, 29), False),
+            (offset_nem_sat_aug_4, datetime(2011, 8, 28), False),
+            
+            (offset_nem_sat_aug_4, datetime(2006, 8, 25), False),
+            (offset_nem_sat_aug_4, datetime(2007, 8, 24), False),
+            (offset_nem_sat_aug_4, datetime(2008, 8, 29), False),
+            (offset_nem_sat_aug_4, datetime(2009, 8, 28), False),
+            (offset_nem_sat_aug_4, datetime(2010, 8, 27), False),
+            (offset_nem_sat_aug_4, datetime(2011, 8, 26), False),
+            (offset_nem_sat_aug_4, datetime(2019, 8, 30), False),
+            
+            #From Micron, see: http://google.brand.edgar-online.com/?sym=MU&formtypeID=7
+            (offset_nem_thu_aug_4, datetime(2012, 8, 30), True),
+            (offset_nem_thu_aug_4, datetime(2011, 9, 1), True),
+            
+            #See: http://google.brand.edgar-online.com/?sym=MU&formtypeID=13
+            (offset_nem_thu_aug_4, datetime(2013, 5, 30), True),
+            (offset_nem_thu_aug_4, datetime(2013, 2, 28), True),
+            (offset_nem_thu_aug_4, datetime(2012, 11, 29), True),
+            (offset_nem_thu_aug_4, datetime(2012, 5, 31), True),
+            (offset_nem_thu_aug_4, datetime(2007, 3, 1), True),
+            (offset_nem_thu_aug_4, datetime(1994, 3, 3), True),
+            
+        ]
+
+        for offset, date, expected in tests:
+            assertOnOffset(offset, date, expected)
+
+    def test_offset(self):
+        offset = makeFY5253NearestEndMonthQuarter(1, startingMonth=8, weekday=WeekDay.THU, qtr_with_extra_week=4)
+        
+        MU = [datetime(2012, 5, 31), datetime(2012, 8, 30), datetime(2012, 11, 29), datetime(2013, 2, 28), datetime(2013, 5, 30)]
+        
+        date = MU[0] + relativedelta(days=-1)
+        for expected in MU:
+            assertEq(offset, date, expected)
+            date = date + offset
+            
+        assertEq(offset, datetime(2012, 5, 31), datetime(2012, 8, 30))
+        assertEq(offset, datetime(2012, 5, 30), datetime(2012, 5, 31))
+            
 class TestQuarterBegin(unittest.TestCase):
     def test_repr(self):
         self.assertEqual(repr(QuarterBegin()), "<QuarterBegin: startingMonth=3>")
@@ -1748,32 +2193,43 @@ def test_compare_ticks():
             assert(kls(3) != kls(4))
 
 
-def test_get_offset_name():
-    assertRaisesRegexp(ValueError, 'Bad rule.*BusinessDays', get_offset_name, BDay(2))
-
-    assert get_offset_name(BDay()) == 'B'
-    assert get_offset_name(BMonthEnd()) == 'BM'
-    assert get_offset_name(Week(weekday=0)) == 'W-MON'
-    assert get_offset_name(Week(weekday=1)) == 'W-TUE'
-    assert get_offset_name(Week(weekday=2)) == 'W-WED'
-    assert get_offset_name(Week(weekday=3)) == 'W-THU'
-    assert get_offset_name(Week(weekday=4)) == 'W-FRI'
+class TestOffsetNames(unittest.TestCase):    
+    def test_get_offset_name(self):
+        assertRaisesRegexp(ValueError, 'Bad rule.*BusinessDays', get_offset_name, BDay(2))
+    
+        assert get_offset_name(BDay()) == 'B'
+        assert get_offset_name(BMonthEnd()) == 'BM'
+        assert get_offset_name(Week(weekday=0)) == 'W-MON'
+        assert get_offset_name(Week(weekday=1)) == 'W-TUE'
+        assert get_offset_name(Week(weekday=2)) == 'W-WED'
+        assert get_offset_name(Week(weekday=3)) == 'W-THU'
+        assert get_offset_name(Week(weekday=4)) == 'W-FRI'
 
+        self.assertEqual(get_offset_name(LastWeekOfMonth(weekday=WeekDay.SUN)), "LWOM-SUN")
+        self.assertEqual(get_offset_name(makeFY5253LastOfMonthQuarter(weekday=1, startingMonth=3, qtr_with_extra_week=4)),"REQ-L-MAR-TUE-4")
+        self.assertEqual(get_offset_name(makeFY5253NearestEndMonthQuarter(weekday=1, startingMonth=3, qtr_with_extra_week=3)), "REQ-N-MAR-TUE-3")
 
 def test_get_offset():
     assertRaisesRegexp(ValueError, "rule.*GIBBERISH", get_offset, 'gibberish')
     assertRaisesRegexp(ValueError, "rule.*QS-JAN-B", get_offset, 'QS-JAN-B')
-    pairs = [('B', BDay()), ('b', BDay()), ('bm', BMonthEnd()),
+    pairs = [
+             ('B', BDay()), ('b', BDay()), ('bm', BMonthEnd()),
              ('Bm', BMonthEnd()), ('W-MON', Week(weekday=0)),
              ('W-TUE', Week(weekday=1)), ('W-WED', Week(weekday=2)),
              ('W-THU', Week(weekday=3)), ('W-FRI', Week(weekday=4)),
-             ('w@Sat', Week(weekday=5))]
+             ('w@Sat', Week(weekday=5)),
+             ("RE-N-DEC-MON", makeFY5253NearestEndMonth(weekday=0, startingMonth=12)),
+             ("RE-L-DEC-TUE", makeFY5253LastOfMonth(weekday=1, startingMonth=12)),
+             ("REQ-L-MAR-TUE-4", makeFY5253LastOfMonthQuarter(weekday=1, startingMonth=3, qtr_with_extra_week=4)),
+             ("REQ-L-DEC-MON-3", makeFY5253LastOfMonthQuarter(weekday=0, startingMonth=12, qtr_with_extra_week=3)),
+             ("REQ-N-DEC-MON-3", makeFY5253NearestEndMonthQuarter(weekday=0, startingMonth=12, qtr_with_extra_week=3)),
+             ]
 
     for name, expected in pairs:
         offset = get_offset(name)
         assert offset == expected, ("Expected %r to yield %r (actual: %r)" %
                                     (name, expected, offset))
-
+        
 
 def test_parse_time_string():
     (date, parsed, reso) = parse_time_string('4Q1984')
@@ -1879,8 +2335,11 @@ def get_all_subclasses(cls):
         ret | get_all_subclasses(this_subclass)
     return ret
 
-
 class TestCaching(unittest.TestCase):
+    no_simple_ctr = [WeekOfMonth, FY5253,
+                     FY5253Quarter, 
+                     LastWeekOfMonth]
+    
     def test_should_cache_month_end(self):
         self.assertTrue(MonthEnd()._should_cache())
 
@@ -1892,7 +2351,8 @@ def test_should_cache_week_month(self):
 
     def test_all_cacheableoffsets(self):
         for subclass in get_all_subclasses(CacheableOffset):
-            if subclass in [WeekOfMonth]:
+            if subclass.__name__[0] == "_" \
+                or subclass in TestCaching.no_simple_ctr:
                 continue
             self.run_X_index_creation(subclass)
 
diff --git a/pandas/tseries/tests/test_period.py b/pandas/tseries/tests/test_period.py
--- a/pandas/tseries/tests/test_period.py
+++ b/pandas/tseries/tests/test_period.py
@@ -199,7 +199,7 @@ def test_period_constructor(self):
 
         self.assertRaises(ValueError, Period, ordinal=200701)
 
-        self.assertRaises(KeyError, Period, '2007-1-1', freq='X')
+        self.assertRaises(ValueError, Period, '2007-1-1', freq='X')
 
     def test_freq_str(self):
         i1 = Period('1982', freq='Min')
@@ -1136,8 +1136,8 @@ def test_constructor_field_arrays(self):
         self.assert_(idx.equals(exp))
 
     def test_constructor_U(self):
-        # X was used as undefined period
-        self.assertRaises(KeyError, period_range, '2007-1-1', periods=500,
+        # U was used as undefined period
+        self.assertRaises(ValueError, period_range, '2007-1-1', periods=500,
                           freq='X')
 
     def test_constructor_arrays_negative_year(self):
diff --git a/pandas/tseries/tests/test_timeseries.py b/pandas/tseries/tests/test_timeseries.py
--- a/pandas/tseries/tests/test_timeseries.py
+++ b/pandas/tseries/tests/test_timeseries.py
@@ -2664,7 +2664,7 @@ def test_frequency_misc(self):
         expected = offsets.Minute(5)
         self.assertEquals(result, expected)
 
-        self.assertRaises(KeyError, fmod.get_freq_code, (5, 'baz'))
+        self.assertRaises(ValueError, fmod.get_freq_code, (5, 'baz'))
 
         self.assertRaises(ValueError, fmod.to_offset, '100foo')
 
@@ -3031,6 +3031,15 @@ def test_frame_apply_dont_convert_datetime64(self):
         df = df.applymap(lambda x: x + BDay())
 
         self.assertTrue(df.x1.dtype == 'M8[ns]')
+        
+    def test_date_range_fy5252(self):
+        dr = date_range(start="2013-01-01", 
+                           periods=2, 
+                           freq=offsets.FY5253(startingMonth=1,
+                                               weekday=3,
+                                               variation="nearest"))
+        self.assertEqual(dr[0], Timestamp('2013-01-31'))
+        self.assertEqual(dr[1], Timestamp('2014-01-30'))
 
 
 if __name__ == '__main__':
diff --git a/pandas/tseries/tests/test_tslib.py b/pandas/tseries/tests/test_tslib.py
--- a/pandas/tseries/tests/test_tslib.py
+++ b/pandas/tseries/tests/test_tslib.py
@@ -283,6 +283,10 @@ def test_period_ordinal_business_day(self):
         # Tuesday
         self.assertEqual(11418, period_ordinal(2013, 10, 8, 0, 0, 0, 0, 0, get_freq('B')))
 
+class TestTomeStampOps(unittest.TestCase):
+    def test_timestamp_and_datetime(self):
+        self.assertEqual((Timestamp(datetime.datetime(2013, 10,13)) - datetime.datetime(2013, 10,12)).days, 1)
+        self.assertEqual((datetime.datetime(2013, 10, 12) - Timestamp(datetime.datetime(2013, 10,13))).days, -1)
 
 if __name__ == '__main__':
     nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],