Last active
October 21, 2025 03:07
-
-
Save paulate/cf7289a212851bde8bebf2a43134e8b0 to your computer and use it in GitHub Desktop.
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
| # For "How does this Balinese calendar work?" Dinachronicle 4 @ Dinacon 2025 | |
| # Below is a lil python script to calculate your Pawukon birthday from your Gregorian birthday. | |
| # Just run: python pawukon.py yyyy-mm-dd | |
| # If you announce your birthday to the locals and it’s clearly an unlucky day, they may change | |
| # your day to a more auspicious one! | |
| from datetime import datetime, date | |
| class PawukonConverter: | |
| def __init__(self): | |
| # Define the cycles | |
| self.saptawara = ["Redite", "Soma", "Anggara", "Buda", "Wraspati", "Sukra", "Saniscara"] | |
| self.pancawara = ["Paing", "Pon", "Wage", "Keliwon", "Umanis"] | |
| self.wuku = [ | |
| "Sinta", "Landep", "Ukir", "Kulantir", "Tolu", "Gumbreg", "Wariga", "Warigadean", | |
| "Julungwangi", "Sungsang", "Dungulan", "Kuningan", "Langkir", "Medangsia", "Pujut", | |
| "Pahang", "Krulut", "Mrakih", "Tambir", "Madangkungan", "Matal", "Uye", "Manail", | |
| "Perangbakat", "Bala", "Ugu", "Wayang", "Klawu", "Dukut", "Watugunung" | |
| ] | |
| # Reference date: July 1, 2025 = Anggara Wage Matal | |
| self.reference_date = date(2025, 7, 1) | |
| self.reference_saptawara = "Anggara" # index 2 | |
| self.reference_pancawara = "Wage" # index 2 | |
| self.reference_wuku = "Matal" # index 20 | |
| # Get reference indices | |
| self.ref_saptawara_idx = self.saptawara.index(self.reference_saptawara) | |
| self.ref_pancawara_idx = self.pancawara.index(self.reference_pancawara) | |
| self.ref_wuku_idx = self.wuku.index(self.reference_wuku) | |
| def convert_date(self, input_date): | |
| """ | |
| Convert a Gregorian date to Pawukon system | |
| Args: | |
| input_date: datetime.date object or string in format 'YYYY-MM-DD' | |
| Returns: | |
| tuple: (saptawara, pancawara, wuku) | |
| """ | |
| if isinstance(input_date, str): | |
| input_date = datetime.strptime(input_date, '%Y-%m-%d').date() | |
| # Calculate days difference from reference date | |
| days_diff = (input_date - self.reference_date).days | |
| # Calculate indices for each cycle | |
| saptawara_idx = (self.ref_saptawara_idx + days_diff) % len(self.saptawara) | |
| pancawara_idx = (self.ref_pancawara_idx + days_diff) % len(self.pancawara) | |
| # Wuku changes every 7 days (it's a week-based cycle) | |
| weeks_diff = days_diff // 7 | |
| wuku_idx = (self.ref_wuku_idx + weeks_diff) % len(self.wuku) | |
| return ( | |
| self.saptawara[saptawara_idx], | |
| self.pancawara[pancawara_idx], | |
| self.wuku[wuku_idx] | |
| ) | |
| def get_wuku_week_info(self, wuku_name): | |
| """ | |
| Get additional information about the wuku | |
| Args: | |
| wuku_name: string name of the wuku | |
| Returns: | |
| dict: information about the wuku | |
| """ | |
| wuku_idx = self.wuku.index(wuku_name) | |
| return { | |
| 'name': wuku_name, | |
| 'number': wuku_idx + 1, | |
| 'position': f"Week {wuku_idx + 1} of 30" | |
| } | |
| def format_result(self, gregorian_date, saptawara, pancawara, wuku, compact=False): | |
| """ | |
| Format the conversion result nicely | |
| Args: | |
| gregorian_date: input date | |
| saptawara: saptawara result | |
| pancawara: pancawara result | |
| wuku: wuku result | |
| compact: if True, return compact single-line format | |
| """ | |
| if compact: | |
| return f"{gregorian_date} → {saptawara} {pancawara} {wuku}" | |
| wuku_info = self.get_wuku_week_info(wuku) | |
| result = f""" | |
| Gregorian Date: {gregorian_date} | |
| Pawukon Date: {saptawara} {pancawara} {wuku} | |
| Details: | |
| - Saptawara (7-day cycle): {saptawara} | |
| - Pancawara (5-day cycle): {pancawara} | |
| - Wuku (30-week cycle): {wuku} ({wuku_info['position']}) | |
| """ | |
| return result.strip() | |
| # Example usage and testing | |
| def main(): | |
| converter = PawukonConverter() | |
| # Test with the reference date | |
| print("=== Testing Reference Date ===") | |
| ref_result = converter.convert_date(date(2025, 7, 1)) | |
| print(converter.format_result(date(2025, 7, 1), *ref_result)) | |
| print("\n=== Testing Other Dates ===") | |
| # Test with some other dates | |
| test_dates = [ | |
| "2025-07-02", # Next day | |
| "2025-07-08", # One week later | |
| "2025-06-30", # Day before | |
| "2025-08-01", # One month later | |
| "2024-07-01", # One year earlier | |
| "2026-07-01" # One year later | |
| ] | |
| for test_date in test_dates: | |
| result = converter.convert_date(test_date) | |
| print(converter.format_result(test_date, *result)) | |
| print() | |
| # Interactive function | |
| def interactive_converter(): | |
| """ | |
| Interactive date converter | |
| """ | |
| converter = PawukonConverter() | |
| print("=== Pawukon Date Converter ===") | |
| print("Enter dates in YYYY-MM-DD format (or 'quit' to exit)") | |
| while True: | |
| user_input = input("\nEnter Gregorian date: ").strip() | |
| if user_input.lower() == 'quit': | |
| break | |
| try: | |
| result = converter.convert_date(user_input) | |
| print(converter.format_result(user_input, *result)) | |
| except ValueError as e: | |
| print(f"Error: Invalid date format. Please use YYYY-MM-DD format.") | |
| except Exception as e: | |
| print(f"Error: {e}") | |
| if __name__ == "__main__": | |
| import sys | |
| if len(sys.argv) == 1: | |
| # No arguments - run interactive mode | |
| interactive_converter() | |
| elif len(sys.argv) == 2: | |
| # One argument - convert single date | |
| try: | |
| converter = PawukonConverter() | |
| input_date = sys.argv[1] | |
| result = converter.convert_date(input_date) | |
| print(converter.format_result(input_date, *result)) | |
| except ValueError: | |
| print("Error: Invalid date format. Please use YYYY-MM-DD format.") | |
| sys.exit(1) | |
| except Exception as e: | |
| print(f"Error: {e}") | |
| sys.exit(1) | |
| elif len(sys.argv) > 2: | |
| # Multiple arguments - convert multiple dates | |
| converter = PawukonConverter() | |
| for date_str in sys.argv[1:]: | |
| try: | |
| result = converter.convert_date(date_str) | |
| print(converter.format_result(date_str, *result)) | |
| print() # Empty line between results | |
| except ValueError: | |
| print(f"Error: Invalid date format '{date_str}'. Please use YYYY-MM-DD format.") | |
| except Exception as e: | |
| print(f"Error converting '{date_str}': {e}") | |
| else: | |
| print("Usage:") | |
| print(" python pawukon.py # Interactive mode") | |
| print(" python pawukon.py 2025-01-01 # Convert single date") | |
| print(" python pawukon.py 2025-01-01 2025-01-02 # Convert multiple dates") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment