[Dbix-class] DateTime personalization

Bernhard Graf dbic3 at augensalat.de
Fri Oct 31 22:35:32 GMT 2008


For my datetime and date fields I use DBIC:InflateColumn::DateTime and 
DBIC:Timestamp.

Now I wanted to personalize those DateTime objects for the authenticated 
user: Time zone and output format should be set on inflation, according 
to what the authenticated user has configured in his/her personal 
settings.

Here is how this is done currently:

In the user table there are columns for the time zone (Olson format), 
datetime and date format (strftime format) and the current locale 
(setlocale(3) format):


    package MyApp::Schema::User;

    __PACKAGE__->add_columns(
        [...]
        locale => {data_type => 'varchar'},
        timezone => {data_type => 'varchar'},
        format_datetime => {data_type => 'varchar'},
        format_date => {data_type => 'varchar'},
        [...]
    );


In MyApp::Schema I have:

    __PACKAGE__->mk_group_accessors(
        'simple' => qw/
            current_locale
            current_time_zone
            current_datetime_format
            current_date_format
        /
    );

It is up to the application to fill those fields in as soon as it can 
catch the user data. In my Catalyst application it is 
MyApp::Controller::Admin::auto() that does this:

    my $schema = $c->model('DB')->schema;
    my $user = $c->user->get_object;
    my $locale = $user->locale || 'en_US';
    my $timezone = $user->timezone || 'local';

    $schema->current_locale($locale);
    $schema->current_time_zone($timezone);

    my $dtf = $user->format_datetime || '%Y-%m-%e %T';
    $schema->current_datetime_format(
        DateTime::Format::Strptime->new(
            pattern => $dtf, locale => $locale, time_zone => $timezone,
        )
    );
    dtf = $user->format_date || '%Y-%m-%e';
    $schema->current_date_format(
        DateTime::Format::Strptime->new(
            pattern => $dtf, locale => $locale, time_zone => $timezone,
        )
    );

The question was now how to do personalization on DateTime inflation.
Unfortunately I didn't find a better solution than using a patched 
version of DBIC::InflateColumn::DateTime.

The inflate-part for inflate_column now looks like this:

          inflate => sub {
            my ($value, $obj) = @_;
            my $z;
            my $dt = $obj->_datetime_parser->$parse($value);
            $dt->set_time_zone($timezone) if $timezone;
            $z = $obj->get_user_time_zone
                and $dt->set_time_zone($z);
            $z = "get_user_${type}_format";
            $z = $obj->$z()
                and $dt->set_formatter($z);
            return $dt;
          },

And also in this package there are three stub methods:

sub get_user_time_zone { }
sub get_user_datetime_format { }
sub get_user_date_format { }

Since those stub methods don't do anything DBIC::InflateColumn::DateTime 
works as before unless you load this component before it:

    package DBIx::Class::DateTimeMethods;

    use strict;
    use warnings;
    use base qw/DBIx::Class/;

    sub get_user_time_zone {
        return shift->result_source->schema->current_time_zone;
    }

    sub get_user_datetime_format {
        return shift->result_source->schema->current_datetime_format;
    }

    sub get_user_date_format {
        return shift->result_source->schema->current_date_format;
    }

    1;

LBNL all I have to change in my DBIC::Schema classes is loading 
component DateTimeMethods before InflateColumn::DateTime or TimeStamp:

    _PACKAGE__->load_components(qw/DateTimeMethods TimeStamp Core/);

Now all datetime objects stringify automagically according to the 
authenticated user's settings.


My only concern is: Would this be possible without patching 
DBIC::InflateColumn::DateTime ?
-- 
Bernhard Graf



More information about the DBIx-Class mailing list