This document explains the internal architecture and design principles of Graphite.
- Overview
- Design Principles
- Class Structure
- Memory Management
- Performance Considerations
- Extension Points
Graphite follows a modular architecture inspired by Carbon PHP, designed for optimal performance and clean code organization in Godot 4.4. The library uses a facade pattern where the main Graphite class acts as an interface to specialized utility classes.
Each utility class handles one specific aspect of datetime operations:
- GraphiteMath: Mathematical operations (add/subtract with comprehensive time unit support)
- GraphiteFormatting: String formatting and output
- GraphiteManipulation: Date/time modification
- GraphiteComparison: Date comparison operations
- GraphiteQueries: Boolean queries about dates
- GraphiteModifiers: Start/end of period operations
- Static Methods: All utility methods are static to avoid object creation overhead
- RefCounted: Automatic memory management without manual
free()calls - Minimal State: Graphite instances only store datetime and timezone dictionaries
- No Deep Inheritance: Flat class hierarchy for better performance
Most methods return the Graphite instance itself, enabling method chaining:
var result = Graphite.now()
.startOfMonth()
.addDays(15)
.setTime(14, 30, 0)
.format("Y-m-d H:i:s")copy()andclone()methods provide safe immutability when needed- Most operations modify the instance in-place for performance
- Users can choose between mutable and immutable patterns
Graphite (main class)
├── GraphiteInterface (constants and enums)
├── GraphiteMath (math operations)
├── GraphiteFormatting (string formatting)
├── GraphiteManipulation (date/time modification)
├── GraphiteComparison (comparison operations)
├── GraphiteQueries (boolean queries)
├── GraphiteModifiers (start/end operations)
└── GraphiteUnix (unix timestamp utilities)
The Graphite class serves as a facade that:
- Stores datetime and timezone state
- Provides public API methods
- Delegates actual work to utility classes
- Maintains method chaining capability
class_name Graphite
extends RefCounted
var _datetime: Dictionary
var _timezone: Dictionary
func add(amount: int, unit: String = "seconds") -> Graphite:
return GraphiteMath.add(self, amount, unit)Each utility class follows this pattern:
- Extends
RefCounted(but instances are never created) - Contains only static methods
- First parameter is always the
Graphiteinstance - Returns either the
Graphiteinstance or a primitive type
class_name GraphiteMath
extends RefCounted
static func add(graphite: Graphite, amount: int, unit: String = "seconds") -> Graphite:
# Implementation here
return graphiteContains constants, enums, and configuration values:
- Day/month constants (MONDAY, JANUARY, etc.)
- Time conversion constants (HOURS_PER_DAY, etc.)
- Format constants (DEFAULT_TO_STRING_FORMAT, etc.)
- Diff and translation options
Graphite uses Godot's RefCounted system:
- Automatic Cleanup: No need to call
free()manually - Reference Counting: Memory is freed when no references exist
- Performance: Minimal overhead compared to manual memory management
- Creation:
Graphite.now()creates a new instance - Usage: Methods are called, state is modified
- Destruction: Automatic when instance goes out of scope
# Short-lived usage - automatic cleanup
func get_formatted_date() -> String:
return Graphite.now().format("Y-m-d") # Graphite instance freed automatically
# Long-lived usage - stored as member
class_name MyClass
var creation_time: Graphite
func _init():
creation_time = Graphite.now() # Freed when MyClass instance is freed- No Object Creation: Utility classes are never instantiated
- CPU Cache Friendly: Static methods have better cache locality
- Memory Efficient: No instance data overhead
- Direct Function Calls: No virtual method lookup
# Fast - static method call
GraphiteMath.add(graphite, 5, "minutes")
# Slower - would require object creation (not used in Graphite)
var math_helper = MathHelper.new()
math_helper.add(graphite, 5, "minutes")Graphite leverages Godot's built-in Time class:
Time.get_datetime_dict_from_system()Time.get_unix_time_from_datetime_dict()Time.get_datetime_dict_from_unix_time()
This ensures compatibility and optimal performance within the Godot ecosystem.
To add new functionality:
- Create a new utility class:
class_name GraphiteCustom
extends RefCounted
static func custom_operation(graphite: Graphite, param: String) -> Graphite:
# Implementation
return graphite- Add method to main Graphite class:
func customOperation(param: String) -> Graphite:
return GraphiteCustom.custom_operation(self, param)Add new format codes to GraphiteFormatting.format():
static func format(graphite: Graphite, format_string: String) -> String:
var result = format_string
# ... existing format codes ...
# Add new format code
result = result.replace("X", custom_format_logic(datetime))
return resultAdd domain-specific queries to GraphiteQueries:
static func isBusinessHour(graphite: Graphite) -> bool:
var hour = graphite.get_datetime().hour
return graphite.isWeekday() and hour >= 9 and hour < 17- Graceful Degradation: Invalid inputs print errors but don't crash
- Method Chaining Preservation: Errors return the unchanged instance
- Developer Feedback: Clear error messages via
push_error()
static func add(graphite: Graphite, amount: int, unit: String = "seconds") -> Graphite:
match unit:
"seconds", "minutes", "hours", "days":
# Valid operations
pass
_:
push_error("Invalid unit: " + unit)
return graphite # Return unchanged- Graphite instances are not thread-safe
- Static utility methods are stateless and thread-safe
- Godot's
Timeclass is thread-safe
- Don't share Graphite instances between threads
- Create separate instances for each thread
- Use
copy()when passing between threads
# Thread-safe pattern
func thread_worker(datetime_dict: Dictionary):
var local_graphite = Graphite.from_dict(datetime_dict, {})
# Work with local_graphite safelyThe architecture supports easy testing:
- Static methods can be tested independently
- Pure functions with predictable outputs
- Minimal dependencies (only Godot's Time class)
# Easy to test specific utility functions
func test_add_minutes():
var test_date = Graphite.from_dict(test_datetime, {})
var result = GraphiteMath.add(test_date, 30, "minutes")
assert(result.get_datetime().minute == expected_minute)- Timezone Support: Full timezone conversion implementation
- Localization: Multi-language date formatting
- Parser: String-to-date parsing functionality
- Intervals: Duration and period calculations
- Immutable Mode: Optional immutable operation mode
The modular architecture ensures that:
- New utility classes can be added without breaking existing code
- Method signatures remain stable
- Performance improvements can be made internally