* 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]