Homoiconicity-Inspired Date/Time Handling in C#

Homoiconicity: Code as Data, Simplifying Date and Time in ERP Systems

Masoud Bahrami
5 min readFeb 11, 2025

Homoiconicity is a fascinating concept in programming languages where a program’s code and its data share the same structure. This means you can manipulate code as if it were data, and vice versa. It’s a powerful idea that enables metaprogramming, domain-specific languages (DSLs), and incredible flexibility.

While languages like C# and Java aren’t inherently homoiconic, their principles can be emulated to bring these benefits to the often-complex world of date and time manipulation, especially within ERP systems.

I developed a simple library in dotnet that makes world a better place for dotnet developers working with date and time. I’ll share it in the next article!

Understanding the Core Idea

Imagine a language where (+ 2 3) isn't just an instruction to add two numbers, but also a list: [+, 2, 3]. This is the essence of homoiconicity. The code itself is represented as a standard data structure within the language. This duality is key. Because code is data, you can write code that manipulates other code.

Clojure❤️: The Classic Example (originated from LISP)

Clojure (a dialect of LISP) is a poster child for homoiconicity. In Lisp, code is written as S-expressions, which are lists. So, (+ 2 3) is both code and a list. This means you can write Clojure code that manipulates other Clojure code. This is how Clojure macros work – they're essentially code transformations that happen at compile time.

Emulating Homoiconicity in C#

C# doesn’t have built-in homoiconicity. However, clever library design can emulate some of the benefits by representing domain-specific concepts as data that can be manipulated programmatically. This approach allows for more expressive and intuitive APIs.

The Challenge: Date/Time in ERPs

In ERP systems, we often work with dates at different granularities: fiscal years, quarters, months, weeks, etc. For example, in accounting, we deal with fiscal years (which may not align with the calendar year), or in payroll, calculations are based on weekly or bi-weekly periods. While we might conceptually say “fiscal year 2024” or “week 45 of 2023”, representing these in C# with DateTime or DateOnly can be cumbersome and error-prone. Standard libraries often fall short, forcing developers to write complex, custom logic for tasks like calculating the end of a fiscal year or determining the next bi-weekly payday.

Bringing Homoiconicity to Dates in C#

Date/Time in ERPs

C# doesn’t have built-in homoiconicity. However, by cleverly representing date and time concepts as data, we can emulate its benefits and significantly simplify date manipulation.

The Challenge: Date/Time Frustrations

Imagine you’re a C# or Java developer building a payroll or an accounting system. You quickly encounter the limitations of standard date/time classes like DateTime and DateOnly.

Scenario 1: Payroll Calculations

You need to calculate bi-weekly or monthly pay periods. Employees get paid every other Friday. With the standard classes, this becomes a mess. You have to manually add 14 days (AddDays(14) or AddDays(-14)!), but then you need to make sure you haven’t accidentally skipped a Friday. What if the payroll date falls on a non-Friday? You have to write logic to adjust it. It gets complicated quickly.

Scenario 2: Fiscal Year Reporting

Consider an accounting system. You need to generate reports by fiscal quarter. The fiscal year might start in April, not January. So, how do you determine which quarter a given transaction belongs to? You have to write code to handle the non-standard fiscal year alignment. It’s another layer of complexity.

Scenario 3: Weekly Reporting

Imagine you’re tasked with a weekly reporting scenario, where you generate and show reports for each of the 52 weeks of a year. In this case, you’re dealing with a date scale containing year and week, and the built-in C# or Java date/time solutions don’t work as expected.

These are real-world problems that developers face every day, and the standard date/time libraries often fall short, forcing them to write a lot of custom, error-prone code.

A Homoiconic Approach to Date/Time

My approach offers a clever workaround. It lets you represent dates as strings that also have meaning. This is where the magic happens.

Instead of just using DateTime or DateOnly objects, you can use strings like "2024", "2024-1", or "2024-W52". These strings aren't just text; they represent specific dates or date ranges and can be used directly with functions designed to understand their structure. This mirrors the homoiconic principle of code-as-data, allowing us to treat date/time concepts as data that can be directly manipulated.

A More Intuitive Date/Time Approach (Examples)

A domain-specific representation of date/time concepts can significantly simplify these tasks. Instead of manipulating DateTime objects directly, we can work with representations that are closer to how we think about dates in these specific domains.

Here are some concrete examples:

  1. Payroll (Weekly/Bi-weekly):

Instead of:

C#

DateTime payrollDate = new DateTime(2024, 1, 12); // A Friday
// Tedious calculations to get the next bi-weekly payday...
DateTime nextPayday = payrollDate.AddDays(14);
// What if it's not a Friday? Need more logic!

We can write:

C#

string payrollWeek = "2024-W02"; // Week 2 of 2024 (assuming ISO 8601 week numbering)
string nextPayday = payrollWeek.Next(2); // Directly gets the next bi-weekly payday (e.g., "2024-W04").
int payPeriods = payrollWeek.Diff("2024-W06"); // Directly gets the number of bi-weekly periods.
  • Fiscal Year (Starts in April):

Instead of:

C#

DateTime transactionDate = new DateTime(2024, 5, 10);
// Complex logic to determine the fiscal year...
int fiscalYear;
if (transactionDate.Month >= 4) {
fiscalYear = transactionDate.Year;
} else {
fiscalYear = transactionDate.Year - 1;
}

We can write:

C#

string fiscalYear = "2024"; // Fiscal year starting in 2024 (April - March)
string fiscalYearEnd = fiscalYear.End(); // Directly gets the end date (e.g., "2025-03-31").
string nextFiscalYear = fiscalYear.Next(); // Directly gets the next fiscal year ("2025").
bool isWithinFiscalYear = "2024-05-10".IsIn(fiscalYear); // Directly checks if the date is within the fiscal year.
  • Reporting Period (Monthly):

Instead of:

C#

DateTime reportDate = new DateTime(2024, 7, 15);
// Complex logic to determine the reporting month and its start/end dates.

We can write:

C#

string reportingMonth = "2024-07"; // July 2024
string reportingMonthEnd = reportingMonth.End(); // Directly gets the end date of the month (e.g., "2024-07-31").
string nextReportingMonth = reportingMonth.Next(); // Directly gets the next reporting month ("2024-08").
bool isWithinReportingMonth = "2024-07-15".IsIn(reportingMonth); // Directly checks if the date is within the month.

This approach simplifies date/time manipulation in ERP systems, making code more readable, less error-prone and more efficient.

The examples demonstrate how emulating aspects of homoiconicity can lead to more elegant and effective solutions for complex problems, even in languages that don’t natively support it. It bridges the gap between how we think about dates in ERP systems and how we represent them in code.

Resources

https://clojure.org/

--

--

Masoud Bahrami
Masoud Bahrami

Written by Masoud Bahrami

DDD teacher and practitioner, software engineer, architect, and modeler. Specialized in building autonomous teams and services.

No responses yet