mobile_date_tz 0.1.2 copy "mobile_date_tz: ^0.1.2" to clipboard
mobile_date_tz: ^0.1.2 copied to clipboard

Port of the DateTz timezone utility for Flutter and Dart.

Mobile Date TZ ⏰⚡️ #

mobile_date_tz is the Dart/Flutter edition of the original DateTz Swiss‑army knife. It keeps minute-precision timestamps rock-solid across IANA zones, slices through daylight-saving transitions, and ships an expressive formatting DSL—all without dragging in heavyweight dependencies.

Think of it as your time-travel sidekick: whether you’re building scheduling flows, dashboards, automations, or mobile reminders, it keeps every cross-zone hop perfectly aligned.


Feature Highlights #

  • DST precision without drama – Static offsets plus timezone package integration give you correct answers even when the host OS doesn’t ship TZDB files.
  • Minute-accurate arithmetic – Internal timestamps are truncated to the minute so scheduled jobs never drift due to stray seconds.
  • Expressive format tokens – Familiar patterns (YYYY, hh, tz, LM, …) and literal escaping make string building painless.
  • Mutability by choiceconvertToTimezone, cloneToTimezone, add, set, and comparisons mirror the TypeScript API for effortless migration.
  • Production-ready pipeline – GitHub Actions workflow auto-bumps versions, runs tests, and publishes to pub.flutter-io.cn when you merge to master.
  • Zero runtime ballast – Just Dart code. No mirrors, no build_runner, no platform channels.

Getting Started #

Install from pub.flutter-io.cn:

dart pub add mobile_date_tz

Or add manually to pubspec.yaml:

dependencies:
  mobile_date_tz: ^0.1.0

Run dart pub get and you’re in business.

Tip: Run DateTz.initializeTimezones() during boot so the timezone database is ready before you render anything. If you skip it, the library lazily initialises the first time it needs dynamic rules.


Quick Start #

import 'package:mobile_date_tz/mobile_date_tz.dart';

void main() {
  // Optional but recommended.
  DateTz.initializeTimezones();

  final rome = DateTz.now('Europe/Rome');
  final nyc = rome.cloneToTimezone('America/New_York');

  print(rome.format());                        // 2025-06-15 09:30:00
  print(nyc.format('YYYY-MM-DD HH:mm tz'));    // 2025-06-15 03:30 America/New_York

  final parsed = DateTz.parse(
    '2025-06-15 09:30',
    'YYYY-MM-DD HH:mm',
    'UTC',
  );

  final handoff = parsed.cloneToTimezone('Asia/Tokyo')
    ..add(1, 'day')
    ..set(11, 'hour')
    ..convertToTimezone('Europe/Rome');

  print(handoff.format('DD LM YYYY HH:mm', 'it')); // 17 Giugno 2025 11:00
}

Formatting Cheatsheet #

Token Meaning Example
YYYY, yyyy Four-digit year 2025
YY, yy Two-digit year 25
MM Month (01–12) 06
LM Locale month name (capitalised, falls back to English) June
DD Day of month (01–31) 15
HH Hour (00–23) 09
hh Hour (01–12) 03
mm Minute (00–59) 30
ss Second (00–59) 00
aa Lowercase am/pm pm
AA Uppercase AM/PM PM
tz Timezone identifier Europe/Rome

Escape literal text by wrapping it in []: YYYY-MM-DD[ @ ]HH:mm2025-06-15 @ 09:30.


API Tour #

Constructors #

final utcNow = DateTz.now();                                   // defaults to UTC
final timestamp = DateTz(1700000000000, 'Europe/Rome');        // from epoch ms
final clone = DateTz(utcNow);                                  // copy constructor
final mapInput = DateTz({'timestamp': 1700000000000});         // Map-based

Mutation & Cloning #

final meeting = DateTz.parse('2025-02-28 09:00', 'YYYY-MM-DD HH:mm', 'America/New_York');

meeting.add(1, 'day');    // 2025-03-01 09:00
meeting.add(3, 'hour');   // 2025-03-01 12:00
meeting.set(15, 'minute'); // 12:15

final utcClone = meeting.cloneToTimezone('UTC'); // new instance
meeting.convertToTimezone('Europe/London');      // mutate in place

Supported units:

  • add: minute, hour, day, month, year
  • set: year, month (1–12), day, hour, minute

Comparison Guards #

final rome = DateTz.now('Europe/Rome');
final tokyo = rome.cloneToTimezone('Asia/Tokyo');

if (!rome.isComparable(tokyo)) {
  // Align before comparing
  tokyo.convertToTimezone(rome.timezone);
}

final diffMs = rome.compare(tokyo); // now safe

Comparison throws if timezones differ—your reminder to convert before mixing apples and oranges.

Parsing Recipes #

// AM/PM pattern
final breakfast = DateTz.parse('02/14/2025 08:30 AM', 'MM/DD/YYYY hh:mm AA', 'America/Chicago');

// Literals and seconds
final deployment = DateTz.parse('Deploy @ 2025-07-01 22:00:00', '[Deploy @ ]YYYY-MM-DD HH:mm:ss', 'UTC');

// Locale-specific names (fallback to English if locale data is missing)
print(deployment.format('DD LM YYYY HH:mm', 'es')); // "01 Julio 2025 22:00"

Timezone Offsets & DST Insight #

final romeNoon = DateTz.parse('2025-08-01 12:00', 'YYYY-MM-DD HH:mm', 'Europe/Rome');
print(romeNoon.timezoneOffset.sdt);         // 3600 (standard offset seconds)
print(romeNoon.timezoneOffset.observesDst); // true
print(romeNoon.isDst);                      // true in summer

Flutter Integration #

Provider / Riverpod Example #

import 'package:flutter/material.dart';
import 'package:mobile_date_tz/mobile_date_tz.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

final clockProvider = StreamProvider.family<DateTz, String>((ref, tz) async* {
  DateTz.initializeTimezones();
  while (true) {
    yield DateTz.now(tz);
    await Future<void>.delayed(const Duration(minutes: 1));
  }
});

class ClockText extends ConsumerWidget {
  const ClockText({required this.tz, super.key});

  final String tz;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final asyncClock = ref.watch(clockProvider(tz));
    return asyncClock.maybeWhen(
      data: (date) => Text(date.format('HH:mm tz')),
      orElse: () => const CircularProgressIndicator(),
    );
  }
}

Material Localization Compatibility #

format(pattern, locale) piggybacks on intl. If the requested locale isn’t initialized, the library falls back to English month names automatically—no hard crash during boot.


Server & CLI Recipes #

Scheduling Email Digests #

final offices = [
  {'tz': 'America/New_York', 'hour': 9},
  {'tz': 'Europe/Rome', 'hour': 9},
  {'tz': 'Asia/Tokyo', 'hour': 9},
];

final baseUtc = DateTz.now();

final sends = offices.map((office) {
  final local = DateTz(baseUtc).convertToTimezone(office['tz']!);
  local.set(office['hour']! as int, 'hour');
  if (local.compare(baseUtc) < 0) {
    local.add(1, 'day');
  }
  return local;
}).toList();

sends.sort((a, b) => a.timestamp.compareTo(b.timestamp));

Express Middleware (Shelf-style) #

Handler withContext(Handler inner) {
  return (request) {
    final headerTz = request.headers['x-user-tz'] ?? 'UTC';
    final now = () => DateTz.now(headerTz);
    return inner(request.change(context: {'now': now}));
  };
}

Package Anatomy #

lib/
 ├─ mobile_date_tz.dart        # Barrel export (library entry)
 └─ src/
     ├─ date_tz.dart           # Core implementation
     ├─ idate_tz.dart          # Minimal interface for interop
     └─ timezones.dart         # Generated timezone offsets
tool/
 └─ bump_version.dart          # CI helper for automated releases
.github/workflows/
 └─ release.yml                # Auto bump, test, publish to pub.flutter-io.cn

Release Flow #

  1. Merge or push to master.
  2. GitHub Actions runs tool/bump_version.dart, updates the changelog, formats/analyzes/tests, publishes to pub.flutter-io.cn (using PUB_CREDENTIALS_JSON secret), and tags the release.
  3. Profit.

Want to ship manually? Run:

dart run tool/bump_version.dart        # prints new version, updates changelog
dart pub publish --dry-run
dart pub publish

Testing & Quality #

dart format .
dart analyze
dart test

The bundled tests mirror the original TypeScript suite, covering formatting, arithmetic, parsing edge cases, and DST conversions. Contributions should include matching test updates.


Migration Guide (TypeScript → Dart) #

TypeScript Dart
new DateTz(ts, 'Europe/Rome') DateTz(ts, 'Europe/Rome')
date.toString(pattern, locale) date.format(pattern, locale)
DateTz.defaultFormat = '...' Same API
cloneToTimezone('UTC') Identical
convertToTimezone('UTC') Identical

The biggest differences:

  • Optional parameters replace overloads (format([pattern, locale])).
  • Parsing errors throw FormatException.
  • Month getter returns zero-based month (aligns with the TypeScript port).

FAQ #

What if the device doesn’t have timezone data?
DateTz falls back to the bundled offsets, so you still get coherent answers. Call DateTz.initializeTimezones() where possible for maximum accuracy.

Can I store seconds or milliseconds?
Timestamps are truncated to the minute on purpose. Keep sub-minute precision elsewhere if you need it.

How do I support custom tokens?
Wrap the output of format() with your own string replacements or fork the formatter—it’s a straightforward map replace.

Can I ship a subset of timezones?
Yes. Regenerate timezones.dart with your curated list to shrink size.


Community & Support #


License #

ISC © LBD SRL Timezone data derived from the IANA TZDB.

0
likes
160
points
61
downloads

Publisher

verified publisherlbdsh.com

Weekly Downloads

Port of the DateTz timezone utility for Flutter and Dart.

Repository (GitHub)
View/report issues

Documentation

API reference

License

ISC (license)

Dependencies

intl, timezone

More

Packages that depend on mobile_date_tz