Last active
March 17, 2025 07:16
-
-
Save mmynk/493f74451596d78546bfa7d98a3bed03 to your computer and use it in GitHub Desktop.
Substantial Presence Test Calculator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# This script calculates the substantial presence test for US tax purposes. | |
# Example usage: ./residency_calc.py "2025-02-02,2025-03-03;2024-04-04,2024-05-05;2023-06-06,2023-07-07" | |
import argparse | |
from dataclasses import dataclass | |
from datetime import datetime | |
@dataclass | |
class TravelDetails: | |
entry: datetime | |
exit: datetime | |
def calculate_substantial_presence(travel_details: list[TravelDetails]) -> bool: | |
current_year = datetime.now().year | |
days_current_year = datetime.now().timetuple().tm_yday | |
days_last_year = 365 | |
days_second_last_year = 365 | |
for travel in travel_details: | |
if travel.exit.year == current_year: | |
exit_day = travel.exit if travel.exit < datetime.now() else datetime.now() | |
if travel.entry.year == current_year: | |
days_current_year_out = (exit_day - travel.entry).days | |
else: | |
days_current_year_out = (exit_day - datetime(current_year, 1, 1)).days | |
days_current_year -= (days_current_year_out - 1) | |
elif travel.exit.year == current_year - 1: | |
if travel.entry.year == current_year - 1: | |
days_last_year_out = (travel.exit - travel.entry).days | |
else: | |
days_last_year_out = (travel.exit - datetime(current_year - 1, 1 | |
, 1)).days | |
days_last_year -= (days_last_year_out - 1) | |
elif travel.exit.year == current_year - 2: | |
if travel.entry.year == current_year - 2: | |
days_second_last_year_out = (travel.exit - travel.entry).days | |
else: | |
days_second_last_year_out = (travel.exit - datetime(current_year - 2, 1 | |
, 1)).days | |
days_second_last_year -= (days_second_last_year_out - 1) | |
print(f'Days current year ({current_year}): {days_current_year}') | |
print(f'Days last year ({current_year - 1}): {days_last_year}') | |
print(f'Days year before last ({current_year - 2}): {days_second_last_year}') | |
return days_current_year >= 31 and days_current_year + (days_last_year // 3) + (days_second_last_year // 6) >= 183 | |
def main(): | |
parser = argparse.ArgumentParser(description='Calculate substantial presence test') | |
parser.add_argument('entries', type=str, help='List of entries and exits in the format "YYYY-MM-DD,YYYY-MM-DD;YYYY-MM-DD,YYYY-MM-DD"') | |
args = parser.parse_args() | |
travel_details = [] | |
for entry_exit in args.entries.split(';'): | |
entry, exit = entry_exit.split(',') | |
travel_details.append(TravelDetails(datetime.strptime(entry, '%Y-%m-%d'), datetime.strptime(exit, '%Y-%m-%d'))) | |
is_resident = calculate_substantial_presence(travel_details) | |
print('Resident' if is_resident else 'Non-resident') | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment