adamjstewart / fiscalyear Goto Github PK
View Code? Open in Web Editor NEW:calendar: Utilities for managing the fiscal calendar
License: MIT License
:calendar: Utilities for managing the fiscal calendar
License: MIT License
I just noticed that the code is primarily inside a __init__.py
. That's an unconventional choice which seems to work fine, but I'm just curious why it was made as a design choice for this package.
It would be convenient to be able to convert back to an ordinary datetime object. I could have missed this, but I didn't see it in the documentation. I imported the module and took a look around and didn't see explicit support for this.
Being in Canada, the conversion is straightforward using the FiscalYear
data structure in my own code. But I figured this would be something nice to have in the fiscalyear package itself.
If the FiscalCalendar
starts on the 31st of any month with 31 days, then FiscalDate().quarter
throws a ValueError
.
Example code:
from fiscalyear import FiscalDate, FiscalYear, fiscal_calendar
with fiscal_calendar(start_month=12, start_day=31):
fiscal_date = FiscalDate(2019, 10, 22)
fiscal_date.quarter
Traceback:
Traceback (most recent call last):
File "<input>", line 3, in <module>
File "/site-packages/fiscalyear.py", line 594, in quarter
if self in q:
File "/site-packages/fiscalyear.py", line 413, in __contains__
return self.start.date() <= item <= self.end.date()
File "/site-packages/fiscalyear.py", line 493, in end
next_start = self.next_quarter.start
File "/site-packages/fiscalyear.py", line 485, in start
return FiscalDateTime(year, month, START_DAY, 0, 0, 0)
ValueError: day is out of range for month
The quarters generated are:
Is there any way to get the Week number for given date in a fiscal year or any plans to support this API?
Ex:
import fiscalyear
fiscalyear.START_MONTH = 7
fiscalyear.START_DAY = 1
cur_date = fiscalyear.FiscalDate(2019, 7, 1)
print(cur_date.week) -> should give 1
Fiscal Month offset does not appear to match fiscal periods
>>>fiscalyear.setup_fiscal_calendar('previous', 12, 29)
>>>c=FiscalMonth(2021, 1)
>>>c.start
FiscalDateTime(2020, 12, 29, 0, 0)
>>>c.end
FiscalDateTime(2021, 1, 28, 23, 59, 59)
>>>a = FiscalDate(2021, 1, 28)
>>>b = FiscalDate(2021, 1, 29)
>>>a.fiscal_month
2
>>>b.fiscal_month
2
>>>c.fiscal_month
1
>>>a in c
True
>>>b in c
False
I think it would be nice if there was an easy way to get the current FiscalYear
/FiscalQuarter
.
This could easily be achieved by using a couple of classmethods
. E.g.:
$ git diff
diff --git a/fiscalyear.py b/fiscalyear.py
index c6f8ddc..f7213d7 100644
--- a/fiscalyear.py
+++ b/fiscalyear.py
@@ -191,6 +191,11 @@ class FiscalYear(object):
self._fiscal_year = fiscal_year
return self
+ @classmethod
+ def current(cls):
+ today = datetime.date.today()
+ return cls(today.year)
+
def __repr__(self):
"""Convert to formal string, for repr().
@@ -371,6 +376,11 @@ class FiscalQuarter(object):
self._quarter = quarter
return self
+ @classmethod
+ def current(cls):
+ today = datetime.date.today()
+ return cls(today.year, (today.month // 3) + 1)
+
def __repr__(self):
"""Convert to formal string, for repr().
This way you could write:
In [3]: FiscalYear.current()
Out[3]: FiscalYear(2018)
In [4]: FiscalQuarter.current()
Out[4]: FiscalQuarter(2018, 1)
If you want, I can try to make a pull request.
Hello, If my assumption is correct, I was expecting the lib to return fiscal_year=2021
when the calendar is set to start on January to follow the calendar year, since the end date (2021-12-31) still falls in 2021.
Code to reproduce:
import fiscalyear
import datetime
start_month = 1
sdate = datetime.datetime(2021, 1, 1)
with fiscalyear.fiscal_calendar(start_day=1, start_month=start_month, start_year='previous'):
f = fiscalyear.FiscalDate(sdate.year, sdate.month, sdate.day)
print('ISO:', f.isoformat(), ' FY:', f.fiscal_year, ' FQ:', f.fiscal_quarter, ' FM:', f.fiscal_month)
fy = fiscalyear.FiscalYear(f.fiscal_year)
print('FY period:', fy.start.date(), '/', fy.end.date())
Output:
ISO: 2021-01-01 FY: 2022 FQ: 1 FM: 1
FY period: 2021-01-01 / 2021-12-31
Is this expected? if yes, how does one switch to standard year when following the calendar year?
I think it would make sense to have a function that would make changing the global parameters easier. E.g.
def setup_fiscal_year(start_year, start_month, start_day):
global START_YEAR, START_MONTH, START_DAY
START_YEAR = start_year
START_MONTH = start_month
START_DAY = start_day
def test_setup_fiscal_year():
# test defaults
day = fiscalyear.FiscalDate(2017, 12, 1)
assert day.fiscal_year == 2018
assert day.quarter == 1
# change fiscal year settings
fiscalyear.setup_fiscal_year("same", 1, 1)
assert day.fiscal_year == 2017
assert day.quarter == 4
# restore defaults and re-test
fiscalyear.setup_fiscal_year("previous", 10, 1)
assert day.fiscal_year == 2018
assert day.quarter == 1
This could also make it possible to change the Fiscal Year settings even if you don't import the whole module. E.g.
In [4]: from fiscalyear import FiscalQuarter, setup_fiscal_year
In [5]: quarter = FiscalQuarter(2018, 1)
In [6]: quarter.start
Out[6]: FiscalDateTime(2017, 10, 1, 0, 0)
In [7]: setup_fiscal_year('same', 1, 1)
In [8]: quarter.start
Out[8]: FiscalDateTime(2018, 1, 1, 0, 0)
The fiscal_day code incorrectly calculates the date delta. Examples:
>>> fiscalyear.FiscalDate(2016,9,30).fiscal_day
1
>>> fiscalyear.FiscalDate(2017,9,30).fiscal_day
365
>>> fiscalyear.FiscalDate(2018,9,30).fiscal_day
366
Some companies use February for their end month for the fiscal year e.g. CarMax and their 10-K with last day as Feb 29, 2016 https://www.sec.gov/Archives/edgar/data/0001170010/000117001016000117/kmx0229201610-k.htm
Currently _check_day
use 2001
a non-leap year to validate the day of the month:
def _check_day(month, day):
"""Check if day is a valid day of month.
:param month: The month to test
:param day: The day to test
:return: The day
:rtype: int
:raises TypeError: If month or day is not an int or int-like string
:raises ValueError: If month or day is out of range
"""
month = _check_month(month)
day = _check_int(day)
# Find the last day of the month
# Use a non-leap year
max_day = calendar.monthrange(2001, month)[1]
Resolution would be to use year
if provided e.g.
def _check_day(day, month, year=None):
"""Check if day is a valid day of month.
:param day: The day to test
:param month: The month to test
:param day: Optional; the year to test; defaults to 2001 a non-leap year
:return: The day
:rtype: int
:raises TypeError: If month or day is not an int or int-like string
:raises ValueError: If month or day is out of range
"""
year = year if year else 2001
month = _check_month(month)
day = _check_int(day)
# Find the last day of the month
max_day = calendar.monthrange(year, month)[1]
Note, in this example, changed order of args (for OCD purposes :) with year last as optional
My current usecase does not fall in this category, but a relatively subtle implication of the current implementation is that it is not really possible to use different fiscal calendar settings in the same application. The problem is that as soon as you update the global settings, the behaviour of the existing objects changes too. For example:
In [2]: from fiscalyear import *
In [3]: y = FiscalYear(2018)
In [4]: y.start
Out[4]: FiscalDateTime(2017, 10, 1, 0, 0)
In [5]: setup_fiscal_calendar('same', 1, 1)
In [6]: y.start
Out[6]: FiscalDateTime(2018, 1, 1, 0, 0)
Arguably, someone could claim that this is a feature too! :P
And in certain contexts, it probably is! Neverheless, it can also lead to subtle bugs and various errors when you do calculations. If nothing else, I think that there should be at least a warning in the docs.
PS. The older I get the more I like immutable objects :P
A user is requesting support for a 4-4-5 calendar. This is quite a bit different than the original implementation I went with, but is inline with my original goals. This will likely require a major refactoring of the module, so it may take some time.
If you also would like to see support for 4-4-5, 4-5-4, or 5-4-4 calendars, give this post a ๐. If you want to see support for a different calendar, open another issue. If you have any suggestions for how to implement a 4-4-5 calendar, including the UI for selecting which calendar to use, feel free to post a comment on this issue.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.