in reply to Date conversion with Class::DBI

I use the has_a relationship using the Time::Piece::MySQL module which will do the formatting and stringifying for you.

use Time::Piece::MySQL; __PACKAGE__->has_a(created => 'Time::Piece', inflate => 'from_mysql_datetime', deflate => 'mysql_datetime', );

- Cees

Replies are listed 'Best First'.
Re: Re: Date conversion with Class::DBI
by bsb (Priest) on Jul 07, 2003 at 05:36 UTC
    This is still not quite there although thanks for the tip.

    One reason is that I can't change the stringification.
    Another is that the mutator still only accepts ISO format since inflate gets called both going from the database to the object and from the mutator/user to the object.

    DB<1> p $p->gp_ref_date Thu Mar 1 00:00:00 2001 DB<2> p $p->gp_ref_date('01/02/2003') after_set_gp_ref_date trigger error: Error parsing time
    I presume that you're "created" column doesn't get modified.

    Thanks again for a good lead though.

      I've never used Class::DBI, but based on what I've read in the docs, i think maybe you aren't fully understanding what cees is trying to say.

      According to the docs, you can use has_a to specify a type of object to model a nugget of data, and whenever the value of that nugget is "set", the specified "inflate" method will be called to construct an object of that type, and the specified "deflate" method will be used whenever the object needs to be formatted to insert into the DB.

      So you should be able to specify an "inflate" method that parses the string in whatever format(s) you want your users to be able to provide as input, and specify a "deflate" method that stringifies using whatever format your DB wants the date to be in.

      The documentation of the "has_a" method has a good example of this.

        I may be getting this all wrong but here's my reasoning:

        • has_a sets triggers to inflate a value to an object on both 'select' and 'after_set_$column'.
        • The inflate method converts the nugget of data into an object.
        • In the 'select' case, the nugget comes from the database.
        • In the 'after_set_$col' case, the nugget comes from the user
        I want to use different formats for the user and the database. Maybe has_a could be part of the solution in combination with normalization (which happens prior to 'after_set_$col' and can do format conversion). But like I said, I don't think has_a alone is enough. I'd like to be wrong though.

        I found the docs quite confusing on this, even more so after I started trying to do it.

        The better answer seems to be just to override the accessor/mutator and leave the objects data in the MySQL format. The outside world sees it d/m/y and Class::DBI can ignore it. I should also be able to override all 'date' type accessors during class initialization automatically.

        (I'm not sure if this will play well with validation though).

        sub gp_ref_date { my $self = shift; if(@_) { # setting my $date = shift; if($date =~ m! (\d+) / (\d+) (?:/ (\d+) )+!x) { my $y = $3; $y += ($y < 70) ? 2000 : 1900 if($y < 100); $date = sprintf "%4d-%02d-%02d", $y,$2,$1; } return $self->_gp_ref_date_accessor($date); } else { my $date = $self->_gp_ref_date_accessor(@_); $date =~ s!(\d{4})-(\d\d)-(\d\d)!$3/$2/$1!; return $date; } }

      I guess I don't quite get what you are trying to do then. To parse a date into a Time::Piece object you can use the strptime:

      my $date = Time::Piece->strptime("01/02/2003", "%e/%B/%Y"); $t->created($date);

      Where $t is a Class::DBI object with a 'created' column. With the has_a relationship you pass an object to the mutator, not a string. If you pass a string to the mutator, it will try to use the inflate method which will expect your date string to be in MySQL format. So just pass it a Time::Piece object which is what it is after anyway.

      Now when you want to print it or retrieve it later on in the format you are after, you can use:

      $t->created->dmy('/');

      You don't have to worry about MySQL date formats at all, you just have to know how to create Time::Piece objects, and how to alter and access Time::Piece objects.

      If you want these formats to happen automatically, then write a couple of functions that do the work for you:

      sub parse_date { return Time::Piece->strptime(shift, "%e/%B/%Y"); } sub stringify_date { return shift->dmy('/'); }

      You could probably write a custom module that inherits from Time::Piece::MySQL and override the new and stringification subroutines with something like the above to make things a little more transparent.

      If I am still misunderstanding what you are trying to accomplish, perhaps you could write a quick code sample of how you would like to use dates with Class::DBI.

      - Cees

      Updated: Fixed a dumb little typo

        That's a good, clear explanation of what it going on.

        My issue is that I have customizable tables that I'm trying to handle generically and I wanted to avoid having to treat date columns differently. Either by calling methods or writing wrappers. I want the mutator to accept D/M/Y strings, even sloppy ones. Given all this, is has_a still what I'm after? Or is changing the accessor/mutator? (Or may I should just change my requirements)

        Also, this was partly an exercise in understanding Class::DBI for me. I didn't expect the inflate method to be used via the mutator.

        Thanks a lot anyway,
        Brad

        PS.You could probably write a custom module that inherits from Time::Piece::MySQL.
        Note that Time::Piece::MySQL doesn't actually inherit from Time::Piece. Time::Piece is a difficult base class.

SPOPS tack (was: Date conversion with Class::DBI)
by lachoy (Parson) on Jul 08, 2003 at 11:55 UTC
    FWIW this is how SPOPS does it as well: using the SPOPS::Tool::DateConvert module you can convert to/from DateTime, Time::Piece, and Class::Date objects, specifying the format of the date as it's pulled from the database as a strptime string in the metadata. I do like the shortcuts you've used in your example... maybe I'll have to swipe that :-)

    Chris
    M-x auto-bs-mode