* How do I find yesterday's date?

   + Added a DateTime example
   + removed the lengthy discussion of daylight savings

* How do I validate input?

   + Rewrote this very short answer to mention several module
   names

* How do I expand function calls in a string?

   + I added an example using scalar context and an example
   that already returns a reference. I wanted to emphasize
   the notion that any code can go in the braces.

* How do I remove consecutive pairs of characters?

   + rewrote answer to explain code samples
   + changed y/// to tr///

*  How do I find matching/nesting anything?

   + changed y/// to tr///






Index: perlfaq4.pod
===================================================================
RCS file: /cvs/public/perlfaq/perlfaq4.pod,v
retrieving revision 1.62
diff -u -d -r1.62 perlfaq4.pod
--- perlfaq4.pod  7 Apr 2005 21:46:36 -0000  1.62
+++ perlfaq4.pod  17 Apr 2005 20:04:42 -0000
@@ -481,60 +481,30 @@
 
 =head2 How do I find yesterday's date?
 
-If you only need to find the date (and not the same time), you
-can use the Date::Calc module.
-
-  use Date::Calc qw(Today Add_Delta_Days);
-
-  my @date = Add_Delta_Days( Today(), -1 );
-
-  print "@date\n";
-
-Most people try to use the time rather than the calendar to
-figure out dates, but that assumes that your days are
-twenty-four hours each.  For most people, there are two days
-a year when they aren't: the switch to and from summer time
-throws this off. Russ Allbery offers this solution.
-
-    sub yesterday {
-     my $now  = defined $_[0] ? $_[0] : time;
-     my $then = $now - 60 * 60 * 24;
-     my $ndst = (localtime $now)[8] > 0;
-     my $tdst = (localtime $then)[8] > 0;
-     $then - ($tdst - $ndst) * 60 * 60;
-     }
-
-Should give you "this time yesterday" in seconds since epoch relative
to
-the first argument or the current time if no argument is given and
-suitable for passing to localtime or whatever else you need to do with
-it.  $ndst is whether we're currently in daylight savings time; $tdst
is
-whether the point 24 hours ago was in daylight savings time.  If $tdst
-and $ndst are the same, a boundary wasn't crossed, and the correction
-will subtract 0.  If $tdst is 1 and $ndst is 0, subtract an hour more
-from yesterday's time since we gained an extra hour while going off
-daylight savings time.  If $tdst is 0 and $ndst is 1, subtract a
-negative hour (add an hour) to yesterday's time since we lost an hour.
-
-All of this is because during those days when one switches off or onto
-DST, a "day" isn't 24 hours long; it's either 23 or 25.
-
-The explicit settings of $ndst and $tdst are necessary because
localtime
-only says it returns the system tm struct, and the system tm struct at
-least on Solaris doesn't guarantee any particular positive value (like,
-say, 1) for isdst, just a positive value.  And that value can
-potentially be negative, if DST information isn't available (this sub
-just treats those cases like no DST).
+(contributed by brian d foy)
 
-Note that between 2am and 3am on the day after the time zone switches
-off daylight savings time, the exact hour of "yesterday" corresponding
-to the current hour is not clearly defined.  Note also that if used
-between 2am and 3am the day after the change to daylight savings time,
-the result will be between 3am and 4am of the previous day; it's
-arguable whether this is correct.
+Use one of the Date modules. The C<DateTime> module makes it simple,
and
+give you the same time of day, only the day before.
 
-This sub does not attempt to deal with leap seconds (most things
don't).
+  use DateTime;
+  
+  my $yesterday = DateTime->now->subtract( days => 1 );
+  
+  print "Yesterday was $yesterday\n";
 
+You can also use the C<Date::Calc> module using its Today_and_Now
+function.
 
+  use Date::Calc qw( Today_and_Now Add_Delta_DHMS );
+  
+  my @date_time = Add_Delta_DHMS( Today_and_Now(), -1, 0, 0, 0 );
+  
+  print "@date\n";
+  
+Most people try to use the time rather than the calendar to figure out
+dates, but that assumes that days are twenty-four hours each.  For
+most people, there are two days a year when they aren't: the switch to
+and from summer time throws this off. Let the modules do the work.
 
 =head2 Does Perl have a Year 2000 problem?  Is Perl Y2K compliant?
 
@@ -570,9 +540,16 @@
 
 =head2 How do I validate input?
 
-The answer to this question is usually a regular expression, perhaps
-with auxiliary logic.  See the more specific questions (numbers, mail
-addresses, etc.) for details.
+(contributed by brian d foy)
+
+There are many ways to ensure that values are what you expect or
+want to accept. Besides the specific examples that we cover in the
+perlfaq, you can also look at the modules with "Assert" and "Validate"
+in their names, along with other modules such as C<Regexp::Common>.
+
+Some modules have validation for particular types of input, such
+as C<Business::ISBN>, C<Business::CreditCard>, C<Email::Valid>,
+and C<Data::Validate::IP>.
 
 =head2 How do I unescape a string?
 
@@ -586,21 +563,58 @@
 
 =head2 How do I remove consecutive pairs of characters?
 
-To turn C<"abbcccd"> into C<"abccd">:
+(contributed by brian d foy)
 
-    s/(.)\1/$1/g;   # add /s to include newlines
+You can use the substitution operator to find pairs of characters (or
+runs of characters) and replace them with a single instance. In this
+substitution, we find a character in C<(.)>. The memory parentheses
+store the matched character in the back-reference C<\1> and we use
+that to require that the same thing immediately follow it. We replace
+that part of the string with the character in C<$1>.
 
-Here's a solution that turns "abbcccd" to "abcd":
+    s/(.)\1/$1/g;
 
-    y///cs;   # y == tr, but shorter :-)
+We can also use the transliteration operator, C<tr///>. In this
+example, the search list side of our C<tr///> contains nothing, 
+but the C<c> option complements that so it contains everything.
+The replacement list also contains nothing, so the transliteration
+is almost a no-op since it won't do any replacements (or more
+exactly, replace the character with itself). However, the
+C<s> option squashes duplicated and consecutive characters in
+the string so a character does not show up next to itself
+
+  my $str = 'Haarlem';   # in the Netherlands
+    $str =~ tr///cs;       # Now Harlem, like in New York
 
 =head2 How do I expand function calls in a string?
 
-This is documented in L<perlref>.  In general, this is fraught with
-quoting and readability problems, but it is possible.  To interpolate
-a subroutine call (in list context) into a string:
+(contributed by brian d foy)
 
-    print "My sub returned @{[mysub(1,2,3)]} that time.\n";
+This is documented in L<perlref>, and although it's not the easiest
+thing to read, it does work. In each of these examples, we call the
+function inside the braces of used to dereference a reference. If we
+have a more than one return value, we can contruct and dereference an
+anonymous array. In this case, we call the function in list context.
+
+    print "The time values are @{ [localtime] }.\n";
+
+If we want to call the function in scalar context, we have to do a bit
+more work. We can really have any code we like inside the braces, so
+we simply have to end with the scalar reference.
+
+  print "The time is ${ my $x = localtime; \$x }.\n";
+  
+If your function already returns a reference, you don't need to create
+the reference yourself.
+
+  sub timestamp { my $t = localtime; \$t }
+  
+  print "The time is ${ timestamp() }.\n";
+  
+In most cases, it is probably easier to simply use string
+concatenation, which also forces scalar context.
+
+  print "The time is " . localtime . ".\n";
 
 =head2 How do I find matching/nesting anything?
 
@@ -1460,15 +1474,15 @@
 Use C<for>/C<foreach>:
 
     for (@lines) {
-  s/foo/bar/; # change that word
-  y/XZ/ZX/;   # swap those letters
+     s/foo/bar/; # change that word
+     tr/XZ/ZX/;  # swap those letters
     }
 
 Here's another; let's compute spherical volumes:
 
     for (@volumes = @radii) {   # @volumes has changed parts
-  $_ **= 3;
-  $_ *= (4/3) * 3.14159;  # this will be constant folded
+     $_ **= 3;
+     $_ *= (4/3) * 3.14159;  # this will be constant folded
     }
 
 which can also be done with map() which is made to transform
@@ -1482,7 +1496,7 @@
 case), you modify the value.
 
     for $orbit ( values %orbits ) {
-  ($orbit **= 3) *= (4/3) * 3.14159;
+     ($orbit **= 3) *= (4/3) * 3.14159;
     }
 
 Prior to perl 5.6 C<values> returned copies of the values,

-- 
brian d foy, [EMAIL PROTECTED]

Reply via email to