fiscal-pi 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fiscal_pi-0.1.1/LICENSE.txt +21 -0
- fiscal_pi-0.1.1/PKG-INFO +628 -0
- fiscal_pi-0.1.1/README.md +588 -0
- fiscal_pi-0.1.1/fiscal/__init__.py +2778 -0
- fiscal_pi-0.1.1/fiscal/boogr.py +562 -0
- fiscal_pi-0.1.1/fiscal/config.py +205 -0
- fiscal_pi-0.1.1/fiscal/fte.py +254 -0
- fiscal_pi-0.1.1/fiscal/sqlite/__init__.py +5 -0
- fiscal_pi-0.1.1/fiscal/sqlite/data.db +0 -0
- fiscal_pi-0.1.1/fiscal/utilities.py +147 -0
- fiscal_pi-0.1.1/fiscal_pi.egg-info/PKG-INFO +628 -0
- fiscal_pi-0.1.1/fiscal_pi.egg-info/SOURCES.txt +18 -0
- fiscal_pi-0.1.1/fiscal_pi.egg-info/dependency_links.txt +1 -0
- fiscal_pi-0.1.1/fiscal_pi.egg-info/requires.txt +21 -0
- fiscal_pi-0.1.1/fiscal_pi.egg-info/top_level.txt +1 -0
- fiscal_pi-0.1.1/pyproject.toml +65 -0
- fiscal_pi-0.1.1/setup.cfg +4 -0
- fiscal_pi-0.1.1/tests/test_boogr.py +27 -0
- fiscal_pi-0.1.1/tests/test_fiscal.py +223 -0
- fiscal_pi-0.1.1/tests/test_fte.py +87 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Terry D. Eppler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
fiscal_pi-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fiscal-pi
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: U.S. federal fiscal-year, holiday, workday, and FTE calculations.
|
|
5
|
+
Author: Terry D. Eppler
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Documentation, https://is-leeroy-jenkins.github.io/fiscal/
|
|
8
|
+
Project-URL: Repository, https://github.com/is-leeroy-jenkins/fiscal
|
|
9
|
+
Project-URL: Issues, https://github.com/is-leeroy-jenkins/fiscal/issues
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE.txt
|
|
21
|
+
Requires-Dist: pandas>=2.0
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
24
|
+
Provides-Extra: docs
|
|
25
|
+
Requires-Dist: mkdocs>=1.6; extra == "docs"
|
|
26
|
+
Requires-Dist: mkdocs-material>=9.6; extra == "docs"
|
|
27
|
+
Requires-Dist: mkdocs-autorefs>=1.4; extra == "docs"
|
|
28
|
+
Requires-Dist: mkdocstrings[python]>=0.30; extra == "docs"
|
|
29
|
+
Requires-Dist: pymdown-extensions>=10.16; extra == "docs"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: black>=25.0; extra == "dev"
|
|
32
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
33
|
+
Requires-Dist: mkdocs>=1.6; extra == "dev"
|
|
34
|
+
Requires-Dist: mkdocs-material>=9.6; extra == "dev"
|
|
35
|
+
Requires-Dist: mkdocs-autorefs>=1.4; extra == "dev"
|
|
36
|
+
Requires-Dist: mkdocstrings[python]>=0.30; extra == "dev"
|
|
37
|
+
Requires-Dist: pymdown-extensions>=10.16; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
###### fiscal
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+
<p align="left">
|
|
46
|
+
<a href="#features">Features</a>
|
|
47
|
+
•
|
|
48
|
+
<a href="#installation">Installation</a>
|
|
49
|
+
•
|
|
50
|
+
<a href="#configuration">Configuration</a>
|
|
51
|
+
•
|
|
52
|
+
<a href="#quick-start">Quick Start</a>
|
|
53
|
+
•
|
|
54
|
+
<a href="#api-overview">API Overview</a>
|
|
55
|
+
•
|
|
56
|
+
<a href="https://github.com/is-leeroy-jenkins/Fiscal/blob/master/resources/USER_GUIDE.md">User Guide</a>
|
|
57
|
+
•
|
|
58
|
+
<a href="https://github.com/is-leeroy-jenkins/Fiscal/blob/master/resources/DEVELOPER_GUIDE.md">Developer Guide</a>
|
|
59
|
+
•
|
|
60
|
+
<a href="#license">License</a>
|
|
61
|
+
</p>
|
|
62
|
+
|
|
63
|
+
___
|
|
64
|
+
|
|
65
|
+
Fiscal is a Python library providing a framework for executing U.S. federal fiscal-year and
|
|
66
|
+
calendar-year calculations. It provides fiscal years and federal holidays with date-range analysis,
|
|
67
|
+
fiscal periods, workday and work-hour calculations, FTE calculations, and actual or observed
|
|
68
|
+
federal-holiday handling.
|
|
69
|
+
|
|
70
|
+
## 📖 Documentation
|
|
71
|
+
[](https://is-leeroy-jenkins.github.io/fiscal/)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
<a id="features"></a>
|
|
75
|
+
|
|
76
|
+
## 📝 Features
|
|
77
|
+
|
|
78
|
+
- SQLite-backed fiscal-year and federal-holiday records
|
|
79
|
+
- Fiscal-year lookup by fiscal year, BPOA, and EPOA
|
|
80
|
+
- Caller-selected calculation dates for reproducible analysis
|
|
81
|
+
- Calendar-year and fiscal-year progress calculations
|
|
82
|
+
- Fiscal-month boundaries, names, day counts, week matrices, and rendered calendars
|
|
83
|
+
- Plain-text fiscal-month and fiscal-year calendars using `calendar.TextCalendar`
|
|
84
|
+
- HTML fiscal-month and fiscal-year calendars using `calendar.HTMLCalendar`
|
|
85
|
+
- Fiscal-quarter identification, boundaries, and day counts
|
|
86
|
+
- ISO calendar-week and fiscal-week calculations
|
|
87
|
+
- Weekday occurrence counts using names such as `"Monday"`
|
|
88
|
+
- Fiscal-year date, weekday, weekend, and workday collections
|
|
89
|
+
- Monthly weekday, weekend, workday, and holiday summaries
|
|
90
|
+
- Actual and observed federal-holiday dates
|
|
91
|
+
- Inclusive date-range counts constrained to the represented fiscal year
|
|
92
|
+
- Compensable-hour, federal work-hour, and annual FTE calculations for inclusive date ranges
|
|
93
|
+
- Elapsed and remaining compensable-hour and federal work-hour calculations
|
|
94
|
+
- Holiday range results as native `date` values or ISO strings
|
|
95
|
+
- Remaining holiday, workday, and weekend counts
|
|
96
|
+
- OMB regular-method and pay-period-method civilian FTE calculations
|
|
97
|
+
- Exact decimal FTE results and inverse FTE-to-hours planning calculations
|
|
98
|
+
- Leap-day detection
|
|
99
|
+
- Fiscal-year and federal-holiday dictionary exports
|
|
100
|
+
- Backward-compatible aliases for existing calendar methods
|
|
101
|
+
- Operational exception wrapping with `boogr.Error`
|
|
102
|
+
|
|
103
|
+
<a id="installation"></a>
|
|
104
|
+
|
|
105
|
+
## 🏗️ Installation
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
|
|
109
|
+
pip install fiscal-pi
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The PyPI distribution is named `fiscal-pi`; the installed Python package remains `fiscal`:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
|
|
117
|
+
import fiscal
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Install the development dependencies when working from a source checkout:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
|
|
125
|
+
pip install -e ".[dev]"
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
<a id="configuration"></a>
|
|
130
|
+
|
|
131
|
+
## ⚙️ Configuration
|
|
132
|
+
|
|
133
|
+
Fiscal includes its SQLite data and diagnostic configuration. Override paths with environment
|
|
134
|
+
variables only when an application needs external data or log storage:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
|
|
138
|
+
DB_PATH=/path/to/fiscal.db
|
|
139
|
+
LOG_PATH=/path/to/Exceptions.db
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
<a id="quick-start"></a>
|
|
144
|
+
|
|
145
|
+

|
|
146
|
+
|
|
147
|
+
___
|
|
148
|
+
|
|
149
|
+
## 🎯 Quick Start
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
|
|
153
|
+
from datetime import date
|
|
154
|
+
|
|
155
|
+
from fiscal import FederalHoliday, FiscalYear, FullTimeEquivalent
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Create a fiscal-year object with a fixed calculation date:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
|
|
163
|
+
fy = FiscalYear(
|
|
164
|
+
fy=2026,
|
|
165
|
+
current_date=date( 2026, 7, 15 ),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Using an explicit `current_date` makes fiscal progress calculations reproducible. When omitted, Fiscal uses the current system date.
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
|
|
174
|
+
print( fy.fiscal_year )
|
|
175
|
+
print( fy.start_date )
|
|
176
|
+
print( fy.end_date )
|
|
177
|
+
print( fy.fiscal_month_number( ) )
|
|
178
|
+
print( fy.fiscal_quarter_number( ) )
|
|
179
|
+
print( fy.fiscal_week_number( ) )
|
|
180
|
+
print( fy.fiscal_percent_elapsed( ) )
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Fiscal-Year Record
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
|
|
188
|
+
print( fy.bpoa )
|
|
189
|
+
print( fy.epoa )
|
|
190
|
+
print( fy.expiration_date )
|
|
191
|
+
print( fy.cancellation_date )
|
|
192
|
+
print( fy.weekdays )
|
|
193
|
+
print( fy.weekends )
|
|
194
|
+
print( fy.workdays )
|
|
195
|
+
print( fy.compensable_days )
|
|
196
|
+
print( fy.compensable_workdays )
|
|
197
|
+
print( fy.compensable_hours )
|
|
198
|
+
print( fy.type )
|
|
199
|
+
print( fy.availability )
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Full-Time Equivalents
|
|
204
|
+
|
|
205
|
+
Use OMB's regular method for October-through-September hours and its pay-period method for the 26
|
|
206
|
+
selected biweekly pay periods:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
|
|
210
|
+
fte = FullTimeEquivalent( 2026 )
|
|
211
|
+
|
|
212
|
+
regular_result = fte.regular_method( 1044 )
|
|
213
|
+
pay_period_result = fte.pay_period_method( 1040 )
|
|
214
|
+
|
|
215
|
+
print( fte.compensable_days ) # 261
|
|
216
|
+
print( fte.compensable_hours ) # Decimal('2088')
|
|
217
|
+
print( regular_result ) # Decimal('0.5')
|
|
218
|
+
print( pay_period_result ) # Decimal('0.5')
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
See the [FTE guide]([https://is-leeroy-jenkins.github.io/Fiscal/user-guide/full-time-equivalents/](https://github.com/is-leeroy-jenkins/fiscal/blob/master/resources/USER_GUIDE.md#calculate-full-time-equivalents))
|
|
223
|
+
for qualifying-hour rules and the 27-pay-period exception.
|
|
224
|
+
|
|
225
|
+
Multi-year availability:
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
|
|
229
|
+
fy = FiscalYear(
|
|
230
|
+
fy=2026,
|
|
231
|
+
bpoa=2024,
|
|
232
|
+
epoa=2026,
|
|
233
|
+
current_date=date( 2026, 7, 15 ),
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Calendar and Fiscal Progress
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
|
|
242
|
+
calendar_status = {
|
|
243
|
+
"DayOfYear": fy.calendar_day_of_year( ),
|
|
244
|
+
"DaysElapsed": fy.calendar_days_elapsed( ),
|
|
245
|
+
"DaysRemaining": fy.calendar_days_remaining( ),
|
|
246
|
+
"MonthsElapsed": fy.calendar_months_elapsed( ),
|
|
247
|
+
"MonthsRemaining": fy.calendar_months_remaining( ),
|
|
248
|
+
"PercentElapsed": fy.calendar_percent_elapsed( ),
|
|
249
|
+
"WeekNumber": fy.calendar_week_number( ),
|
|
250
|
+
"MonthName": fy.calendar_month_name( ),
|
|
251
|
+
"WeekdayName": fy.current_weekday_name( ),
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
fiscal_status = {
|
|
255
|
+
"DayOfYear": fy.fiscal_day_of_year( ),
|
|
256
|
+
"DaysElapsed": fy.fiscal_days_elapsed( ),
|
|
257
|
+
"DaysRemaining": fy.fiscal_days_remaining( ),
|
|
258
|
+
"MonthsElapsed": fy.fiscal_months_elapsed( ),
|
|
259
|
+
"MonthsRemaining": fy.fiscal_months_remaining( ),
|
|
260
|
+
"PercentElapsed": fy.fiscal_percent_elapsed( ),
|
|
261
|
+
"MonthNumber": fy.fiscal_month_number( ),
|
|
262
|
+
"QuarterNumber": fy.fiscal_quarter_number( ),
|
|
263
|
+
"WeekNumber": fy.fiscal_week_number( ),
|
|
264
|
+
"CompensableHoursElapsed": fy.compensable_hours_elapsed( ),
|
|
265
|
+
"CompensableHoursRemaining": fy.compensable_hours_remaining( ),
|
|
266
|
+
"WorkHoursElapsed": fy.work_hours_elapsed( ),
|
|
267
|
+
"WorkHoursRemaining": fy.work_hours_remaining( ),
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Fiscal Months and Quarters
|
|
273
|
+
|
|
274
|
+
Fiscal months are numbered from October through September.
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
|
|
278
|
+
month_start, month_end = fy.fiscal_month_bounds( 10 )
|
|
279
|
+
month_name = fy.fiscal_month_name( 10 )
|
|
280
|
+
month_days = fy.fiscal_days_in_month( 10 )
|
|
281
|
+
quarter_start, quarter_end = fy.fiscal_quarter_bounds( 4 )
|
|
282
|
+
quarter_days = fy.fiscal_days_in_quarter( 4 )
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Count weekday occurrences without importing `calendar`:
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
|
|
290
|
+
mondays = fy.weekday_occurrences(
|
|
291
|
+
fiscal_month=10,
|
|
292
|
+
weekday="Monday",
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Integer weekday values from `0` through `6` remain supported for compatibility.
|
|
298
|
+
|
|
299
|
+
### Text and HTML Calendars
|
|
300
|
+
|
|
301
|
+
Render one fiscal month as plain text:
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
|
|
305
|
+
text_month = fy.fiscal_month_text_calendar(
|
|
306
|
+
fiscal_month=10,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Render October through September as plain text:
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
|
|
315
|
+
text_year = fy.fiscal_year_text_calendar( )
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Render one fiscal month as an HTML table:
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
|
|
323
|
+
html_month = fy.fiscal_month_html_calendar(
|
|
324
|
+
fiscal_month=10,
|
|
325
|
+
with_year=True,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Render the entire fiscal year in rows of three month tables:
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
|
|
334
|
+
html_year = fy.fiscal_year_html_calendar(
|
|
335
|
+
width=3,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
The fiscal-year renderers preserve federal fiscal order from October through September.
|
|
341
|
+
|
|
342
|
+
### Date-Range Analysis
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
|
|
346
|
+
start_date = date( 2026, 7, 1 )
|
|
347
|
+
end_date = date( 2026, 7, 31 )
|
|
348
|
+
|
|
349
|
+
summary = {
|
|
350
|
+
"WeekendDays": fy.count_weekends(
|
|
351
|
+
start=start_date,
|
|
352
|
+
end=end_date,
|
|
353
|
+
),
|
|
354
|
+
"FederalHolidays": fy.count_holidays(
|
|
355
|
+
start=start_date,
|
|
356
|
+
end=end_date,
|
|
357
|
+
),
|
|
358
|
+
"Workdays": fy.count_workdays(
|
|
359
|
+
start=start_date,
|
|
360
|
+
end=end_date,
|
|
361
|
+
),
|
|
362
|
+
"CompensableHours": fy.compensable_hours_between(
|
|
363
|
+
start=start_date,
|
|
364
|
+
end=end_date,
|
|
365
|
+
),
|
|
366
|
+
"WorkHours": fy.work_hours_between(
|
|
367
|
+
start=start_date,
|
|
368
|
+
end=end_date,
|
|
369
|
+
),
|
|
370
|
+
"FTE": fy.fte_between(
|
|
371
|
+
start=start_date,
|
|
372
|
+
end=end_date,
|
|
373
|
+
),
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Range operations are inclusive and constrained to the represented fiscal year. A reversed range or a range that does not intersect the fiscal year raises `boogr.Error`.
|
|
379
|
+
|
|
380
|
+
`compensable_hours_between()` counts every Monday-through-Friday date, including federal holidays,
|
|
381
|
+
consistent with OMB's regular-method denominator. `work_hours_between()` excludes federal holidays
|
|
382
|
+
and therefore represents scheduled operational work hours. Both methods accept a positive
|
|
383
|
+
`hours_per_day` value and return an exact `Decimal` result.
|
|
384
|
+
|
|
385
|
+
`fte_between()` divides the range's compensable hours by the selected fiscal year's
|
|
386
|
+
`CompensableHours` value. It returns the annual FTE represented by the range, not an annualized rate
|
|
387
|
+
for the shorter period. For example, a full FY 2026 at six hours per compensable weekday returns
|
|
388
|
+
`Decimal("0.75")`.
|
|
389
|
+
|
|
390
|
+
```python
|
|
391
|
+
|
|
392
|
+
part_time_hours = fy.work_hours_between(
|
|
393
|
+
start=start_date,
|
|
394
|
+
end=end_date,
|
|
395
|
+
hours_per_day=6,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Use actual holiday dates instead of observed dates:
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
|
|
404
|
+
actual_workdays = fy.count_workdays(
|
|
405
|
+
start=start_date,
|
|
406
|
+
end=end_date,
|
|
407
|
+
use_observed=False,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Return native date values:
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
|
|
416
|
+
holiday_dates = fy.holiday_dates_between(
|
|
417
|
+
start=start_date,
|
|
418
|
+
end=end_date,
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Return ISO-formatted strings for compatibility:
|
|
424
|
+
|
|
425
|
+
```python
|
|
426
|
+
|
|
427
|
+
holiday_text = fy.holidays_between(
|
|
428
|
+
start=start_date,
|
|
429
|
+
end=end_date,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Date Collections and Monthly Summaries
|
|
435
|
+
|
|
436
|
+
```python
|
|
437
|
+
|
|
438
|
+
dates = fy.fiscal_dates( )
|
|
439
|
+
weekdays = fy.fiscal_weekdays( )
|
|
440
|
+
weekends = fy.fiscal_weekends( )
|
|
441
|
+
workdays = fy.fiscal_workdays( )
|
|
442
|
+
|
|
443
|
+
dates_by_month = fy.dates_by_month( )
|
|
444
|
+
weekdays_by_month = fy.weekdays_by_month( )
|
|
445
|
+
weekends_by_month = fy.weekends_by_month( )
|
|
446
|
+
workdays_by_month = fy.workdays_by_month( )
|
|
447
|
+
holidays_by_month = fy.holidays_by_month( )
|
|
448
|
+
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Federal Holidays
|
|
452
|
+
|
|
453
|
+
```python
|
|
454
|
+
|
|
455
|
+
holidays = FederalHoliday( 2026 )
|
|
456
|
+
|
|
457
|
+
holiday_map = holidays.holidays( )
|
|
458
|
+
independence_day = holiday_map[ "Independence Day" ]
|
|
459
|
+
|
|
460
|
+
print( independence_day[ "actual" ] )
|
|
461
|
+
print( independence_day[ "observed" ] )
|
|
462
|
+
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
```python
|
|
466
|
+
|
|
467
|
+
print(
|
|
468
|
+
holidays.is_holiday(
|
|
469
|
+
when=date( 2026, 7, 3 ),
|
|
470
|
+
observed=True,
|
|
471
|
+
)
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
print( holidays.is_weekend( date( 2026, 7, 4 ) ) )
|
|
475
|
+
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
<a id="api-overview"></a>
|
|
479
|
+
|
|
480
|
+

|
|
481
|
+
|
|
482
|
+
___
|
|
483
|
+
|
|
484
|
+
## 🧠 API Overview
|
|
485
|
+
|
|
486
|
+
```python
|
|
487
|
+
|
|
488
|
+
from fiscal import DB, Error, FederalHoliday, FiscalYear, FullTimeEquivalent, throw_if, to_date
|
|
489
|
+
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### `FiscalYear`
|
|
493
|
+
|
|
494
|
+
#### Construction
|
|
495
|
+
|
|
496
|
+
```python
|
|
497
|
+
FiscalYear(
|
|
498
|
+
fy: str | int,
|
|
499
|
+
bpoa: str | int = "",
|
|
500
|
+
epoa: str | int = "",
|
|
501
|
+
current_date: date | datetime | str | None = None,
|
|
502
|
+
)
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Ranges and Boundaries
|
|
506
|
+
|
|
507
|
+
- `calendar_day_of_year()`
|
|
508
|
+
- `calendar_days_in_year()`
|
|
509
|
+
- `calendar_days_elapsed()`
|
|
510
|
+
- `calendar_days_remaining()`
|
|
511
|
+
- `calendar_months_elapsed()`
|
|
512
|
+
- `calendar_months_remaining()`
|
|
513
|
+
- `calendar_percent_elapsed()`
|
|
514
|
+
- `calendar_bounds()`
|
|
515
|
+
- `calendar_week_number()`
|
|
516
|
+
- `calendar_month_name()`
|
|
517
|
+
- `current_weekday_name()`
|
|
518
|
+
- `fiscal_day_of_year()`
|
|
519
|
+
- `fiscal_days_in_year()`
|
|
520
|
+
- `fiscal_days_elapsed()`
|
|
521
|
+
- `fiscal_days_remaining()`
|
|
522
|
+
- `fiscal_months_elapsed()`
|
|
523
|
+
- `fiscal_months_remaining()`
|
|
524
|
+
- `fiscal_percent_elapsed()`
|
|
525
|
+
- `fiscal_bounds()`
|
|
526
|
+
- `compensable_hours_elapsed(hours_per_day=8)`
|
|
527
|
+
- `compensable_hours_remaining(hours_per_day=8)`
|
|
528
|
+
- `work_hours_elapsed(hours_per_day=8, use_observed=True)`
|
|
529
|
+
- `work_hours_remaining(hours_per_day=8, use_observed=True)`
|
|
530
|
+
|
|
531
|
+
### Fiscal Periods
|
|
532
|
+
|
|
533
|
+
- `fiscal_month_number()`
|
|
534
|
+
- `fiscal_month_bounds(fiscal_month)`
|
|
535
|
+
- `fiscal_days_in_month(fiscal_month)`
|
|
536
|
+
- `fiscal_month_name(fiscal_month)`
|
|
537
|
+
- `fiscal_month_calendar(fiscal_month)`
|
|
538
|
+
- `fiscal_month_weeks(fiscal_month)`
|
|
539
|
+
- `fiscal_month_dates(fiscal_month)`
|
|
540
|
+
- `fiscal_month_day_numbers(fiscal_month)`
|
|
541
|
+
- `fiscal_month_text_calendar(fiscal_month)`
|
|
542
|
+
- `fiscal_year_text_calendar()`
|
|
543
|
+
- `fiscal_month_html_calendar(fiscal_month, with_year=True)`
|
|
544
|
+
- `fiscal_year_html_calendar(width=3)`
|
|
545
|
+
- `fiscal_quarter_number()`
|
|
546
|
+
- `fiscal_quarter_bounds(quarter)`
|
|
547
|
+
- `fiscal_days_in_quarter(quarter)`
|
|
548
|
+
- `fiscal_week_number()`
|
|
549
|
+
- `fiscal_week_bounds(fiscal_week)`
|
|
550
|
+
- `weekday_occurrences(fiscal_month, weekday)`
|
|
551
|
+
|
|
552
|
+
#### Date and Holiday Analysis
|
|
553
|
+
|
|
554
|
+
- `count_weekends(start, end)`
|
|
555
|
+
- `count_holidays(start, end, use_observed=True)`
|
|
556
|
+
- `count_workdays(start, end, use_observed=True)`
|
|
557
|
+
- `compensable_hours_between(start, end, hours_per_day=8)`
|
|
558
|
+
- `work_hours_between(start, end, hours_per_day=8, use_observed=True)`
|
|
559
|
+
- `fte_between(start, end, hours_per_day=8)`
|
|
560
|
+
- `holiday_dates_between(start, end, use_observed=True)`
|
|
561
|
+
- `holidays_between(start, end, use_observed=True)`
|
|
562
|
+
- `fiscal_dates()`
|
|
563
|
+
- `fiscal_weekdays()`
|
|
564
|
+
- `fiscal_weekends()`
|
|
565
|
+
- `fiscal_workdays(use_observed=True)`
|
|
566
|
+
- `fiscal_calendar()`
|
|
567
|
+
- `dates_by_month()`
|
|
568
|
+
- `weekdays_by_month()`
|
|
569
|
+
- `weekends_by_month()`
|
|
570
|
+
- `workdays_by_month(use_observed=True)`
|
|
571
|
+
- `holidays_by_month(use_observed=True)`
|
|
572
|
+
- `holidays_remaining(use_observed=True)`
|
|
573
|
+
- `workdays_remaining(use_observed=True)`
|
|
574
|
+
- `weekends_remaining()`
|
|
575
|
+
- `contains_leap_day()`
|
|
576
|
+
- `leap_days_in_availability()`
|
|
577
|
+
- `to_dict()`
|
|
578
|
+
|
|
579
|
+
### `FederalHoliday`
|
|
580
|
+
|
|
581
|
+
- `observed_date(value)`
|
|
582
|
+
- `holidays()`
|
|
583
|
+
- `is_holiday(when, observed=True)`
|
|
584
|
+
- `is_weekend(when)`
|
|
585
|
+
- `to_dict()`
|
|
586
|
+
|
|
587
|
+
### Utilities
|
|
588
|
+
|
|
589
|
+
- `throw_if(name, value)`
|
|
590
|
+
- `to_date(value)`
|
|
591
|
+
- `to_decimal(name, value)`
|
|
592
|
+
|
|
593
|
+
`to_date()` accepts `date`, `datetime`, `YYYY-MM-DD`, `MM/DD/YYYY`, and `MM/DD/YY`. Database sentinel values resolve to `None`.
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
### Date-Range Calendars
|
|
597
|
+
|
|
598
|
+
Render every month intersecting an inclusive date range:
|
|
599
|
+
|
|
600
|
+
```python
|
|
601
|
+
text_calendar = fy.date_range_text_calendar(
|
|
602
|
+
start=date( 2025, 11, 15 ),
|
|
603
|
+
end=date( 2026, 2, 2 ),
|
|
604
|
+
)
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
```python
|
|
608
|
+
html_calendar = fy.date_range_html_calendar(
|
|
609
|
+
start=date( 2025, 11, 15 ),
|
|
610
|
+
end=date( 2026, 2, 2 ),
|
|
611
|
+
width=2,
|
|
612
|
+
with_year=True,
|
|
613
|
+
)
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
The methods render November 2025 through February 2026 in chronological order. The supplied range is inclusive and constrained to the represented fiscal year.
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
## 📚 References
|
|
620
|
+
|
|
621
|
+
- [Fiscal Year](https://github.com/is-leeroy-jenkins/Fiscal/blob/master/resources/Definitions.md#fiscal-year)
|
|
622
|
+
- [Federal Holidays](https://www.opm.gov/policy-data-oversight/pay-leave/federal-holidays/)
|
|
623
|
+
|
|
624
|
+
<a id="license"></a>
|
|
625
|
+
|
|
626
|
+
## 📜 [License](https://github.com/is-leeroy-jenkins/fiscal/blob/master/LICENSE.txt)
|
|
627
|
+
|
|
628
|
+
MIT © 2022 Terry D. Eppler
|