NAME

DateTime::Format::RelativeTime - A Web Intl.RelativeTimeFormat Class Implementation

SYNOPSIS

use DateTime;
use DateTime::Format::RelativeTime;
my $fmt = DateTime::Format::RelativeTime->new(
    # You can use en-GB (Unicode / web-style) or en_GB (system-style), it does not matter.
    'en_GB', {
        localeMatcher => 'best fit',
        # see getNumberingSystems() in Locale::Intl for the supported number systems
        numberingSystem => 'latn',
        # Possible values are: long, short or narrow
        style => 'short',
        # Possible values are: always or auto
        numeric => 'always',
    },
) || die( DateTime::Format::RelativeTime->error );

# Format relative time using negative value (-1).
$fmt->format( -1, 'day' ); # "1 day ago"

# Format relative time using positive value (1).
$fmt->format( 1, 'day' ); # "in 1 day"

You can also pass one or two DateTime objects, and let this interface find out the greatest difference between the two objects. If you pass only one DateTime object, this will instantiate another DateTime object, using the method now with the time_zone value from the first object.

my $dt = DateTime->new(
    year => 2024,
    month => 8,
    day => 15,
);
$fmt->format( $dt );
# Assuming today is 2024-12-31, this would return: "1 qtr. ago"

or, with 2 DateTime objects:

my $dt = DateTime->new(
    year => 2024,
    month => 8,
    day => 15,
);
my $dt2 = DateTime->new(
    year => 2022,
    month => 2,
    day => 22,
);
$fmt->format( $dt => $dt2 ); # "2 yr. ago"

Using the auto option

If numeric option is set to auto, it will produce the string yesterday or tomorrow instead of 1 day ago or in 1 day. This allows to not always have to use numeric values in the output.

# Create a relative time formatter in your locale with numeric option set to 'auto'.
my $fmt = DateTime::Format::RelativeTime->new( 'en', { numeric => 'auto' });

# Format relative time using negative value (-1).
$fmt->format( -1, 'day' ); # "yesterday"

# Format relative time using positive day unit (1).
$fmt->format( 1, 'day' ); # "tomorrow"

In basic use without specifying a locale, DateTime::Format::RelativeTime uses the default locale and default options.

A word about precision:

When formatting numbers for display, this module uses up to 15 significant digits. This decision balances between providing high precision for calculations and maintaining readability for the user. If numbers with more than 15 significant digits are provided, they will be formatted to this limit, which should suffice for most practical applications:

my $num = 0.123456789123456789;
my $formatted = sprintf("%.15g", $num);
# $formatted would be "0.123456789123457"

For users requiring exact decimal representation beyond this precision, consider using modules like Math::BigFloat.

VERSION

v0.1.1

DESCRIPTION

This module provides the equivalent of the JavaScript implementation of Intl.RelativeTimeFormat

It relies on Locale::Unicode::Data, which provides access to all the Unicode CLDR (Common Locale Data Repository), and Locale::Intl to achieve similar results. It requires perl v5.10.1 minimum to run.

The algorithm provides the same result you would get with a web browser.

Because, just like its JavaScript equivalent, DateTime::Format::Intl does quite a bit of look-ups and sensible guessing upon object instantiation, you want to create an object for a specific format, cache it and re-use it rather than creating a new one for each date formatting.

CONSTRUCTOR

new

# Create a relative time formatter in your locale
# with default values explicitly passed in.
my $fmt = DateTime::Format::RelativeTime->new( 'en', {
    localeMatcher => 'best fit', # other values: 'lookup'
    numeric => 'always', # other values: 'auto'
    style => 'long', # other values: 'short' or 'narrow'
}) || die( DateTime::Format::RelativeTime->error );

# Format relative time using negative value (-1).
$fmt->format( -1, 'day' ); # "1 day ago"

# Format relative time using positive value (1).
$fmt->format( 1, 'day' ); # "in 1 day"

This takes a locale (a.k.a. language code compliant with ISO 15924 as defined by IETF) and an hash or hash reference of options and will return a new DateTime::Format::RelativeTime object, or upon failure undef in scalar context and an empty list in list context.

Each option can also be accessed or changed using their corresponding method of the same name.

See the CLDR (Unicode Common Locale Data Repository) page for more on the format patterns used.

Supported options are:

Locale options

  • localeMatcher

    The locale matching algorithm to use. Possible values are lookup and best fit; the default is best fit. For information about this option, see Locale identification and negotiation.

    Whatever value you provide, does not actually have any influence on the algorithm used. best fit will always be the one used.

  • numberingSystem

    The numbering system to use for number formatting, such as fullwide, hant, mathsans, and so on. For a list of supported numbering system types, see getNumberingSystems(). This option can also be set through the nu Unicode extension key; if both are provided, this options property takes precedence.

    For example, a Japanese locale with the latn number system extension set and with the jptyo time zone:

    my $fmt = DateTime::Format::RelativeTime->new( 'ja-u-nu-latn-tz-jptyo' );

    However, note that you can only provide a number system that is supported by the locale, and that is of type numeric, i.e. not algorithmic. For instance, you cannot specify a locale ar-SA (arab as spoken in Saudi Arabia) with a number system of Japan:

    my $fmt = DateTime::Format::RelativeTime->new( 'ar-SA', { numberingSystem => 'japn' } );
    say $fmt->resolvedOptions->{numberingSystem}; # arab

    It would reject it, and issue a warning, if warnings are enabled, and fallback to the locale's default number system, which is, in this case, arab

    Additionally, even though the number system jpanfin is supported by the locale ja, it would not be acceptable, because it is not suitable for datetime formatting since it is not of type numeric, or at least this is how it is treated by web browsers (see here the web browser engine implementation and here for the Unicode ICU implementation). This API could easily make it acceptable, but it was designed to closely mimic the web browser implementation of the JavaScript API Intl.DateTimeFormat. Thus:

    my $fmt = DateTime::Format::RelativeTime->new( 'ja-u-nu-jpanfin-tz-jptyo' );
    say $fmt->resolvedOptions->{numberingSystem}; # latn

    See Mozilla documentation, and also the perl module Locale::Intl

  • style

    The style of the formatted relative time. Possible values are:

    • long

      This is the default. For example: in 1 month

    • short

      For example: in 1 mo.

    • narrow

      For example: in 1 mo.. The narrow style could be similar to the short style for some locales.

  • numeric

    Whether to use numeric values in the output. Possible values are always and auto; the default is always. When set to auto, the output may use more idiomatic phrasing such as yesterday instead of 1 day ago.

METHODS

format

my $fmt = new DateTime::Format::RelativeTime( 'en', { style => 'short' });

say $fmt->format( 3, 'quarter' );
# Expected output: "in 3 qtrs."

say $fmt->format( -1, 'day' );
# Expected output: "1 day ago"

say $fmt->format( 10, 'seconds' );
# Expected output: "in 10 sec."

Alternatively, you can pass two DateTime objects, and format will calculate the greatest time difference between the two. If you provide only one DateTime, format will instantiate a new DateTime object using the time_zone value from the first DateTime object.

my $dt = DateTime->new(
    year => 2024,
    month => 8,
    day => 15,
);
$fmt->format( $dt );
# Assuming today is 2024-12-31, this would return: "1 qtr. ago"

or, with 2 DateTime objects:

my $dt = DateTime->new(
    year => 2024,
    month => 8,
    day => 15,
);
my $dt2 = DateTime->new(
    year => 2022,
    month => 2,
    day => 22,
);
$fmt->format( $dt => $dt2 ); # "2 yr. ago"

The format() method of DateTime::Format::RelativeTime instances formats a value and unit according to the locale and formatting options of this DateTime::Format::RelativeTime object.

It returns a string representing the given value and unit formatted according to the locale and formatting options of this DateTime::Format::RelativeTime object.

Supported parameters are:

value

Numeric value to use in the internationalized relative time message.

If the value is negative, the result will be formatted in the past.

unit

Unit to use in the relative time internationalized message.

Possible values are: year, quarter, month, week, day, hour, minute, second. Plural forms are also permitted.

Note: Most of the time, the formatting returned by format() is consistent. However, the output may vary between implementations, even within the same locale — output variations are by design and allowed by the specification. It may also not be what you expect. For example, the string may use non-breaking spaces or be surrounded by bidirectional control characters. You should not compare the results of format() to hardcoded constants.

formatToParts

my $fmt = new DateTime::Format::RelativeTime( 'en', { numeric => 'auto' });
my $parts = $fmt->formatToParts( 10, 'seconds' );

say $parts->[0]->{value};
# Expected output: "in "

say $parts->[1]->{value};
# Expected output: "10"

say $parts->[2]->{value};
# Expected output: " seconds"

my $fmt = new DateTime::Format::RelativeTime( 'en', { numeric => 'auto' });

# Format relative time using the day unit
$fmt->formatToParts( -1, 'day' );
# [{ type: 'literal', value: 'yesterday' }]

$fmt->formatToParts( 100, 'day' );
# [
#     { type => 'literal', value => 'in ' },
#     { type => 'integer', value => 100, unit => 'day' },
#     { type => 'literal', value => ' days' }
# ]

Just like for format, you can alternatively provide one or two DateTime objects.

The formatToParts() method of DateTime::Format::RelativeTime instances returns an array reference of hash reference representing the relative time format in parts that can be used for custom locale-aware formatting.

The DateTime::Format::RelativeTime-formatToParts> method is a version of the format method that returns an array reference of hash reference which represents parts of the object, separating the formatted number into its constituent parts and separating it from other surrounding text. These hash reference have two or three properties:

  • type a string

  • value, a string representing the component of the output.

  • unit

    The unit value for the number value, when the type is integer

Supported parameters are:

  • value

    Numeric value to use in the internationalized relative time message.

    If the value is negative, the result will be formatted in the past.

  • unit

    Unit to use in the relative time internationalized message.

    Possible values are: year, quarter, month, week, day, hour, minute, second. Plural forms are also permitted.

resolvedOptions

my $fmt = new DateTime::Format::RelativeTime('en', { style => 'narrow' });
my $options1 = $fmt->resolvedOptions();

my $fmt2 = new DateTime::Format::RelativeTime('es', { numeric => 'auto' });
my $options2 = $fmt2->resolvedOptions();

say "$options1->{locale}, $options1->{style}, $options1->{numeric}";
# Expected output: "en, narrow, always"

say "$options2->{locale}, $options2->{style}, $options2->{numeric}";
# Expected output: "es, long, auto"

The resolvedOptions() method of DateTime::Format::RelativeTime instances returns a new hash reference with properties reflecting the options computed during initialisation of this DateTime::Format::RelativeTime object.

For the details of the properties retured, see the new instantiation method.

CLASS METHODS

supportedLocalesOf

my $locales1 = ['ban', 'id-u-co-pinyin', 'de-ID'];
my $options1 = { localeMatcher: 'lookup' };

say DateTime::Format::RelativeTime->supportedLocalesOf( $locales1, $options1 );
# Expected output: ['id-u-co-pinyin', 'de-ID']

The DateTime::Format::RelativeTime-supportedLocalesOf> class method returns an array containing those of the provided locales that are supported in relative time formatting without having to fall back to the runtime's default locale.

Supported parameters are:

  • locale

    A string with a BCP 47 language tag, or an array of such strings. For the general form and interpretation of the locales argument, see the parameter description on the Locale::Intl documentation.

  • options

    An hash reference that may have the following property:

    • localeMatcher

      The locale matching algorithm to use. Possible values are lookup and best fit; the default is best fit. For information about this option, see the DateTime::Format::Intl documentation.

      In reality, it does not matter what value you set, because this module only support the best fit option.

OTHER NON-CORE METHODS

error

Sets or gets an exception object

When called with parameters, this will instantiate a new DateTime::Format::Intl::Exception object, passing it all the parameters received.

When called in accessor mode, this will return the latest exception object set, if any.

fatal

$fmt->fatal(1); # Enable fatal exceptions
$fmt->fatal(0); # Disable fatal exceptions
my $bool = $fmt->fatal;

Sets or get the boolean value, whether to die upon exception, or not. If set to true, then instead of setting an exception object, this module will die with an exception object. You can catch the exception object then after using try. For example:

use v.5.34; # to be able to use try-catch blocks in perl
use experimental 'try';
no warnings 'experimental';
try
{
    my $fmt = DateTime::Format::Intl->new( 'x', fatal => 1 );
}
catch( $e )
{
    say "Error occurred: ", $e->message;
    # Error occurred: Invalid locale value "x" provided.
}

AUTHOR

Jacques Deguest <jack@deguest.jp>

SEE ALSO

perl

COPYRIGHT & LICENSE

Copyright(c) 2024-2025 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.