in reply to Re: Date conversion with Class::DBI
in thread Date conversion with Class::DBI

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.

Replies are listed 'Best First'.
Re: Re: Re: Date conversion with Class::DBI
by hossman (Prior) on Jul 07, 2003 at 06:24 UTC

    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; } }
Re: Re: Re: Date conversion with Class::DBI
by cees (Curate) on Jul 07, 2003 at 13:33 UTC

    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.

        Well, I can tell by your other responses that you are aiming for the 'change the mutator' option, but since you're still open for suggestions, I'll try to extend my suggestion to include your other requirements.

        Cheers,

        Cees

        ps I have enjoyed this endevour and thank you for the question. I have learned a bit more about has_a and what can be done with it, so even if you choose not to go this route, I am happy to have learned something myself :)