myanmar_calendar_dart 2.0.1 copy "myanmar_calendar_dart: ^2.0.1" to clipboard
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 #

ci pub package License: MIT

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-DD target 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);
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:

  • &y Myanmar year
  • &M Myanmar month name
  • &P moon phase
  • &ff fortnight day (zero padded)
  • &W weekday name
  • &N year name cycle
  • &Nay localized day word (Nay)
  • &Yat localized 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/cacheVersion values.
  • Western holiday provider cache keys use westernHolidayProvider.cacheKey.
  • If your workload is mostly one-off conversions, disabling cache is reasonable.

Localization #

Supported languages:

  • Language.english
  • Language.myanmar
  • Language.zawgyi
  • Language.mon
  • Language.shan
  • Language.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.

0
likes
160
points
89
downloads

Publisher

verified publisherkyawzayartun.com

Weekly Downloads

A comprehensive dart package for Myanmar calendar with date conversions, astrological calculations, and multi-language support

Repository (GitHub)
View/report issues

Topics

#calendar #myanmar-calendar #myanmar-datetime #utility #date-converter

Documentation

API reference

License

MIT (license)

Dependencies

collection, meta

More

Packages that depend on myanmar_calendar_dart