Index: /trunk/PS-IPP-Metadata-Config/Changes
===================================================================
--- /trunk/PS-IPP-Metadata-Config/Changes	(revision 9757)
+++ /trunk/PS-IPP-Metadata-Config/Changes	(revision 9758)
@@ -3,4 +3,8 @@
 0.09
     - fix a bug in Build.PL where a rebuilt grammar was not installed
+    - fix time tests that were dependent on MULTI times being broken
+    - fix MULTI time
+    - no longer require the 'Z' and the end of ISO8601 times
+    - implement NULL time support
 
 0.08 Wed Oct 11 16:09:26 HST 2006
Index: /trunk/PS-IPP-Metadata-Config/config_grammar.txt
===================================================================
--- /trunk/PS-IPP-Metadata-Config/config_grammar.txt	(revision 9757)
+++ /trunk/PS-IPP-Metadata-Config/config_grammar.txt	(revision 9758)
@@ -1,5 +1,5 @@
 # Copyright (c) 2005  Joshua Hoblitt
 #
-# $Id: config_grammar.txt,v 1.6 2006-10-12 02:08:51 jhoblitt Exp $
+# $Id: config_grammar.txt,v 1.7 2006-10-27 02:15:45 jhoblitt Exp $
 
 {
@@ -146,4 +146,11 @@
             $return->{comment} = $item{'comment(?)'}[0]
                 if $item{'comment(?)'}[0];
+
+            if ( defined $thisparser->{local}{name}{ $item{name} } ) {
+                $return->{multi}++
+                    if $thisparser->{local}{name}{ $item{name} } =~ /MULTI/;
+            } else {
+                $thisparser->{local}{name}{ $item{name} }++;
+            }
         }
 
@@ -362,20 +369,24 @@
     iso8601
     | utc_epoch
+    | NULL
 
 UT1:
     iso8601
     | epoch
+    | NULL
 
 TAI:
     iso8601
     | epoch
+    | NULL
 
 TT:
     iso8601
     | epoch
+    | NULL
 
 iso8601:
     # based on code from DateTime::Format::ISO8601
-    / \d{4} -?? \d\d -?? \d\d T?? \d\d :?? \d\d :?? \d\d (?:[\.,] (\d+))? Z/x
+    / \d{4} -?? \d\d -?? \d\d T?? \d\d :?? \d\d :?? \d\d (?:[\.,] (\d+))? Z?/x
         { DateTime::Format::ISO8601->parse_datetime( $item[1] ) }
 
@@ -394,4 +405,7 @@
             nsec    => $2,
         }}
+NULL:
+    /NULL/
+    { $return = "NULL" }
 
 eofile:
Index: /trunk/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/Config.pm
===================================================================
--- /trunk/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/Config.pm	(revision 9757)
+++ /trunk/PS-IPP-Metadata-Config/lib/PS/IPP/Metadata/Config.pm	(revision 9758)
@@ -1,5 +1,5 @@
 # Copyright (c) 2005  Joshua Hoblitt
 #
-# $Id: Config.pm,v 1.26 2006-10-12 02:09:10 jhoblitt Exp $
+# $Id: Config.pm,v 1.27 2006-10-27 02:15:45 jhoblitt Exp $
 
 package PS::IPP::Metadata::Config;
@@ -49,4 +49,8 @@
     # multi-symbols
     $self->_merge_duplicates( $tree ) or return undef;
+
+    # translate NULL values into perl undefs (Parse::RecDescent can't return
+    # undefs, args)
+    $self->_fix_nulls( $tree ) or return undef;
 
     #print Dumper($tree);
@@ -106,4 +110,27 @@
 }
 
+sub _fix_nulls {
+    my ( $self, $tree ) = @_;
+
+    # Iteratate through the parse tree looking for values of NULL and
+    # translating them into Perl undefs
+    for (my $i = 0; $i < @{$tree}; $i++) {
+        my $elem = $tree->[$i];
+
+        # recurse through nested metadata
+        if ( $elem->{class} eq "metadata" ) {
+            $self->_fix_nulls( $elem->{value} );
+        }
+
+        # force stringification of $elem->{value} -- specifically because you
+        # can't compare a DateTime object to a string value
+        if ("$elem->{value}" eq "NULL") {
+            $elem->{value} = undef;
+        }
+    }
+
+    return 1;
+}
+
 1;
 
Index: /trunk/PS-IPP-Metadata-Config/t/05_time.t
===================================================================
--- /trunk/PS-IPP-Metadata-Config/t/05_time.t	(revision 9757)
+++ /trunk/PS-IPP-Metadata-Config/t/05_time.t	(revision 9758)
@@ -3,5 +3,5 @@
 # Copyright (C) 2005  Joshua Hoblitt
 #
-# $Id: 05_time.t,v 1.3 2006-09-25 21:49:42 jhoblitt Exp $
+# $Id: 05_time.t,v 1.4 2006-10-27 02:15:45 jhoblitt Exp $
 
 use strict;
@@ -12,5 +12,5 @@
 #$::RD_TRACE = 1;
 
-use Test::More tests => 10;
+use Test::More tests => 17;
 use PS::IPP::Metadata::Config;
 
@@ -19,4 +19,135 @@
 {
 my $example =<<END;
+mytime  MULTI
+mytime  UTC     NULL
+mytime  UT1     NULL
+mytime  TAI     NULL
+mytime  TT      NULL
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "NULL times parsed");
+
+    my $tree = [
+        {
+            name    => 'mytime',
+            class   => 'time',
+            type    => 'UTC',
+            value   => undef,
+        },
+        {
+            name    => 'mytime',
+            class   => 'time',
+            type    => 'UT1',
+            value   => undef,
+        },
+        {
+            name    => 'mytime',
+            class   => 'time',
+            type    => 'TAI',
+            value   => undef,
+        },
+        {
+            name    => 'mytime',
+            class   => 'time',
+            type    => 'TT',
+            value   => undef,
+        },
+    ];
+
+    is_deeply( $config, $tree, "NULL times structure" );
+}
+
+{
+my $example =<<END;
+mytime  MULTI
+mytime  UTC     NULL    # comment
+mytime  UT1     NULL    # comment
+mytime  TAI     NULL    # comment
+mytime  TT      NULL    # comment
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "NULL times with comments parsed");
+
+    my $tree = [
+        {
+            name    => 'mytime',
+            class   => 'time',
+            type    => 'UTC',
+            value   => undef,
+            comment => 'comment',
+        },
+        {
+            name    => 'mytime',
+            class   => 'time',
+            type    => 'UT1',
+            value   => undef,
+            comment => 'comment',
+        },
+        {
+            name    => 'mytime',
+            class   => 'time',
+            type    => 'TAI',
+            value   => undef,
+            comment => 'comment',
+        },
+        {
+            name    => 'mytime',
+            class   => 'time',
+            type    => 'TT',
+            value   => undef,
+            comment => 'comment',
+        },
+    ];
+
+    is_deeply( $config, $tree, "NULL times with comments structure" );
+}
+
+{
+my $example =<<END;
+recently    MULTI
+recently    UTC     2005-03-18T16:05:00
+recently    UT1     2005-03-18T16:05:00
+recently    TAI     2005-03-18T16:05:00
+recently    TT      2005-03-18T16:05:00
+END
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "basic ISO8601 parsed");
+
+    my $tree = [
+        {
+            name    => 'recently',
+            class   => 'time',
+            type    => 'UTC',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'time',
+            type    => 'UT1',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'time',
+            type    => 'TAI',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'time',
+            type    => 'TT',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+    ];
+
+    is_deeply( $config, $tree, "basic ISO8601 structure" );
+}
+
+{
+my $example =<<END;
+recently    MULTI
 recently    UTC     2005-03-18T16:05:00Z
 recently    UT1     2005-03-18T16:05:00Z
@@ -24,10 +155,41 @@
 recently    TT      2005-03-18T16:05:00Z
 END
-    my $config = $config_parser->parse( $example );
-    ok( defined( $config ), "basic IS8601");
-}
-
-{
-my $example =<<END;
+
+    my $config = $config_parser->parse( $example );
+    ok( defined( $config ), "basic ISO8601 w/Z parsed");
+
+    my $tree = [
+        {
+            name    => 'recently',
+            class   => 'time',
+            type    => 'UTC',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'time',
+            type    => 'UT1',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'time',
+            type    => 'TAI',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+        {
+            name    => 'recently',
+            class   => 'time',
+            type    => 'TT',
+            value   => DateTime::Format::ISO8601->parse_datetime("2005-03-18T16:05:00Z"),
+        },
+    ];
+
+    is_deeply( $config, $tree, "basic ISO8601 w/Z structure" );
+}
+
+{
+my $example =<<END;
+recently    MULTI
 recently    UTC     2005-03-18T16:05:00.000001Z
 recently    UT1     2005-03-18T16:05:00.000001Z
@@ -36,9 +198,10 @@
 END
     my $config = $config_parser->parse( $example );
-    ok( defined( $config ), "basic IS8601 with fractional seconds");
-}
-
-{
-my $example =<<END;
+    ok( defined( $config ), "basic IS8601 with fractional seconds parsed");
+}
+
+{
+my $example =<<END;
+recently    MULTI
 recently    UTC     2005-03-18T16:05:00Z    # foo
 recently    UT1     2005-03-18T16:05:00Z    # bar
@@ -47,9 +210,10 @@
 END
     my $config = $config_parser->parse( $example );
-    ok( defined( $config ), "ISO8601 with comments");
-}
-
-{
-my $example =<<END;
+    ok( defined( $config ), "ISO8601 with comments parsed");
+}
+
+{
+my $example =<<END;
+recently    MULTI
 recently    UTC     2005-03-18T16:05:00.000001Z    # foo
 recently    UT1     2005-03-18T16:05:00.000001Z    # bar
@@ -58,9 +222,12 @@
 END
     my $config = $config_parser->parse( $example );
-    ok( defined( $config ), "ISO8601 with comments and fractional seconds");
-}
-
-{
-my $example =<<END;
+    ok( defined( $config ), "ISO8601 with comments and fractional seconds parsed");
+}
+
+diag("epoch time format maybe deprecated");
+
+{
+my $example =<<END;
+recently    MULTI
 recently    UTC     123456, 5000, 1
 recently    UT1     123456, 5000
@@ -69,9 +236,10 @@
 END
     my $config = $config_parser->parse( $example );
-    ok( defined( $config ), "basic epoch time");
-}
-
-{
-my $example =<<END;
+    ok( defined( $config ), "basic epoch time parsed");
+}
+
+{
+my $example =<<END;
+recently    MULTI
 recently    UTC     123456, 5000, 1         # foo
 recently    UT1     123456, 5000            # bar
@@ -80,36 +248,36 @@
 END
     my $config = $config_parser->parse( $example );
-    ok( defined( $config ), "epoch time with comments");
-}
-
-{
-my $example =<<END;
-broken      UTC     2005-03-18T16:05:00
-END
-    my $config = $config_parser->parse( $example );
-    ok( !defined( $config ), "bad format");
-}
-
-{
-my $example =<<END;
-broken      UT1     2005-03-18T16:05:00
-END
-    my $config = $config_parser->parse( $example );
-    ok( !defined( $config ), "bad format");
-}
-
-{
-my $example =<<END;
-broken      TAI     2005-03-18T16:05:00
-END
-    my $config = $config_parser->parse( $example );
-    ok( !defined( $config ), "bad format");
-}
-
-{
-my $example =<<END;
-broken      TT      2005-03-18T16:05:00
-END
-    my $config = $config_parser->parse( $example );
-    ok( !defined( $config ), "bad format");
-}
+    ok( defined( $config ), "epoch time with comments parsed");
+}
+
+{
+my $example =<<END;
+broken      UTC     2005-03-18 16:05:00
+END
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "bad format");
+}
+
+{
+my $example =<<END;
+broken      UT1     2005-03-18 16:05:00
+END
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "bad format");
+}
+
+{
+my $example =<<END;
+broken      TAI     2005-03-18 16:05:00
+END
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "bad format");
+}
+
+{
+my $example =<<END;
+broken      TT      2005-03-18 16:05:00
+END
+    my $config = $config_parser->parse( $example );
+    ok( !defined( $config ), "bad format");
+}
