/
ch-1.pl
executable file
·129 lines (107 loc) · 3.8 KB
/
ch-1.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/perl
use strict;
use warnings;
use English;
################################################################################
# Begin main execution
################################################################################
my @dates = (
[ 2024, 4, 3, 2 ],
[ 2025, 10, 2, 4 ],
[ 2026, 8, 5, 3 ],
);
print("\n");
foreach my $date (@dates){
printf(
"Input: Year = %4d, Month = %2d, Weekday of month = %2d, " .
"day of week = %1d\nOutput: %d\n\n",
@{$date},
determine_date_from_week_count(@{$date})
);
}
exit(0);
################################################################################
# End main execution; subroutines follow
################################################################################
################################################################################
# Determine what day of the month a day occurs when specified by year, month,
# day of week, and week count within the specified month
# Takes four arguments:
# * The specified year
# * The specified month (1-12 --> January-December)
# * The week within the month (1-5)
# * The specified day of the week (1-7 --> Mon-Sun)
# Returns on success:
# * The date of the specified day in the specified week within the specified
# month
# Returns on error:
# * 0 if the specified day within the specified week is not actually within the
# specified month
# * undef if the specified arguments define an invalid date or the calcualted
# date is outside the range of the Gregorian calendar
################################################################################
sub determine_date_from_week_count{
use Time::Local;
# Seconds per day
use constant SPD => 86400;
# Sanity checks
return(undef)
if(
# Test for valid month
($ARG[1] < 1) || ($ARG[1] > 12)
||
# Test for valid week in month
($ARG[2] < 1) || ($ARG[2] > 5)
||
# Test for valid day of week
($ARG[3] < 1) || ($ARG[3] > 7)
||
# Test whether we have a date roughly within
# the Gregorian calendar; calculated date will
# be confirmed later
(($ARG[0] < 1582) || (($ARG[0] == 1582) && ($ARG[1] < 10)))
);
# Switch Sunday from 7 to 0 to match the values
# used by the time functions
my $day_spec = $ARG[3] == 7 ? 0 : $ARG[3];
my $day_first;
my $month;
# Start with noon on the first day of the month
my $time = timegm(
0, 0, 12, 1, $ARG[1] - 1, $ARG[0]
);
# Determine what day of the week the first is
(undef, undef, undef, undef, undef, undef, $day_first, undef, undef) =
gmtime($time);
# Determine the when the first instance of the
# specified day of the week occurs and adjust
# $time accordingly
if($day_spec < $day_first){
# It's in the second week
$time += SPD * ((6 - $day_first) + ($day_spec + 1));
} else{
# It's in the first week
$time += SPD * ($day_spec - $day_first);
}
# Skip ahead the specified number of weeks
$time += SPD * 7 * ($ARG[2] - 1);
# Determine the month and day associated with
# the calculated time...
(undef, undef, undef, $day_spec, $month, undef, undef, undef, undef) =
gmtime($time);
# See if the calculated date is in the
# specified month:
if($month == ($ARG[1] - 1)){
# It is...
# Final Gregorian calendar sanity check on
# the calculated date (years and months
# before this have already been filtered out)
return(undef)
if(($ARG[0] == 1582) && ($ARG[0] == 10) && ($day_spec < 15));
# Return the calculated day
return($day_spec);
} else{
# It isn't; return 0
return(0);
}
}