Fix 'timestamp with time zone' columns for IC::DT inflation
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / InflateColumn / DateTime.pm
index afd118d..54e66f5 100644 (file)
@@ -3,6 +3,7 @@ package DBIx::Class::InflateColumn::DateTime;
 use strict;
 use warnings;
 use base qw/DBIx::Class/;
+use Carp::Clan qw/^DBIx::Class/;
 
 =head1 NAME
 
@@ -94,7 +95,7 @@ sub register_column {
 
   my $type;
 
-  for (qw/date datetime/) {
+  for (qw/date datetime timestamp/) {
     my $key = "inflate_${_}";
 
     next unless exists $info->{$key};
@@ -106,19 +107,22 @@ sub register_column {
 
   unless ($type) {
     $type = lc($info->{data_type});
-    $type = 'datetime' if ($type =~ /^timestamp/);
+    if ($type eq "timestamp with time zone" || $type eq "timestamptz") {
+      $type = "timestamp";
+      $info->{_ic_dt_method} ||= "timestamp_with_timezone";
+    }
   }
 
   my $timezone;
   if ( defined $info->{extra}{timezone} ) {
-    warn "Putting timezone into extra => { timezone => '...' } has been deprecated, ".
+    carp "Putting timezone into extra => { timezone => '...' } has been deprecated, ".
          "please put it directly into the columns definition.";
     $timezone = $info->{extra}{timezone};
   }
 
   my $locale;
   if ( defined $info->{extra}{locale} ) {
-    warn "Putting locale into extra => { locale => '...' } has been deprecated, ".
+    carp "Putting locale into extra => { locale => '...' } has been deprecated, ".
          "please put it directly into the columns definition.";
     $locale = $info->{extra}{locale};
   }
@@ -128,37 +132,24 @@ sub register_column {
 
   my $undef_if_invalid = $info->{datetime_undef_if_invalid};
 
-  if ($type eq 'datetime' || $type eq 'date') {
-    my ($parse, $format) = ("parse_${type}", "format_${type}");
-
-    # This assignment must happen here, otherwise Devel::Cycle treats
-    # the resulting deflator as a circular reference (go figure):
-    #
-    # Cycle #1
-    #     DBICTest::Schema A->{source_registrations} => %B
-    #     %B->{Event} => DBIx::Class::ResultSource::Table C
-    #     DBIx::Class::ResultSource::Table C->{_columns} => %D
-    #     %D->{created_on} => %E
-    #     %E->{_inflate_info} => %F
-    #     %F->{deflate} => &G
-    #     closure &G, $info => $H
-    #     $H => %E
-    #
-    my $floating_tz_ok;
+  if ($type eq 'datetime' || $type eq 'date' || $type eq 'timestamp') {
+    # This shallow copy of %info avoids t/52_cycle.t treating
+    # the resulting deflator as a circular reference.
+    my %info = ( '_ic_dt_method' => $type , %{ $info } );
+
     if (defined $info->{extra}{floating_tz_ok}) {
-      warn "Putting floating_tz_ok into extra => { floating_tz_ok => 1 } has been deprecated, ".
+      carp "Putting floating_tz_ok into extra => { floating_tz_ok => 1 } has been deprecated, ".
            "please put it directly into the columns definition.";
-      $floating_tz_ok = $info->{extra}{floating_tz_ok};
+      $info{floating_tz_ok} = $info->{extra}{floating_tz_ok};
     }
-    $floating_tz_ok = $info->{floating_tz_ok} if defined $info->{floating_tz_ok};
 
     $self->inflate_column(
       $column =>
         {
           inflate => sub {
             my ($value, $obj) = @_;
-            my $dt = eval { $obj->_datetime_parser->$parse($value); };
-            die "Error while inflating ${value} for ${column} on ${self}: $@"
+            my $dt = eval { $obj->_inflate_to_datetime( $value, \%info ) };
+            $self->throw_exception ("Error while inflating ${value} for ${column} on ${self}: $@")
               if $@ and not $undef_if_invalid;
             $dt->set_time_zone($timezone) if $timezone;
             $dt->set_locale($locale) if $locale;
@@ -167,21 +158,41 @@ sub register_column {
           deflate => sub {
             my ($value, $obj) = @_;
             if ($timezone) {
-                warn "You're using a floating timezone, please see the documentation of"
+                carp "You're using a floating timezone, please see the documentation of"
                   . " DBIx::Class::InflateColumn::DateTime for an explanation"
                   if ref( $value->time_zone ) eq 'DateTime::TimeZone::Floating'
-                      and not $floating_tz_ok
+                      and not $info{floating_tz_ok}
                       and not $ENV{DBIC_FLOATING_TZ_OK};
                 $value->set_time_zone($timezone);
                 $value->set_locale($locale) if $locale;
             }
-            $obj->_datetime_parser->$format($value);
+            $obj->_deflate_from_datetime( $value, \%info );
           },
         }
     );
   }
 }
 
+sub _flate_or_fallback
+{
+  my( $self, $value, $info, $method_fmt ) = @_;
+
+  my $parser = $self->_datetime_parser;
+  my $preferred_method = sprintf($method_fmt, $info->{ _ic_dt_method });
+  my $method = $parser->can($preferred_method) ? $preferred_method : sprintf($method_fmt, 'datetime');
+  return $parser->$method($value);
+}
+
+sub _inflate_to_datetime {
+  my( $self, $value, $info ) = @_;
+  return $self->_flate_or_fallback( $value, $info, 'parse_%s' );
+}
+
+sub _deflate_from_datetime {
+  my( $self, $value, $info ) = @_;
+  return $self->_flate_or_fallback( $value, $info, 'format_%s' );
+}
+
 sub _datetime_parser {
   my $self = shift;
   if (my $parser = $self->__datetime_parser) {