myanmar_calendar_dart 2.0.1
myanmar_calendar_dart: ^2.0.1 copied to clipboard
A comprehensive dart package for Myanmar calendar with date conversions, astrological calculations, and multi-language support
Myanmar Calendar Dart #
A pure Dart package for Myanmar calendar calculation, conversion, formatting, holiday rules, and astrological information.
Migration #
Upgrading from v1? See the migration guide: MIGRATION.md
Installation #
dart pub add myanmar_calendar_dart
Quick Start #
import 'package:myanmar_calendar_dart/myanmar_calendar_dart.dart';
void main() {
MyanmarCalendar.configure(
language: Language.english,
timezoneOffset: 6.5,
);
final today = MyanmarCalendar.today();
print(today.formatComplete(includeAstro: true, includeHolidays: true));
final converted = MyanmarCalendar.fromWestern(2024, 1, 1);
print(converted.formatMyanmar('&y &M &P &ff'));
}
Example App #
Run the built-in CLI demo app:
dart run example/lib/main.dart
With options:
dart run example/lib/main.dart \
--date=2024-01-04 \
--language=en \
--timezone=6.5 \
--cache=high \
--chronicle=true
Options:
--date=YYYY-MM-DDtarget Western date--language=en|my|zawgyi|mon|shan|karen--timezone=<offset>range-12..14--cache=default|high|memory|off--chronicle=true|false
What You Get #
- Myanmar <-> Western date conversion
- Complete date object (Myanmar, Western, Shan, holidays, astro)
- Myanmar and Western date formatting with localization
- Built-in holidays with flexible disabling rules
- Custom holiday rules (typed matcher + date-rule helpers)
- Pluggable western holiday provider (Eid/Diwali/Chinese New Year)
- Chronicle and dynasty lookup APIs
- Cache controls for throughput and memory tuning
Core API #
Main entry point #
final date = MyanmarCalendar.today();
final fromWestern = MyanmarCalendar.fromWestern(2024, 4, 17);
final fromMyanmar = MyanmarCalendar.fromMyanmar(1385, 1, 1);
final complete = MyanmarCalendar.getCompleteDate(DateTime.now());
final astro = MyanmarCalendar.getAstroInfo(date.myanmarDate);
final holidays = MyanmarCalendar.getHolidayInfo(date.myanmarDate);
Instance-first client (recommended for isolation) #
final client = MyanmarCalendar.createClient(
config: const CalendarConfig(
defaultLanguage: 'en',
timezoneOffset: 6.5,
),
cacheConfig: const CacheConfig.memoryEfficient(),
);
final complete = client.getCompleteDate(DateTime(2024, 1, 4));
final yearInfo = client.getMyanmarYearInfo(1385); // MyanmarYearInfo (typed)
print(yearInfo.yearType);
Formatting #
final myanmarText = date.formatMyanmar('&y &M &P &ff');
final westernText = date.formatWestern('%yyyy-%mm-%dd');
final localized = MyanmarCalendar.format(
date,
language: Language.myanmar,
);
Myanmar format tokens #
Common tokens:
&yMyanmar year&MMyanmar month name&Pmoon phase&fffortnight day (zero padded)&Wweekday name&Nyear name cycle&Naylocalized day word (Nay)&Yatlocalized date-day word (Yat)
final withYat = date.formatMyanmar('&d &Yat', Language.myanmar);
final withNay = date.formatMyanmar('&d &Nay', Language.myanmar);
Validation and parsing #
final valid = MyanmarCalendar.isValidMyanmar(1385, 10, 1);
final result = MyanmarCalendar.validateMyanmar(1385, 10, 1);
final parsedMyanmar = MyanmarCalendar.parseMyanmar('1385/10/1');
final parsedWestern = MyanmarCalendar.parseWestern('2024-01-01');
Holiday Customization #
Add custom holidays #
final myHoliday = CustomHoliday.westernDate(
id: 'team_day',
name: 'Team Day',
type: HolidayType.cultural,
month: 7,
day: 27,
cacheVersion: 1,
);
MyanmarCalendar.configure(customHolidayRules: [myHoliday]);
When you change predicate logic but keep the same holiday ID, increment
cacheVersion so cached holiday results are invalidated deterministically.
You can also write a typed matcher directly:
final fullMoonFestival = CustomHoliday(
id: 'full_moon_festival',
name: 'Full Moon Festival',
type: HolidayType.cultural,
cacheVersion: 1,
localizedNames: {
Language.myanmar: 'လပြည့်ပွဲ',
},
matcher: (context) {
return context.myanmarDate.moonPhase == 1 && context.myanmarDate.day == 15;
},
);
Legacy untyped holiday predicates are removed. Use matcher: (context) { ... }.
Removed legacy aliases:
CustomHoliday.legacy(...)MyanmarCalendar.addCustomHoliday(...)MyanmarCalendar.addCustomHolidays(...)MyanmarCalendar.removeCustomHoliday(...)MyanmarCalendar.clearCustomHolidays(...)
Disable built-in holidays #
MyanmarCalendar.configure(
disabledHolidays: [HolidayId.independenceDay],
disabledHolidaysByYear: {
2026: [HolidayId.newYearDay],
},
disabledHolidaysByDate: {
'2026-04-17': [HolidayId.myanmarNewYearDay],
},
);
Override western-calendar holiday lookup #
Use this when you need custom or more accurate date tables for holidays like Eid, Diwali, or Chinese New Year.
const provider = TableWesternHolidayProvider(
singleDayRules: {
HolidayId.diwali: {
2045: WesternHolidayDate(month: 11, day: 2),
},
},
multiDayRules: {
HolidayId.eidAlFitr: {
2045: [
WesternHolidayDate(month: 1, day: 1),
WesternHolidayDate(month: 1, day: 2),
],
},
},
);
MyanmarCalendar.configure(westernHolidayProvider: provider);
Use an empty provider to disable built-in table-based western holiday matches:
MyanmarCalendar.configure(
westernHolidayProvider: const TableWesternHolidayProvider(),
);
For custom provider classes, override cacheKey with a stable value that
changes when the provider's rule table changes:
class MyHolidayProvider extends WesternHolidayProvider {
const MyHolidayProvider({required this.version});
final int version;
@override
String get cacheKey => 'my_holiday_provider:v$version';
@override
bool matches(HolidayId holidayId, int year, int month, int day) {
// Implement your lookup logic
return false;
}
}
Remote Configuration (Firebase or Backend) #
You can deliver custom holiday rules dynamically from Firebase Remote Config (Flutter apps) or any backend API (pure Dart/server apps).
Suggested JSON payload:
{
"holidayRules": [
{
"id": "team_day",
"name": "Team Day",
"type": "cultural",
"kind": "western_fixed",
"month": 7,
"day": 27,
"cacheVersion": 1,
"localizedNames": {
"my": "အသင်းနေ့"
}
}
]
}
Map remote payload to CustomHoliday rules:
import 'dart:convert';
import 'package:myanmar_calendar_dart/myanmar_calendar_dart.dart';
HolidayType _holidayTypeFromString(String value) {
return HolidayType.values.firstWhere(
(type) => type.name == value,
orElse: () => HolidayType.other,
);
}
Map<Language, String> _localizedNames(dynamic raw) {
if (raw is! Map<String, dynamic>) return const {};
final result = <Language, String>{};
for (final entry in raw.entries) {
result[Language.fromCode(entry.key)] = entry.value as String;
}
return result;
}
List<CustomHoliday> parseHolidayRules(Map<String, dynamic> payload) {
final rules = payload['holidayRules'] as List<dynamic>? ?? const [];
return rules.map((raw) {
final item = raw as Map<String, dynamic>;
final id = item['id'] as String;
final name = item['name'] as String;
final type = _holidayTypeFromString(item['type'] as String? ?? 'other');
final cacheVersion = item['cacheVersion'] as int? ?? 1;
final localized = _localizedNames(item['localizedNames']);
final kind = item['kind'] as String? ?? 'western_fixed';
if (kind == 'myanmar_fixed') {
return CustomHoliday.myanmarDate(
id: id,
name: name,
type: type,
month: item['month'] as int,
day: item['day'] as int,
year: item['year'] as int?,
fromYear: item['fromYear'] as int?,
toYear: item['toYear'] as int?,
cacheVersion: cacheVersion,
localizedNames: localized,
);
}
return CustomHoliday.westernDate(
id: id,
name: name,
type: type,
month: item['month'] as int,
day: item['day'] as int,
year: item['year'] as int?,
fromYear: item['fromYear'] as int?,
toYear: item['toYear'] as int?,
cacheVersion: cacheVersion,
localizedNames: localized,
);
}).toList();
}
Apply rules from Firebase Remote Config (Flutter app):
final jsonString = remoteConfig.getString('myanmar_calendar_rules');
final payload = jsonDecode(jsonString) as Map<String, dynamic>;
final rules = parseHolidayRules(payload);
MyanmarCalendar.configure(customHolidayRules: rules);
Apply rules from a custom backend:
final response = await client.get(Uri.parse('https://api.example.com/calendar/rules'));
final payload = jsonDecode(response.body) as Map<String, dynamic>;
final rules = parseHolidayRules(payload);
MyanmarCalendar.configure(customHolidayRules: rules);
When remote rule logic changes, increment cacheVersion for each changed rule.
Caching #
Caching is enabled by default to improve repeated conversion and lookup paths.
Configure cache profile #
// Default profile
MyanmarCalendar.configureCache(const CacheConfig());
// Larger cache for heavy throughput
MyanmarCalendar.configureCache(const CacheConfig.highPerformance());
// Smaller cache with TTL
MyanmarCalendar.configureCache(const CacheConfig.memoryEfficient());
// Disable cache completely
MyanmarCalendar.configureCache(const CacheConfig.disabled());
Cache operations #
MyanmarCalendar.clearCache();
MyanmarCalendar.resetCacheStatistics();
final stats = MyanmarCalendar.getCacheStatistics();
print(stats);
final typedStats = MyanmarCalendar.getTypedCacheStatistics();
print(typedStats.hitRate);
Notes #
- Cache entries are isolated by configuration fingerprint to avoid cross-config collisions.
- Holiday and complete-date cache entries are language-aware.
- Custom holiday cache keys are based on stable
cacheKey/cacheVersionvalues. - Western holiday provider cache keys use
westernHolidayProvider.cacheKey. - If your workload is mostly one-off conversions, disabling cache is reasonable.
Localization #
Supported languages:
Language.englishLanguage.myanmarLanguage.zawgyiLanguage.monLanguage.shanLanguage.karen
MyanmarCalendar.setLanguage(Language.myanmar);
final mm = MyanmarCalendar.getCompleteDate(
DateTime(2024, 1, 4),
language: Language.myanmar,
);
final en = MyanmarCalendar.getCompleteDate(
DateTime(2024, 1, 4),
language: Language.english,
);
setLanguage updates the default language. For per-request localization, pass
language: directly to APIs that support it.
Chronicle APIs #
final entries = MyanmarCalendar.getChronicleFor(DateTime(1752, 9, 14));
final dynasties = MyanmarCalendar.listDynasties();
final dynasty = MyanmarCalendar.getDynastyById('konbaung');
Error Handling #
try {
MyanmarCalendar.fromWestern(2024, 2, 30); // invalid date
} on ArgumentError catch (e) {
print(e);
}
Development #
dart pub get
dart format --output=none --set-exit-if-changed .
dart analyze
dart test
Reference parity fixtures #
The package includes golden fixture tests that compare Dart outputs with
reference/ceMmDateTime.js.
Regenerate fixtures when algorithm behavior changes:
node tool/generate_reference_parity_fixtures.mjs
dart test test/parity_reference_test.dart
Acknowledgements #
The core calculation algorithms are based on the original work by Dr Yan Naing Aye.
License #
MIT License. See LICENSE.