DateTime-Lite

 view release on metacpan or  search on metacpan

lib/DateTime/Lite.pm  view on Meta::CPAN

    $dt->start_of( 'month' );
    say $dt;  # 2026-04-01T00:00:00

    # Comparison
    my @sorted = sort { $a <=> $b } @datetimes;  # overloaded <=>
    DateTime::Lite->compare( $dt1, $dt2 );       # -1, 0, 1
    DateTime::Lite->compare_ignore_floating( $dt1, $dt2 );
    $dt->is_between( $lower, $upper );

    # Class-level settings
    DateTime::Lite->DefaultLocale('fr-FR');
    my $class = $dt->duration_class;  # 'DateTime::Lite::Duration'

    # Constants
    DateTime::Lite::INFINITY();        # +Inf
    DateTime::Lite::NEG_INFINITY();    # -Inf
    DateTime::Lite::NAN();             # NaN
    DateTime::Lite::MAX_NANOSECONDS(); # 1_000_000_000
    DateTime::Lite::SECONDS_PER_DAY(); # 86400

    # Error handling
    my $dt2 = DateTime::Lite->new( %bad_args ) ||
        die( DateTime::Lite->error );
    # Chaining: bad calls return a NullObject so the chain continues safely;
    # check the return value of the last call in the chain.
    my $result = $dt->some_method->another_method ||
        die( $dt->error );

=head1 VERSION

    v0.6.5

=head1 DESCRIPTION

C<DateTime::Lite> is a lightweight, memory-efficient, drop-in replacement for L<DateTime> with the following design goals:

=over 4

=item Low dependency footprint

Runtime dependencies are limited to: L<DateTime::Lite::TimeZone> (bundled SQLite timezone data, with automatic fallback to L<DateTime::TimeZone> if L<DBD::SQLite> is unavailable), L<DateTime::Locale::FromCLDR> (locale data via L<Locale::Unicode::Data...

The heavy L<Specio>, L<Params::ValidationCompiler>, L<Try::Tiny>, and C<namespace::autoclean> are eliminated entirely.

=item Low memory footprint

C<DateTime> loads a cascade of modules which inflates C<%INC> significantly. C<DateTime::Lite> avoids this via selective lazy loading.

=item Accurate timezone data from TZif binaries

C<DateTime::TimeZone> derives its zone data from the IANA Olson I<source> files (C<africa>, C<northamerica>, etc.) via a custom text parser (C<DateTime::TimeZone::OlsonDB>), then pre-generates one C<.pm> file per zone at distribution build time. This...

C<DateTime::Lite::TimeZone> instead compiles the IANA source files with C<zic(1)>, which is the official IANA compiler, and reads the resulting TZif binary files directly, following L<RFC 9636|https://www.rfc-editor.org/rfc/rfc9636> (TZif versions 1 ...

Crucially, the POSIX footer TZ string embedded in every TZif v2+ file, such as C<EST5EDT,M3.2.0,M11.1.0>, is extracted and stored in the SQLite database.

This string encodes the recurring DST rule for all dates beyond the last explicit transition. At runtime, C<DateTime::Lite::TimeZone> evaluates the footer rule via an XS implementation of the IANA C<tzcode> reference algorithm (see C<dtl_posix.h>, de...

=item XS-accelerated hot paths

The XS layer covers all CPU-intensive calendar arithmetic (C<_rd2ymd>, C<_ymd2rd>, C<_seconds_as_components>, all leap-second helpers), plus new functions not in the original: C<_rd_to_epoch>, C<_epoch_to_rd>, C<_normalize_nanoseconds>, and C<_compar...

=item Compatible API

The public API mirrors L<DateTime> as closely as possible, so existing code using C<DateTime> should work with C<DateTime::Lite> as a drop-in replacement.

=item Full Unicode CLDR / BCP 47 locale support

C<DateTime> is limited to the set of pre-generated C<DateTime::Locale::*> modules, one per locale. C<DateTime::Lite> accepts any valid Unicode CLDR / BCP 47 locale tag, including complex forms with Unicode extensions (C<-u->), transform extensions (C...

    my $dt = DateTime::Lite->now( locale => 'en' );    # simple form
    my $dt = DateTime::Lite->now( locale => 'en-GB' ); # simple form
    # And more complex forms too
    my $dt = DateTime::Lite->now( locale => 'he-IL-u-ca-hebrew-tz-jeruslm' );
    my $dt = DateTime::Lite->now( locale => 'ja-Kana-t-it' );
    my $dt = DateTime::Lite->now( locale => 'ar-SA-u-nu-latn' );

Locale data is resolved dynamically by L<DateTime::Locale::FromCLDR> via L<Locale::Unicode::Data>, so tags like C<he-IL-u-ca-hebrew-tz-jeruslm> or C<ja-Kana-t-it> work transparently without any additional installed modules.

Additionally, if the locale tag carries a L<Unicode timezone extension|Locale::Unicode/"Unicode extensions"> (C<-u-tz->), and no explicit C<time_zone> argument is provided to the constructor, C<DateTime::Lite> will automatically resolve the correspon...

    # time_zone is inferred as 'Asia/Jerusalem' from the -u-tz-jeruslm extension
    my $dt = DateTime::Lite->now( locale => 'he-IL-u-ca-hebrew-tz-jeruslm' );
    say $dt->time_zone;            # Asia/Jerusalem
    say $dt->time_zone_long_name;  # Asia/Jerusalem

An explicit C<time_zone> argument always takes priority over the locale extension.

=item No die() in normal operation

Following the L<Module::Generic> / L<Locale::Unicode> error-handling philosophy, C<DateTime::Lite> never calls C<die()> in normal error paths.

Instead it sets a L<DateTime::Lite::Exception> object and returns C<undef> in scalar context, or an empty list in list context.

However, if you really want this module to C<die> upon error, you can pass the C<fatal> option with a true value upon object instantiation.

=back

=head1 KNOWN DIFFERENCES FROM DateTime

=over 4

=item Validation

C<DateTime> uses L<Specio> / L<Params::ValidationCompiler> for constructor validation. C<DateTime::Lite> performs equivalent checks manually. Error messages are similar but not identical.

=item No warnings::register abuse

C<DateTime::Lite> uses C<warnings::enabled> consistently and does not depend on the C<warnings::register> mechanism for user-facing output.

=back

=head1 METHODS NOT IMPLEMENTED

None at this time. If you encounter a method missing from the L<DateTime> API, please file a report.

=head1 CONSTRUCTORS

=head2 new

Accepted parameters are:

lib/DateTime/Lite.pm  view on Meta::CPAN

    my $dow = $dt->day_of_week;  # 1=Mon .. 7=Sun

Returns the day of week as a number from 1 (Monday) to 7 (Sunday), following the ISO 8601 convention.

=head2 day_of_year

    my $doy = $dt->day_of_year;

Returns the day of the year (1-366).

=head2 day_abbr

    my $abbr = $dt->day_abbr;  # e.g. "Mon"

Returns the abbreviated weekday name for the current locale.

=head2 day_name

    my $name = $dt->day_name;  # e.g. "Monday"

Returns the full weekday name for the current locale.

=head2 month_0

    my $m0 = $dt->month_0;  # 0=Jan .. 11=Dec

Returns the month as a zero-based number (0-11).

=head2 mon_0

Alias for L</month_0>.

=head2 month_abbr

    my $abbr = $dt->month_abbr;  # e.g. "Jan"

Returns the abbreviated month name for the current locale.

=head2 month_name

    my $name = $dt->month_name;  # e.g. "January"

Returns the full month name for the current locale.

=head2 week

    my( $wy, $wn ) = $dt->week;

Returns a two-element list C<( $week_year, $week_number )> according to ISO 8601 week numbering.

=head2 week_number

    my $wn = $dt->week_number;

Returns the ISO 8601 week number (1-53).

=head2 week_year

    my $wy = $dt->week_year;

Returns the year that the ISO 8601 week belongs to. This may differ from L</year> for days near the start or end of the calendar year.

=head2 quarter

    my $q = $dt->quarter;

Returns the quarter of the year (1-4).

=head2 epoch

    my $ts = $dt->epoch;

Returns the Unix timestamp (seconds since 1970-01-01T00:00:00 UTC) as an integer.

=head2 hires_epoch

    my $ts = $dt->hires_epoch;

Returns the Unix timestamp as a floating-point number (IEEE 754 double) that includes sub-second precision.

B<Precision caveat:> a 64-bit double has ~15-16 significant decimal digits.
A Unix timestamp around 2026 already consumes 10 digits for the integer part, leaving only ~6 digits for the fractional part. This means precision is effectively limited to the microsecond range (~1 µs); nanosecond values smaller than a few hundred ...

For full nanosecond precision, combine L</epoch> and L</nanosecond> directly:

    printf "%d.%09d\n", $dt->epoch, $dt->nanosecond;

=head2 jd

    my $jd = $dt->jd;

Returns the Julian Day Number as a floating-point number.

=head2 mjd

    my $mjd = $dt->mjd;

Returns the Modified Julian Day (Julian Day minus 2,400,000.5).

=head2 offset

    my $off = $dt->offset;

Returns the UTC offset in seconds for the current datetime, such as C<32400> for C<+09:00>.

=head2 time_zone

    my $tz = $dt->time_zone;

Returns the L<DateTime::Lite::TimeZone> object associated with this datetime.

=head2 time_zone_long_name

    my $name = $dt->time_zone_long_name;

Returns the long name of the time zone, such as C<America/New_York>.

=head2 time_zone_short_name

    my $abbr = $dt->time_zone_short_name;

lib/DateTime/Lite.pm  view on Meta::CPAN


=head2 hms( [$sep] )

    my $time = $dt->hms;          # "12:34:56"
    my $time = $dt->hms( '.' );   # "12.34.56"

Returns the time portion as C<HH:MM:SS> (default separator C<":">>).

=head2 dmy( [$sep] )

    my $dmy = $dt->dmy;           # "09-04-2026"

Returns the date as C<DD-MM-YYYY>.

=head2 mdy( [$sep] )

    my $mdy = $dt->mdy;           # "04-09-2026"

Returns the date as C<MM-DD-YYYY>.

=head2 rfc3339

    my $str = $dt->rfc3339;       # "2026-04-09T12:34:56+09:00" 

Returns an RFC 3339 string. For a UTC datetime this is the same as L</iso8601> with a C<Z> suffix; for other timezones it appends the numeric offset.

=head1 ARITHMETIC

=head2 add( %args )

    $dt->add( years => 1, months => 3 );
    $dt->add( hours => 2, minutes => 30 );

Adds a duration to the datetime in-place (mutates C<$self>). Accepts the same keys as L<DateTime::Lite::Duration/new>: C<years>, C<months>, C<weeks>, C<days>, C<hours>, C<minutes>, C<seconds>, C<nanoseconds>.

Returns C<$self> to allow chaining.

=head2 subtract( %args )

    $dt->subtract( days => 7 );

Subtracts a duration from the datetime in-place (mutates C<$self>). Equivalent to C<< $dt->add >> with all values negated.

=head2 add_duration( $dur )

    my $dur = DateTime::Lite::Duration->new( months => 2 );
    $dt->add_duration( $dur );

Adds a L<DateTime::Lite::Duration> object to the datetime in-place (mutates C<$self>).

Returns C<$self> to allow chaining.

=head2 subtract_duration( $dur )

    $dt->subtract_duration( $dur );

Subtracts a L<DateTime::Lite::Duration> object from the datetime in-place (mutates C<$self>). Equivalent to C<< $dt->add_duration( $dur->inverse ) >>.

=head2 subtract_datetime( $dt )

Returns a L<DateTime::Lite::Duration> representing the difference between two C<DateTime::Lite> objects (calendar-aware).

=head2 subtract_datetime_absolute( $dt )

Returns a L<DateTime::Lite::Duration> representing the absolute UTC difference in seconds/nanoseconds.

=head2 delta_days( $dt )

    my $dur = $dt1->delta_days( $dt2 );
    printf "%d days apart\n", $dur->days;

Returns a L<DateTime::Lite::Duration> containing only a C<days> component representing the number of whole days between C<$self> and C<$dt>.

=head2 delta_md( $dt )

    my $dur = $dt1->delta_md( $dt2 );

Returns a L<DateTime::Lite::Duration> with C<months> and C<days> components (calendar-aware difference).

=head2 delta_ms( $dt )

    my $dur = $dt1->delta_ms( $dt2 );

Returns a L<DateTime::Lite::Duration> with C<minutes> and C<seconds> components (absolute clock difference).

=head1 SETTERS

=head2 set

    $dt->set( hour => 0, minute => 0, second => 0 );

Sets one or more datetime components in-place. Accepted keys are any of C<year>, C<month>, C<day>, C<hour>, C<minute>, C<second>, C<nanosecond>. Returns C<$self>.

=head2 set_year

    $dt->set_year(2030);

Sets the year component. Returns C<$self>.

=head2 set_month

    $dt->set_month(12);

Sets the month (1-12). Returns C<$self>.

=head2 set_day

    $dt->set_month(31);

Sets the day of the month. Returns C<$self>.

=head2 set_hour

    $dt->set_hour(14);

Sets the hour (0-23). Returns C<$self>.

=head2 set_minute

    $dt->set_minute(40);

Sets the minute (0-59). Returns C<$self>.

=head2 set_second

    $dt->set_second(30);

Sets the second (0-59). Returns C<$self>.

=head2 set_nanosecond

    $dt->set_nanosecond(1000);

Sets the nanosecond component (0-999_999_999). Returns C<$self>.

=head2 set_locale

    $dt->set_locale( 'zh-TW' );

lib/DateTime/Lite.pm  view on Meta::CPAN

=head2 Overloading

C<DateTime::Lite> overloads the following operators:

=over 4

=item * B<C<+>> - adds a L<DateTime::Lite::Duration> to a datetime, returning a new datetime.

=item * B<C<->> - either subtracts a duration from a datetime (returning a new datetime), or subtracts two datetimes (returning a L<DateTime::Lite::Duration>).

=item * B<C<< <=> >>  and  B<C<cmp>>> - numeric and string comparison, for use with C<sort> and comparison operators.

=item * B<C<"">> (stringification) - calls L<stringify|/stringify>, which delegates to the formatter if set, otherwise returns the L<iso8601|/iso8601> string.

=item * B<C<bool>> - always true for finite objects.

=back

The C<fallback> parameter is set, so derived operators (C<+=>, C<-=>, etc.) work as expected. Do not expect C<++> or C<--> to be useful.

    my $dt2 = $dt + $duration;  # new datetime
    my $dt3 = $dt - $duration;  # new datetime
    my $dur = $dt - $other_dt;  # Duration

    for my $dt ( sort @datetimes ) { ... }  # uses <=>

=head2 Formatters And Stringification

You can supply a C<formatter> object to control how a datetime is stringified.
Any constructor accepts a C<formatter> argument:

    my $fmt = DateTime::Format::Unicode->new( locale => 'fr-FR' );
    my $dt  = DateTime::Lite->new( year => 2026, formatter => $fmt );

Or set it afterwards:

    $dt->set_formatter( $fmt );
    my $current_fmt = $dt->formatter;

Once set, C<$dt> will call C<< $fmt->format_datetime($dt) >> instead of L<iso8601|/iso8601>. Pass C<undef> to revert to the default.

A formatter must implement a C<format_datetime($dt)> method. The L<DateTime::Format::Unicode> module (available separately on CPAN) provides a full-featured CLDR formatter with support for date/time intervals and additional pattern tokens not covered...

=head1 CLDR PATTERNS

The CLDR (Unicode Common Locale Data Repository) pattern language is more powerful and more complex than strftime. Unlike strftime, patterns are plain letters with no prefix, so any literal text must be quoted.

=head2 Quoting and escaping

Surround literal ASCII letters with single quotes (C<'>). To include a literal single quote, write two consecutive single quotes (C<''>). Spaces and non-letter characters are always passed through unchanged.

    my $p1 = q{'Today is ' EEEE};           # "Today is Thursday"
    my $p2 = q{'It is now' h 'o''clock' a}; # "It is now 9 o'clock AM"

=head2 Pattern length and padding

Most patterns pad with leading zeroes when the specifier is longer than one character. For example, C<h> gives C<9> but C<hh> gives C<09>. The exception is that B<five> of a letter usually means the narrow form, such as C<EEEEE> gives C<T> for Thursd...

=head2 Format vs. stand-alone forms

Many tokens have a I<format> form (used inside a larger string) and a I<stand-alone> form (used alone, such as in a calendar header). They are distinguished by case: C<M> is format, C<L> is stand-alone for months; C<E>/C<e> is format, C<c> is stand-a...

=head2 Token reference

    Era
      G{1,3}   abbreviated era (BC, AD)
      GGGG     wide era (Before Christ, Anno Domini)
      GGGGG    narrow era

    Year
      y        year, zero-padded as needed
      yy       two-digit year (special case)
      Y{1,}    week-of-year calendar year (from week_year)
      u{1,}    same as y, but yy is not special

    Quarter
      Q{1,2}   quarter as number (1-4)
      QQQ      abbreviated format quarter
      QQQQ     wide format quarter
      q{1,2}   quarter as number (stand-alone)
      qqq      abbreviated stand-alone quarter
      qqqq     wide stand-alone quarter

    Month
      M{1,2}   numerical month (format)
      MMM      abbreviated format month name
      MMMM     wide format month name
      MMMMM    narrow format month name
      L{1,2}   numerical month (stand-alone)
      LLL      abbreviated stand-alone month name
      LLLL     wide stand-alone month name
      LLLLL    narrow stand-alone month name

    Week
      w{1,2}   week of year (from week_number)
      W        week of month (from week_of_month)

    Day
      d{1,2}   day of month
      D{1,3}   day of year
      F        day of week in month (from weekday_of_month)
      g{1,}    modified Julian day (from mjd)

    Weekday
      E{1,3}   abbreviated format weekday
      EEEE     wide format weekday
      EEEEE    narrow format weekday
      e{1,2}   locale-based numeric weekday (1 = first day of week for locale)
      eee      abbreviated format weekday (same as E{1,3})
      eeee     wide format weekday
      eeeee    narrow format weekday
      c        numeric weekday, Monday = 1 (stand-alone)
      ccc      abbreviated stand-alone weekday
      cccc     wide stand-alone weekday
      ccccc    narrow stand-alone weekday

    Period
      a        AM or PM (localized)

    Hour
      h{1,2}   hour 1-12
      H{1,2}   hour 0-23
      K{1,2}   hour 0-11
      k{1,2}   hour 1-24
      j{1,2}   locale-preferred hour (12h or 24h)

    Minute / Second
      m{1,2}   minute
      s{1,2}   second
      S{1,}    fractional seconds (without decimal point)
      A{1,}    millisecond of day

    Time zone

lib/DateTime/Lite.pm  view on Meta::CPAN

The following tokens are B<not supported> by C<format_cldr()> but are supported by L<DateTime::Format::Unicode>:

=over 4

=item * C<b> / C<B> - period and flexible period of day (C<noon>, C<at night>...)

=item * C<O> / C<OOOO> - localized GMT format (C<GMT-8>, C<GMT-08:00>)

=item * C<r> - related Gregorian year

=item * C<x>/C<X> - ISO 8601 timezone offsets with optional C<Z>

=back

=head2 CLDR Available Formats

The CLDR data includes locale-specific pre-defined format skeletons. A skeleton is a pattern key that maps to a locale-appropriate rendering pattern. For example, the skeleton C<MMMd> maps to C<MMM d> in C<en-US> (giving C<Apr 9>) and to C<d MMM> in ...

Retrieve the locale-specific pattern via the locale object and pass it to C<format_cldr>:

    say $dt->format_cldr( $dt->locale->available_format('MMMd') );
    say $dt->format_cldr( $dt->locale->available_format('yQQQ') );
    say $dt->format_cldr( $dt->locale->available_format('hm') );

See L<DateTime::Locale::FromCLDR/available_formats> for the full list of skeletons for any given locale.

=head2 DateTime::Format::Unicode

For more advanced formatting, including features not covered by C<format_cldr()>, use L<DateTime::Format::Unicode> (available separately on CPAN). It provides:

=over 4

=item * Support for the additional tokens listed above (C<b>, C<B>, C<O>, C<r>, C<x>, C<X>)

=item * Formatting of datetime B<intervals>, such as "Apr 9 - 12, 2026"

=item * Full CLDR number system support (Arabic-Indic numerals, etc.)

=item * Any CLDR locale, including complex tags such as C<es-419-u-ca-gregory>

=back

    use DateTime::Format::Unicode;

    my $fmt = DateTime::Format::Unicode->new(
        locale  => 'ja-JP',
        pattern => 'GGGGy年M月d日(EEEE)',
    ) || die( DateTime::Format::Unicode->error );

    say $fmt->format_datetime( $dt );

    # Interval formatting:
    my $fmt2 = DateTime::Format::Unicode->new(
        locale  => 'en',
        pattern => 'GyMMMd',
    );
    say $fmt2->format_interval( $dt1, $dt2 );  # e.g. "Apr 9 - 12, 2026"

=head1 HOW DATETIME MATH WORKS

Date math in C<DateTime::Lite> follows the same model as L<DateTime>. The key distinction is between I<calendar units> (months, days) and I<clock units> (minutes, seconds, nanoseconds). Understanding this distinction is essential for correct results.

=head2 Duration buckets

A L<DateTime::Lite::Duration> stores its components in five independent I<buckets>: months, days, minutes, seconds, nanoseconds. Each bucket is kept as a signed integer. The buckets are B<not normalised> against each other: a duration of C<< { months...

=head2 Calendar vs. clock units

I<Calendar units> (months, days) are relative: their real duration depends on the datetime to which they are applied. I<Clock units> (minutes, seconds, nanoseconds) are absolute.

When L<add|/add> applies a duration, calendar units are applied first, then clock units:

    $dt->add( months => 1, hours => 2 );
    # Step 1: advance by 1 month  (calendar)
    # Step 2: advance by 2 hours  (clock)

=head2 End-of-month handling

Adding months to a date whose day is beyond the end of the target month requires a policy decision. L<DateTime::Lite::Duration> supports three C<end_of_month> modes:

=over 4

=item * C<wrap> (default) - wrap into the next month. January 31 + 1 month = March 3 (or 2 in leap years).

=item * C<limit> - clamp to the last day of the target month. January 31 + 1 month = February 28 (or 29 in leap years).

=item * C<preserve> - like C<limit>, but remember that the original day was at the end of month, so a further addition of one month will also land on the last day.

=back

=head2 Subtraction

C<< $dt1->subtract_datetime( $dt2 ) >> returns a duration representing the difference. The calendar part is computed in months and days (from the local dates), and the clock part in seconds and nanoseconds (from the UTC representations). This is the ...

C<< $dt1->subtract_datetime_absolute( $dt2 ) >> returns a duration in pure clock units (seconds and nanoseconds), based on the UTC epoch difference. This is useful when you need an exact elapsed time independent of DST changes.

=head2 Leap seconds

C<DateTime::Lite> handles leap seconds when the time zone is not floating.
Adding a duration in clock units across a leap second boundary will correctly account for the extra second.

=head1 SEE ALSO

L<DateTime>, L<DateTime::Lite::Duration>, L<DateTime::Lite::Exception>, L<DateTime::Lite::Infinite>, L<DateTime::Locale::FromCLDR>, L<Locale::Unicode::Data>, L<DateTime::Format::Unicode>

=head1 CREDITS

Credits to the original author of L<DateTime>, Dave Rolsky and all the contributors for their great work on which this module L<DateTime::Lite> is derived.

=head1 AUTHOR

Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>

=head1 COPYRIGHT & LICENSE

Copyright(c) 2026 DEGUEST Pte. Ltd.

All rights reserved

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

=cut



( run in 1.678 second using v1.01-cache-2.11-cpan-39bf76dae61 )