Skip to content

pragneshkoli/universal_date_parser

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

11 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Universal Date Parser

A highly optimized, auto-detecting, bulletproof date parsing package for Dart and Flutter. Automatically resolves, parses, and reformats date strings without requiring you to manually specify the input pattern. Perfect for handling varying user inputs, erratic API responses, or inconsistent legacy databases.

pub package License: MIT


✨ Features

  • πŸ” Auto-Detect Format Routing: Instant pattern matching handles date formats automatically.
  • ⚑ Zero-Instantiation Access: Call static methods or top-level helpers directly without creating objects.
  • πŸ”— String Extensions: Perform fluid parsing and formatting directly on any String literal in your Flutter widgets.
  • πŸ“– Rich IDE Hover Documentation: Every public method is fully documented with complete Dartdoc comments and interactive markdown code blocks for instant hover assistance in VS Code, Android Studio, and IntelliJ.
  • πŸš€ High-Performance Architecture: Built for zero-overhead hot paths (precompiled regular expressions, pre-grouped list allocations, cached common output formatters, and O(1) case capitalization map lookups).
  • πŸ“… 50+ Date Format Variations: Full support for ISO-8601, RFC-2822, European Dot-separated dates, US (Month/Day/Year) layouts, 2-digit years, and compact strings.
  • 🌍 Case-Insensitive RFC/HTTP Header Parsing: Easily handles HTTP/RFC date formats in any case casing (Mon, mon, MON).
  • πŸ›‘οΈ Logical Calendrical Validation: Rejects invalid dates automatically (e.g., April 31st, February 30th, month 13, non-leap year Feb 29ths).
  • βœ… Exhaustive Testing Suite: 85+ passing unit tests covering edge cases, leap years, timezone offsets, and bounds.

πŸ“¦ Installation

Add the dependency to your package's pubspec.yaml file:

environment:
  sdk: ^3.12.0 # Supports Dart 3.12.0+ & Flutter 3.44.0+

dependencies:
  universal_date_parser: ^1.1.4

Then download the package:

dart pub get

πŸš€ Quick Start

1. Fluid String Extensions (Highly Recommended for Widgets)

import 'package:universal_date_parser/universal_date_parser.dart';

void main() {
  // Format string directly
  print('2025-11-21 14:20:30'.formatDate()); 
  // Output: 21/11/2025 14:20

  // Parse directly to native DateTime?
  DateTime? parsed = '21.11.25'.tryParseDate();
  print(parsed?.year); // Output: 2025
}

2. Static Class API (Stateless)

import 'package:universal_date_parser/universal_date_parser.dart';

void main() {
  // Direct formatting
  String formatted = UniversalDateParser.format('11/21/25', outputDateFormat: 'yyyy-MM-dd');
  print(formatted); // Output: 2025-11-21

  // Direct native DateTime parsing
  DateTime dt = UniversalDateParser.parse('202511211420');
  print(dt.hour); // Output: 14
}

3. Global Top-Level Helpers

import 'package:universal_date_parser/universal_date_parser.dart';

void main() {
  print(formatDate('Mon, 21 Nov 2025 14:20:00 +0530')); // 21/11/2025 14:20
  DateTime? dt = tryParseDate('21.11.2025');
}

πŸ“‹ Supported Date Formats

The parser matches, detects, and falls back to a massive catalog of date variations:

ISO 8601 Formats (Instant Fast-Path)

  • 2025-11-21T14:20:00.000Z (UTC Specifier)
  • 2025-11-21T14:20:00+05:30 (Timezone Offset)
  • 2025-11-21T14:20:00.123456 (Microsecond Accuracy)
  • 2025-11-21 (Date only)

Slash (/) Formats (European, US, 4 & 2 digit years)

  • dd/MM/yyyy HH:mm:ss / dd/MM/yyyy HH:mm / dd/MM/yyyy (e.g., 21/11/2025)
  • yyyy/MM/dd HH:mm:ss / yyyy/MM/dd HH:mm / yyyy/MM/dd (e.g., 2025/11/21)
  • dd/MM/yy HH:mm:ss / dd/MM/yy (e.g., 21/11/25)
  • MM/dd/yyyy HH:mm:ss / MM/dd/yyyy (e.g., 11/21/2025 - US Format)
  • MM/dd/yy HH:mm:ss / MM/dd/yy (e.g., 11/21/25 - US 2-digit format)

Dash (-) Formats (European, US, 4 & 2 digit years)

  • yyyy-MM-dd HH:mm:ss / yyyy-MM-dd HH:mm / yyyy-MM-dd (e.g., 2025-11-21)
  • dd-MM-yyyy HH:mm:ss / dd-MM-yyyy HH:mm / dd-MM-yyyy (e.g., 21-11-2025)
  • dd-MM-yy HH:mm:ss / dd-MM-yy (e.g., 21-11-25)
  • MM-dd-yyyy HH:mm:ss / MM-dd-yyyy (e.g., 11-21-2025 - US format)
  • MM-dd-yy (e.g., 11-21-25 - US 2-digit format)

Dot (.) Formats (European, US, 4 & 2 digit years)

  • dd.MM.yyyy HH:mm:ss / dd.MM.yyyy HH:mm / dd.MM.yyyy (e.g., 21.11.2025)
  • yyyy.MM.dd HH:mm:ss / yyyy.MM.dd (e.g., 2025.11.21)
  • MM.dd.yyyy HH:mm:ss / MM.dd.yyyy (e.g., 11.21.2025 - US format)
  • dd.MM.yy / MM.dd.yy (e.g., 21.11.25, 11.21.25 - 2-digit variants)

Text/Named Month Formats

  • dd MMM yyyy HH:mm:ss / dd MMM yyyy (e.g., 21 Nov 2025)
  • MMM dd, yyyy HH:mm:ss / MMM dd, yyyy (e.g., Nov 21, 2025)

Compact (Purely Numeric) Formats

  • yyyyMMdd (e.g., 20251121)
  • yyyyMMddHHmm (e.g., 202511211420)
  • yyyyMMddHHmmss (e.g., 20251121142030)

RFC 2822 / HTTP Header Formats (Case-Insensitive)

  • Mon, 21 Nov 2025 14:20:00 +05:30 (Proper case)
  • mon, 21 nov 2025 14:20:00 +0530 (Lowercase, flat timezone)
  • MON, 21 NOV 2025 14:20:00 (Uppercase, no timezone)
  • Fri, 25 Dec 2025 23:59:59 GMT (GMT Standard label)

🎨 Custom Formatting Outputs

Output formatted strings are highly customizable by specifying outputDateFormat during calls:

final input = '2025-11-21 14:20:30';

UniversalDateParser.format(input, outputDateFormat: 'yyyy-MM-dd');
// Output: 2025-11-21

UniversalDateParser.format(input, outputDateFormat: 'EEEE, MMMM d, yyyy');
// Output: Friday, November 21, 2025

UniversalDateParser.format(input, outputDateFormat: 'dd MMM yyyy HH:mm:ss');
// Output: 21 Nov 2025 14:20:30

⚑ API Reference & Zero-Instantiation Access

UniversalDateParser offers multiple, highly flexible ways to use the library depending on your architecture:

1. Static Class API (Zero-Instantiation)

Parse dates statically anywhere in your code without instantiating objects.

  • UniversalDateParser.tryParse(String date) Parses any supported format and returns a native [DateTime] object. Returns null if parsing fails.
    DateTime? parsed = UniversalDateParser.tryParse('21/11/2025 14:20');
  • UniversalDateParser.parse(String date) Parses and returns a native [DateTime] object. Throws a [FormatException] if parsing fails.
    DateTime parsed = UniversalDateParser.parse('2025.11.21');
  • UniversalDateParser.format(String date, {String outputDateFormat}) Formats the date string automatically. Returns 'Invalid date' if parsing fails.
    String display = UniversalDateParser.format('20251121');

2. Elegant String Extensions (Widget-Friendly)

Direct, fluid extension methods on standard String literals. Extremely convenient for direct use in Flutter widgets.

  • String.formatDate({String outputDateFormat}) Formats this string automatically.
    Text('2025-11-21'.formatDate()) // Output widget: 21/11/2025 00:00
  • String.tryParseDate() Parses this string into a native [DateTime?].
    DateTime? dt = 'Mon, 21 Nov 2025 14:20:00'.tryParseDate();
  • String.parseDate() Parses this string into a native [DateTime]. Throws [FormatException] if invalid.
    DateTime dt = '202511211420'.parseDate();

3. Global Top-Level Helpers

Standard global functions available after importing the package.

  • tryParseDate(String date) ──► Returns DateTime?
  • parseDate(String date) ────► Returns DateTime
  • formatDate(String date, {String outputDateFormat}) ──► Returns String

4. Instance-level API

Ideal for object-oriented injections.

final parser = UniversalDateParser();
String display = parser.formatDate(date: '21/11/2025');

Engineered for Maximum Performance:

  1. Direct Delimiter Insertion: Purely numeric strings (length 8, 12, 14) are pre-segmented with standard dash delimiters, allowing them to instantly bypass complex pattern parsing and resolve directly via DateTime.tryParse().
  2. Precompiled Regex Assets: All Regular Expression objects are allocated once as static final members, removing heavy regex compile cycles during consecutive format runs.
  3. O(1) Capitalization Normalization: Rather than looping and performing 19 sequential regex replacements for day/month casing in RFCs, a single-pass \b[A-Za-z]{3}\b replacement resolves casing immediately via map lookup.
  4. Grouped Memory References: Candidate detection lists are pre-allocated statically. Auto-detection directly returns static array references instead of generating, filtering, and mapping lists on the fly.
  5. Direct Format Caching: Pre-compiles the default output formatter DateFormat('dd/MM/yyyy HH:mm') as a static final object to prevent initialization overhead.

⚠️ Known Limitations & Ambiguity Resolutions

While UniversalDateParser is built to be extremely flexible, developers must be aware of certain logic constraints:

1. Resolution of Ambiguous Formats (European vs. US Dates)

When an ambiguous numeric date is passed (e.g., 05/06/2025), it is mathematically impossible to differentiate between June 5th (European/International) and May 6th (US format) without context.

  • Resolution Behavior: The parser prioritizes International/European conventions (dd/MM/yyyy) by default over US structures. Therefore, 05/06/2025 will always be resolved as June 5th, 2025.
  • US Fallback Resolution: If a US date is unambiguous (e.g., 05/26/2025 where the month cannot be 26), the International parser will reject it, and the fallback pipeline will automatically attempt and successfully parse it using the US formatter as May 26th, 2025.

2. Timezone Shifting in ISO Formats

Dates containing timezone offsets (e.g. 2025-11-21T14:20:00-12:00 or 2025-11-21T14:20:00.000Z) are parsed using Dart's native DateTime.tryParse().

  • Resolution Behavior: Native Dart parsing converts these values into the local timezone of the host device.
  • Developer Notice: If your local timezone offset shifts the date across a midnight boundary (for example, parsing an offset of -12:00 on a device set to +05:30 local time), the resulting formatted date string will reflect the local date (e.g. transitioning from the 21st to the 22nd).

3. 2-Digit Year Pivoting

When parsing 2-digit years (such as 21/11/25 or 11-21-85):

  • Resolution Behavior: The parser implements a fixed pivot window at year 80:
    • Values under 80 resolve to the 21st Century (20xx). E.g., 25 -> 2025.
    • Values 80 and above resolve to the 20th Century (19xx). E.g., 85 -> 1985.

4. Non-Standard Compact Formats

Compact numeric sequences must be exactly 8, 12, or 14 characters long (yyyyMMdd, yyyyMMddHHmm, or yyyyMMddHHmmss). Numeric representations outside these lengths (e.g., a 10-digit number like 2025112114) cannot be safely auto-delimited and will fall back to "Invalid date".


πŸ§ͺ Running Tests

The test suite covers happy paths, edge cases, boundaries, timezone offsets, leap years, static helper execution, and calendrical errors. To run the suite:

dart test

Output:

00:00 +85: All tests passed!

πŸ“ Example Demo Showcase

To see the date parser resolve 30+ different formats in real time, execute the console example app:

dart run example/universal_date_parser_example.dart

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A highly optimized, auto-detecting, zero-configuration date and time parsing package for Dart & Flutter. Supports ISO, slash, dash, dot, text, US, RFC, and compact formats.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages