#!/usr/bin/perl -wT # -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*- # # date-01: Date Controls Demo # # Copyright (c) 2002 by Ian Hickson # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use utf8; use CGI; use POSIX qw(strftime mktime); my $query = new CGI; local $ENV{TZ} = 'UTC+0'; print < Date Controls Demo 01: Results end my $tz = parseFloat($query->param('timezone')) || 0; my $t1 = eval { parseDateTime($query->param('t1'), $tz); }; my $e1 = $@; my $t2 = eval { parseDateTime($query->param('t2'), $tz); }; my $e2 = $@; if (not $e1 and not $e2 and defined $t1 and defined $t2) { my $difference = $t2 - $t1; my $s = $difference == 1 ? '' : 's'; $t1 = prettyPrint($t1); $t2 = prettyPrint($t2); print "

The two points in time you gave are $difference second$s apart.

\n"; print "

First point: $t1

\n"; print "

Second point: $t2

\n"; } else { print "

First date and time: $e1

" if $e1; # make sure exceptions from parseDateTime can never include < or & characters. print "

Second date and time: $e2

" if $e2; print "

You must enter both dates.

" unless $query->param('t1') and $query->param('t2'); print "

The format for the times is DD/MM/YYYY HH:MM. Press the \"back\" button to change what you entered and try again.

\n"; } my $nowThere = parseFloat($query->param('now')) || 0; if ($nowThere and abs(time - $nowThere) > 1000) { print "

Note: Your clock is probably wrong.

\n"; } print < end sub parseFloat { my($data) = @_; return undef unless defined $data; if ($data =~ m/^[0-9]+(?:\.[0-9]+)?$/os) { return 0+$data; } return undef; } sub prettyPrint { my($data) = @_; my $s = strftime("%Y-%m-%dT%H:%M:%S", gmtime(int $data)); if (int $data < $data) { $s .= '.'; $s .= substr(($data - int $data), 2); } return $s . 'Z'; } sub daysInMonth { my($month, $year) = @_; if ($month == 2) { if (($year % 4 == 0 and $year % 100 != 0) or ($year % 400 == 0)) { return 29; } return 28; } my @months = (31, undef, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); return $months[$month-1]; } sub parseDateTime { my($data, $tz) = @_; my($year, $month, $date, $hours, $minutes, $seconds, $ampm); my %months = ('jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12); if ($data =~ m/^ ([0-9]{4,}) # Year - ([0-9]{2}) # Month - ([0-9]{2}) # Date T ([0-9]{2}) # Hours : ([0-9]{2}) # Minutes (?: : ([0-9]{2} # Seconds (optional) (?: . [0-9]+ )?) # Fraction of a second (optional) )? Z $/osx) { # Timezone Z # WF2-compliant (ISO8601) datetime. # warn "'$data' is an ISO8601-style datetime\n"; $year = $1; $month = $2; $date = $3; $hours = $4; $minutes = $5; $seconds = $6 || 0; $tz = 0; } elsif ($data =~ m/^ \s* ([0-9]{4,}) # Year \s* ([-\/.]) # Separator \s* ([0-9]{1,2}) # Month \s* \2 # Same Separator \s* ([0-9]{1,2}) # Date \s+ ([0-9]{1,2}) # Hours \s* : \s* ([0-9]{1,2}) # Minutes (?: \s* : \s* ([0-9]{1,2} # Seconds (optional) (?: . [0-9]+ )?) # Fraction of a second (optional) )? \s* (am?|pm?)? \s* $/osix) { # Similar, but looser # warn "'$data' is a type-1 datetime (year first)\n"; $year = $1; $month = $3; $date = $4; $hours = $5; $minutes = $6; $seconds = $7 || 0; $ampm = $8; } elsif ($data =~ m/^ \s* ([0-9]{1,2}) # Date or Month \s* ([-\/.]) # Separator \s* ([0-9]{1,2}) # Month or Date \s* \2 # Same Separator \s* ([0-9]+) # Year \s+ ([0-9]{1,2}) # Hours \s* : \s* ([0-9]{1,2}) # Minutes (?: \s* : \s* ([0-9]{1,2} # Seconds (optional) (?: . [0-9]+ )?) # Fraction of a second (optional) )? \s* (am?|pm?)? \s* $/osix) { # dates like DD/MM/YY, DD-MM-YYYY, D.M.Y # the format requested by the page is DD/MM/YYYY HH:MM # warn "'$data' is a type-2 datetime (year last)\n"; $year = $4; $month = $3; $date = $1; $hours = $5; $minutes = $6; $seconds = $7 || 0; $ampm = $8; } elsif ($data =~ m/^ \s* ([0-9]{4,}) # Year \s* ([-\/.]|\s+) # Separator \s* ([a-z][a-z][a-z])[a-z]* # Month name \.? \s* (?:\2|\s+)? # Same Separator (?:the)? # \s* ([0-9]{1,2}) # Date (?:st|nd|rd|th)? # Suffix \s+ ([0-9]{1,2}) # Hours \s* : \s* ([0-9]{1,2}) # Minutes (?: \s* : \s* ([0-9]{1,2} # Seconds (optional) (?: . [0-9]+ )?) # Fraction of a second (optional) )? \s* (am?|pm?)? \s* $/osix) { # For "YY MMM. DD" style dates, where MMM is a string # warn "'$data' is a type-3 datetime (year first with text)\n"; $year = $1; $month = $months{lc $3}; die "The word '$3' does not seem to be a month name.\n" unless defined $month; $date = $4; $hours = $5; $minutes = $6; $seconds = $7 || 0; $ampm = $8; } elsif ($data =~ m/^ \s* ([0-9]{1,2}) # Date (?:st|nd|rd|th)? # Suffix (?:of)? # \s* ([-\/.]|\s+) # Separator \s* ([a-z][a-z][a-z])[a-z]* # Month name \.? \s* (?:\2|\s+)? # Same Separator \s* ([0-9]+) # Year \s+ ([0-9]{1,2}) # Hours \s* : \s* ([0-9]{1,2}) # Minutes (?: \s* : \s* ([0-9]{1,2} # Seconds (optional) (?: . [0-9]+ )?) # Fraction of a second (optional) )? \s* (am?|pm?)? \s* $/osix) { # For "DD MMM. YY" style dates, where MMM is a string # warn "'$data' is a type-4 datetime (year last with text)\n"; $year = $4; $month = $months{lc $3}; die "The word '$3' does not seem to be a month name.\n" unless defined $month; $date = $1; $hours = $5; $minutes = $6; $seconds = $7 || 0; $ampm = $8; } else { warn "Couldn't parse '$data' as a datetime.\n"; die "The string '$data' does not seem to specify a date and a time.\n" if length($data); return undef; } if ($month > 12) { # swap date and month if month is out of range # warn '(but swapping month and date)\n'; ($date, $month) = ($month, $date); } if (length($year) <= 2) { if ($year < 30) { # 2000 to 2029 can be written with just two digits $year += 2000; } else { # 1930 to 1999 can be written with just two digits $year += 1900; } } if (defined $ampm and $ampm =~ m/p/osi and $hours < 12) { $hours += 12; } if ($hours == 24) { $hours = 0; $tz -= 24; # one day in hours } die "The year you entered ($year) is less than 1902. Years must be in the range 1902 to 2037.\n" unless $year >= 1902; die "The year you entered ($year) is more than 2037. Years must be in the range 1902 to 2037.\n" unless $year <= 2037; die "The month you entered ($month) is less than 1. Months must be in the range 1 to 12.\n" unless $month > 0; die "The month you entered ($month) is more than 12. Months must be in the range 1 to 12.\n" unless $month <= 12; my $n = daysInMonth($month, $year); die "The date you entered ($date) is less than 1. Dates in month $month of year $year must be in the range 1 to $n.\n" unless $date > 0; die "The date you entered ($date) is more than $n. Dates in month $month of year $year must be in the range 1 to $n.\n" unless $date <= $n; die "The hour you entered ($hours) is less than 0. Hours must be in the range 0 to 23.\n" unless $hours >= 0; die "The hour you entered ($hours) is more than 23. Hours must be in the range 0 to 23.\n" unless $hours <= 23; die "The minute you entered ($minutes) is less than 0. Minutes must be in the range 0 to 59.\n" unless $minutes >= 0; die "The minute you entered ($minutes) is more than 59. Minutes must be in the range 0 to 59.\n" unless $minutes <= 59; die "The second you entered ($seconds) is less than 0. Seconds must be in the range 0 to 59.\n" unless $seconds >= 0; die "The second you entered ($seconds) is more than 59. Seconds must be in the range 0 to 60 (exclusive).\n" unless $seconds < 60; my $time = mktime(0, $minutes, $hours, $date, $month - 1, $year - 1900); $time -= $tz * 60 * 60; # $tz is in hours $time += $seconds; my $t = prettyPrint($time); # warn "Got $t from $year-$month-$date $hours:$minutes:$seconds from '$data' [time=$time; tz=$tz]\n"; return $time; }