=head1 NAME
Date::Calc - Gregorian calendar date calculations
=head1 MOTTO
Keep it small, fast and simple
=head1 PREFACE
This
package
consists of a C library and a Perl module (which uses
the C library, internally)
for
all kinds of date calculations based
on the Gregorian calendar (the one used in all western countries today),
thereby complying
with
all relevant norms and standards: S<ISO/R 2015-1971>,
S<DIN 1355> and, to some extent, S<ISO 8601> (where applicable).
for
a scan of part of the
"S<DIN 1355>"
document (in German)).
The module of course handles year numbers of 2000 and above correctly
(
"Year 2000"
or
"Y2K"
compliance) -- actually all year numbers from 1
to the largest positive integer representable on your
system
(which
is at least 32767) can be dealt
with
.
This is not true, however,
for
the
import
/export functions in this
package
which are an interface to the internal POSIX date and
time
functions of your
system
, which can only cover dates in the following
ranges:
01-Jan-1970 00:00:00 GMT .. 19-Jan-2038 03:14:07 GMT [Unix etc.]
01-Jan-1904 00:00:00 LT .. 06-Feb-2040 06:28:15 LT [MacOS Classic]
(LT =
local
time
)
Note that this
package
projects the Gregorian calendar back
until
the
year S<1 A.D.> -- even though the Gregorian calendar was only adopted
in 1582, mostly by the Catholic European countries, in obedience to the
corresponding decree of Pope S<Gregory XIII> in that year.
Some (mainly protestant) countries continued to
use
the Julian calendar
(used
until
then)
until
as late as the beginning of the 20th century.
Finally, note that this
package
is not intended to
do
everything you could
ever imagine automagically
for
you; it is rather intended to serve as a
toolbox (in the best of UNIX spirit and traditions) which should, however,
always get you where you want to go.
See the section
"RECIPES"
at the bottom of this document
for
solutions
to common problems!
If nevertheless you can't figure out how to solve a particular problem,
please let me know! (See e-mail address at the end of this document.)
=head1 SYNOPSIS
Days_in_Year
Days_in_Month
Weeks_in_Year
leap_year
check_date
check_time
check_business_date
Day_of_Year
Date_to_Days
Day_of_Week
Week_Number
Week_of_Year
Monday_of_Week
Nth_Weekday_of_Month_Year
Standard_to_Business
Business_to_Standard
Delta_Days
Delta_DHMS
Delta_YMD
Delta_YMDHMS
N_Delta_YMD
N_Delta_YMDHMS
Normalize_DHMS
Add_Delta_Days
Add_Delta_DHMS
Add_Delta_YM
Add_Delta_YMD
Add_Delta_YMDHMS
Add_N_Delta_YMD
Add_N_Delta_YMDHMS
System_Clock
Today
Now
Today_and_Now
This_Year
Gmtime
Localtime
Mktime
Timezone
Date_to_Time
Time_to_Date
Easter_Sunday
Decode_Month
Decode_Day_of_Week
Decode_Language
Decode_Date_EU
Decode_Date_US
Fixed_Window
Moving_Window
Compress
Uncompress
check_compressed
Compressed_to_Text
Date_to_Text
Date_to_Text_Long
English_Ordinal
Calendar
Month_to_Text
Day_of_Week_to_Text
Day_of_Week_Abbreviation
Language_to_Text
Language
Languages
Decode_Date_EU2
Decode_Date_US2
Parse_Date
ISO_LC
ISO_UC
)
;
Days_in_Year
$days
= Days_in_Year(
$year
,
$month
);
Days_in_Month
$days
= Days_in_Month(
$year
,
$month
);
Weeks_in_Year
$weeks
= Weeks_in_Year(
$year
);
leap_year
if
(leap_year(
$year
))
check_date
if
(check_date(
$year
,
$month
,
$day
))
check_time
if
(check_time(
$hour
,
$min
,
$sec
))
check_business_date
if
(check_business_date(
$year
,
$week
,
$dow
))
Day_of_Year
$doy
= Day_of_Year(
$year
,
$month
,
$day
);
Date_to_Days
$days
= Date_to_Days(
$year
,
$month
,
$day
);
Day_of_Week
$dow
= Day_of_Week(
$year
,
$month
,
$day
);
Week_Number
$week
= Week_Number(
$year
,
$month
,
$day
);
Week_of_Year
(
$week
,
$year
) = Week_of_Year(
$year
,
$month
,
$day
);
$week
= Week_of_Year(
$year
,
$month
,
$day
);
Monday_of_Week
(
$year
,
$month
,
$day
) = Monday_of_Week(
$week
,
$year
);
Nth_Weekday_of_Month_Year
if
((
$year
,
$month
,
$day
) =
Nth_Weekday_of_Month_Year(
$year
,
$month
,
$dow
,
$n
))
Standard_to_Business
(
$year
,
$week
,
$dow
) =
Standard_to_Business(
$year
,
$month
,
$day
);
Business_to_Standard
(
$year
,
$month
,
$day
) =
Business_to_Standard(
$year
,
$week
,
$dow
);
Delta_Days
$Dd
= Delta_Days(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
);
Delta_DHMS
(
$Dd
,
$Dh
,
$Dm
,
$Ds
) =
Delta_DHMS(
$year1
,
$month1
,
$day1
,
$hour1
,
$min1
,
$sec1
,
$year2
,
$month2
,
$day2
,
$hour2
,
$min2
,
$sec2
);
Delta_YMD
(
$Dy
,
$Dm
,
$Dd
) =
Delta_YMD(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
);
Delta_YMDHMS
(
$D_y
,
$D_m
,
$D_d
,
$Dh
,
$Dm
,
$Ds
) =
Delta_YMDHMS(
$year1
,
$month1
,
$day1
,
$hour1
,
$min1
,
$sec1
,
$year2
,
$month2
,
$day2
,
$hour2
,
$min2
,
$sec2
);
N_Delta_YMD
(
$Dy
,
$Dm
,
$Dd
) =
N_Delta_YMD(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
);
N_Delta_YMDHMS
(
$D_y
,
$D_m
,
$D_d
,
$Dhh
,
$Dmm
,
$Dss
) =
N_Delta_YMDHMS(
$year1
,
$month1
,
$day1
,
$hour1
,
$min1
,
$sec1
,
$year2
,
$month2
,
$day2
,
$hour2
,
$min2
,
$sec2
);
Normalize_DHMS
(
$Dd
,
$Dh
,
$Dm
,
$Ds
) =
Normalize_DHMS(
$Dd
,
$Dh
,
$Dm
,
$Ds
);
Add_Delta_Days
(
$year
,
$month
,
$day
) =
Add_Delta_Days(
$year
,
$month
,
$day
,
$Dd
);
Add_Delta_DHMS
(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) =
Add_Delta_DHMS(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$Dd
,
$Dh
,
$Dm
,
$Ds
);
Add_Delta_YM
(
$year
,
$month
,
$day
) =
Add_Delta_YM(
$year
,
$month
,
$day
,
$Dy
,
$Dm
);
Add_Delta_YMD
(
$year
,
$month
,
$day
) =
Add_Delta_YMD(
$year
,
$month
,
$day
,
$Dy
,
$Dm
,
$Dd
);
Add_Delta_YMDHMS
(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) =
Add_Delta_YMDHMS(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$D_y
,
$D_m
,
$D_d
,
$Dh
,
$Dm
,
$Ds
);
Add_N_Delta_YMD
(
$year
,
$month
,
$day
) =
Add_N_Delta_YMD(
$year
,
$month
,
$day
,
$Dy
,
$Dm
,
$Dd
);
Add_N_Delta_YMDHMS
(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) =
Add_N_Delta_YMDHMS(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$D_y
,
$D_m
,
$D_d
,
$Dhh
,
$Dmm
,
$Dss
);
System_Clock
(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$doy
,
$dow
,
$dst
) =
System_Clock([
$gmt
]);
Today
(
$year
,
$month
,
$day
) = Today([
$gmt
]);
Now
(
$hour
,
$min
,
$sec
) = Now([
$gmt
]);
Today_and_Now
(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) = Today_and_Now([
$gmt
]);
This_Year
$year
= This_Year([
$gmt
]);
Gmtime
(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$doy
,
$dow
,
$dst
) =
Gmtime([
time
]);
Localtime
(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$doy
,
$dow
,
$dst
) =
Localtime([
time
]);
Mktime
$time
= Mktime(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
);
Timezone
(
$D_y
,
$D_m
,
$D_d
,
$Dh
,
$Dm
,
$Ds
,
$dst
) = Timezone([
time
]);
Date_to_Time
$time
= Date_to_Time(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
);
Time_to_Date
(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) = Time_to_Date([
time
]);
Easter_Sunday
(
$year
,
$month
,
$day
) = Easter_Sunday(
$year
);
Decode_Month
if
(
$month
= Decode_Month(
$string
[,
$lang
]))
Decode_Day_of_Week
if
(
$dow
= Decode_Day_of_Week(
$string
[,
$lang
]))
Decode_Language
if
(
$lang
= Decode_Language(
$string
))
Decode_Date_EU
if
((
$year
,
$month
,
$day
) = Decode_Date_EU(
$string
[,
$lang
]))
Decode_Date_US
if
((
$year
,
$month
,
$day
) = Decode_Date_US(
$string
[,
$lang
]))
Fixed_Window
$year
= Fixed_Window(
$yy
);
Moving_Window
$year
= Moving_Window(
$yy
);
Compress
$date
= Compress(
$year
,
$month
,
$day
);
Uncompress
if
((
$century
,
$year
,
$month
,
$day
) = Uncompress(
$date
))
check_compressed
if
(check_compressed(
$date
))
Compressed_to_Text
$string
= Compressed_to_Text(
$date
[,
$lang
]);
Date_to_Text
$string
= Date_to_Text(
$year
,
$month
,
$day
[,
$lang
]);
Date_to_Text_Long
$string
= Date_to_Text_Long(
$year
,
$month
,
$day
[,
$lang
]);
English_Ordinal
$string
= English_Ordinal(
$number
);
Calendar
$string
= Calendar(
$year
,
$month
[,
$orthodox
[,
$lang
]]);
Month_to_Text
$string
= Month_to_Text(
$month
[,
$lang
]);
Day_of_Week_to_Text
$string
= Day_of_Week_to_Text(
$dow
[,
$lang
]);
Day_of_Week_Abbreviation
$string
= Day_of_Week_Abbreviation(
$dow
[,
$lang
]);
Language_to_Text
$string
= Language_to_Text(
$lang
);
Language
$lang
= Language();
Language(
$lang
);
$oldlang
= Language(
$newlang
);
Languages
$max_lang
= Languages();
Decode_Date_EU2
if
((
$year
,
$month
,
$day
) = Decode_Date_EU2(
$string
[,
$lang
]))
Decode_Date_US2
if
((
$year
,
$month
,
$day
) = Decode_Date_US2(
$string
[,
$lang
]))
Parse_Date
if
((
$year
,
$month
,
$day
) = Parse_Date(
$string
[,
$lang
]))
ISO_LC
$lower
= ISO_LC(
$string
);
ISO_UC
$upper
= ISO_UC(
$string
);
Version
$string
= Date::Calc::Version();
=head1 IMPORTANT NOTES
(See the section
"RECIPES"
at the bottom of this document
for
solutions to common problems!)
=over 2
=item *
"Year 2000"
(
"Y2K"
) compliance
The upper limit
for
any year number in this module is only
given
by the size of the largest positive integer that can be represented
in a variable of the C type
"int"
on your
system
, which is at least
32767, according to the ANSI C standard (exceptions see below).
In order to simplify calculations, this module projects the gregorian
calendar back
until
the year S<1 A.D.> -- i.e., back B<BEYOND> the
year 1582
when
this calendar was first decreed by the Catholic Pope
S<Gregory XIII>!
Therefore, B<BE SURE TO ALWAYS SPECIFY
"1998"
WHEN YOU MEAN
"1998"
>,
for
instance, and B<DO NOT WRITE
"98"
INSTEAD>, because this will
in fact perform a calculation based on the year
"98"
A.D. and
B<NOT>
"1998"
!
An exception from this rule are the functions which contain the
word
"compress"
in their names (which can only handle years between
1970 and 2069 and also
accept
the abbreviations
"00"
to
"99"
), and
the functions whose names begin
with
"Decode_Date_"
(which translate
year numbers below 100 using a technique known as
"moving window"
).
If you want to convert a two-digit year number into a full-fledged,
four-digit (at least
for
some years to come C<;-)>) year number,
use
the two functions
"Fixed_Window()"
and
"Moving_Window()"
(see their description further below).
Note also that the following
import
/export functions (which are
interfaces to the POSIX functions
"time()"
,
"gmtime()"
,
"localtime()"
and
"mktime()"
or (the
last
two) substitutes
for
the BSD function
"timegm()"
and the POSIX function
"gmtime()"
) have a very limited
range of representable dates (in contrast to all other functions
in this
package
, which cover virtually any date including and
after
S<January 1st 1 A.D.>):
System_Clock()
Today()
Now()
Today_and_Now()
This_Year()
Gmtime()
Localtime()
Mktime()
Timezone()
Date_to_Time()
Time_to_Date()
These functions can only deal
with
dates in the range from
S<01-Jan-1970 00:00:00 GMT> to S<19-Jan-2038 03:14:07 GMT>
(the latter limit is only authoritative on S<32 bit> systems,
however, and can (in principle, through a few code changes)
be extended somewhat C<:-)> on S<64 bit> systems).
On MacOS Classic, the valid range of dates is between
(both included) S<01-Jan-1904 00:00:00> (
local
time
)
to S<06-Feb-2040 06:28:15> (
local
time
).
Note further that the function
"Easter_Sunday()"
can only
be used
for
years in the range 1583 to 2299.
=item *
POSIX functions
Note that the following functions
Gmtime()
Localtime()
Mktime()
Timezone()
are actually wrappers
around
or based upon the corresponding
POSIX functions
"time()"
,
"gmtime()"
,
"localtime()"
and
"mktime()"
.
As such, they depend on
local
settings of the underlying machine
such as e.g. the
system
clock, the
time
zone and the locale.
Their results can therefore sometimes be unexpected or counter-intuitive.
Therefore,
no
support can be provided
for
these functions.
They are supplied
"as is"
, purely
for
the sake of interoperability.
Use at your own risk. (You have been warned!)
=item *
First
index
B<ALL> ranges in this module start
with
"C<1>"
, B<NOT>
"C<0>"
!
I.e., the day of month, day of week, day of year, month of year,
week of year, first valid year number and language B<ALL> start
counting at one, B<NOT> zero!
The only exception is the function
"C<Week_Number()>"
, which may
in fact
return
"C<0>"
when
the
given
date actually lies in the
last
week of the B<PREVIOUS> year, and of course the numbers
for
hours (C<0..23>), minutes (C<0..59>) and seconds (C<0..59>).
=item *
Function naming conventions
Function names completely in lower case indicate a boolean
return
value.
=item *
Boolean
values
Boolean
values
returned from functions in this module are always a
numeric zero (
"C<0>"
)
for
"false"
and a numeric one (
"C<1>"
)
for
"true"
.
=item *
Exception handling
The functions in this module will usually
die
with
a corresponding error
message
if
their input parameters, intermediate results or output
values
are out of range.
The following functions handle errors differently:
- check_date()
- check_time()
- check_business_date()
- check_compressed()
(which
return
a
"false"
return
value
when
the
given
input does not represent
a valid date or
time
),
- Nth_Weekday_of_Month_Year()
(which returns an empty list
if
the requested 5th day of week does not exist),
- Decode_Month()
- Decode_Day_of_Week()
- Decode_Language()
- Fixed_Window()
- Moving_Window()
- Compress()
(which
return
"C<0>"
upon failure or invalid input), and
- Decode_Date_EU()
- Decode_Date_US()
- Decode_Date_EU2()
- Decode_Date_US2()
- Parse_Date()
- Uncompress()
(which
return
an empty list upon failure or invalid input).
Note that you can always
catch
an exception thrown by any of the functions
in this module and handle it yourself by enclosing the function call in an
"C<eval>"
with
curly brackets and checking the special variable
"C<$@>"
(see L<perlfunc(1)/
eval
>
for
details).
=back
=head1 DESCRIPTION
=over 2
=item *
C<
use
Date::Calc
qw( Days_in_Year Days_in_Month ... )
;>
=item *
You can either specify the functions you want to
import
explicitly by
enumerating them between the parentheses of the
"C<qw()>"
operator, or
you can
use
the
"C<:all>"
tag instead to
import
B<ALL> available functions.
=item *
C<
$days
= Days_in_Year(
$year
,
$month
);>
This function returns the sum of the number of days in the months starting
with
January up to and including
"C<$month>"
in the
given
year
"C<$year>"
.
I.e.,
"C<Days_in_Year(1998,1)>"
returns
"C<31>"
,
"C<Days_in_Year(1998,2)>"
returns
"C<59>"
,
"C<Days_in_Year(1998,3)>"
returns
"C<90>"
, and so on.
Note that
"C<Days_in_Year($year,12)>"
returns the number of days in the
given
year
"C<$year>"
, i.e., either
"C<365>"
or
"C<366>"
.
=item *
C<
$days
= Days_in_Month(
$year
,
$month
);>
This function returns the number of days in the
given
month
"C<$month>"
of
the
given
year
"C<$year>"
.
The year must always be supplied, even though it is only needed
when
the
month is February, in order to determine whether it is a leap year or not.
I.e.,
"C<Days_in_Month(1998,1)>"
returns
"C<31>"
,
"C<Days_in_Month(1998,2)>"
returns
"C<28>"
,
"C<Days_in_Month(2000,2)>"
returns
"C<29>"
,
"C<Days_in_Month(1998,3)>"
returns
"C<31>"
, and so on.
=item *
C<
$weeks
= Weeks_in_Year(
$year
);>
This function returns the number of weeks in the
given
year
"C<$year>"
,
i.e., either
"C<52>"
or
"C<53>"
.
=item *
C<
if
(leap_year(
$year
))>
This function returns
"true"
(
"C<1>"
)
if
the
given
year
"C<$year>"
is
a leap year and
"false"
(
"C<0>"
) otherwise.
=item *
C<
if
(check_date(
$year
,
$month
,
$day
))>
This function returns
"true"
(
"C<1>"
)
if
the
given
three numerical
values
"C<$year>"
,
"C<$month>"
and
"C<$day>"
constitute a valid date,
and
"false"
(
"C<0>"
) otherwise.
=item *
C<
if
(check_time(
$hour
,
$min
,
$sec
))>
This function returns
"true"
(
"C<1>"
)
if
the
given
three numerical
values
"C<$hour>"
,
"C<$min>"
and
"C<$sec>"
constitute a valid
time
(C<0 E<lt>=
$hour
E<lt> 24>, C<0 E<lt>=
$min
E<lt> 60> and
C<0 E<lt>=
$sec
E<lt> 60>), and
"false"
(
"C<0>"
) otherwise.
=item *
C<
if
(check_business_date(
$year
,
$week
,
$dow
))>
This function returns
"true"
(
"C<1>"
)
if
the
given
three numerical
values
"C<$year>"
,
"C<$week>"
and
"C<$dow>"
constitute a valid date
in business
format
, and
"false"
(
"C<0>"
) otherwise.
B<Beware> that this function does B<NOT> compute whether a
given
date
is a business day (i.e., Monday to Friday)!
To
do
so,
use
"C<(Day_of_Week($year,$month,$day) E<lt> 6)>"
instead.
=item *
C<
$doy
= Day_of_Year(
$year
,
$month
,
$day
);>
This function returns the (relative) number of the day of the
given
date
in the
given
year.
E.g.,
"C<Day_of_Year($year,1,1)>"
returns
"C<1>"
,
"C<Day_of_Year($year,2,1)>"
returns
"C<32>"
, and
"C<Day_of_Year($year,12,31)>"
returns either
"C<365>"
or
"C<366>"
.
The day of year is sometimes also referred to as the Julian day (or date),
although it
has
nothing to
do
with
the Julian calendar, the calendar which
was used
before
the Gregorian calendar.
In order to convert the number returned by this function back into a
date,
use
the function
"C<Add_Delta_Days()>"
(described further below),
as follows:
$doy
= Day_of_Year(
$year
,
$month
,
$day
);
(
$year
,
$month
,
$day
) = Add_Delta_Days(
$year
,1,1,
$doy
- 1);
=item *
C<
$days
= Date_to_Days(
$year
,
$month
,
$day
);>
This function returns the (absolute) number of the day of the
given
date,
where counting starts at the 1st of January of the year S<1 A.D.>
I.e.,
"C<Date_to_Days(1,1,1)>"
returns
"C<1>"
,
"C<Date_to_Days(1,12,31)>"
returns
"C<365>"
,
"C<Date_to_Days(2,1,1)>"
returns
"C<366>"
,
"C<Date_to_Days(1998,5,1)>"
returns
"C<729510>"
, and so on.
This is sometimes also referred to (not quite correctly) as the Julian
date (or day). This may cause confusion, because also the number of the
day in a year (from 1 to 365 or 366) is frequently called the
"Julian day"
.
More confusing still, this
has
nothing to
do
with
the Julian calendar,
which was used B<BEFORE> the Gregorian calendar.
The Julian calendar was named
after
famous Julius Caesar, who had
instituted it in Roman
times
. The Julian calendar is less precise than
the Gregorian calendar because it
has
too many leap years compared to
the true mean
length
of a year (but the Gregorian calendar also still
has
one day too much every 5000 years). Anyway, the Julian calendar was
better than what existed
before
, because rulers had often changed the
calendar used
until
then in arbitrary ways, in order to lengthen their
own reign,
for
instance.
In order to convert the number returned by this function back into
a date,
use
the function
"C<Add_Delta_Days()>"
(described further
below), as follows:
$days
= Date_to_Days(
$year
,
$month
,
$day
);
(
$year
,
$month
,
$day
) = Add_Delta_Days(1,1,1,
$days
- 1);
=item *
C<
$dow
= Day_of_Week(
$year
,
$month
,
$day
);>
This function returns the number of the day of week of the
given
date.
The function returns
"C<1>"
for
Monday,
"C<2>"
for
Tuesday and so on
until
"C<7>"
for
Sunday.
Note that in the Hebrew calendar (on which the Christian calendar is based),
the week starts
with
Sunday and ends
with
the Sabbath or Saturday (where
according to the Genesis (as described in the Bible) the Lord rested from
creating the world).
In medieval
times
, Catholic Popes have decreed the Sunday to be the official
day of rest, in order to dissociate the Christian from the Hebrew belief.
It appears that this actually happened
with
the Emperor Constantin, who
converted to Christianity but still worshipped the Sun god and therefore
moved the Christian sabbath to the day of the Sun.
Nowadays, the Sunday B<AND> the Saturday are commonly considered (and
used as) days of rest, usually referred to as the
"week-end"
.
Consistent
with
this practice, current norms and standards (such as
S<ISO/R 2015-1971>, S<DIN 1355> and S<ISO 8601>) define the Monday
as the first day of the week.
=item *
C<
$week
= Week_Number(
$year
,
$month
,
$day
);>
This function returns the number of the week the
given
date lies in.
If the
given
date lies in the B<LAST> week of the B<PREVIOUS> year,
"C<0>"
is returned.
If the
given
date lies in the B<FIRST> week of the B<NEXT> year,
"C<Weeks_in_Year($year) + 1>"
is returned.
=item *
C<(
$week
,
$year
) = Week_of_Year(
$year
,
$month
,
$day
);>
This function returns the number of the week the
given
date lies in,
as well as the year that week belongs to.
I.e.,
if
the
given
date lies in the B<LAST> week of the B<PREVIOUS> year,
"C<(Weeks_in_Year($year-1), $year-1)>"
is returned.
If the
given
date lies in the B<FIRST> week of the B<NEXT> year,
"C<(1, $year+1)>"
is returned.
Otherwise,
"C<(Week_Number($year,$month,$day), $year)>"
is returned.
=item *
C<
$week
= Week_of_Year(
$year
,
$month
,
$day
);>
In
scalar
context, this function returns just the week number. This
allows you to
write
"C<$week = Week_of_Year($year,$month,$day);>"
instead of
"C<($week) = Week_of_Year($year,$month,$day);>"
(note
the parentheses
around
"C<$week>"
).
If the
given
date lies in the B<LAST> week of the B<PREVIOUS> year,
"C<Weeks_in_Year($year-1)>"
is returned.
If the
given
date lies in the B<FIRST> week of the B<NEXT> year,
"C<1>"
is returned.
Otherwise the
return
value is identical
with
that of
"C<Week_Number($year,$month,$day)>"
.
B<BEWARE> that using this function in
scalar
context is a B<DANGEROUS>
feature, because without knowing which year the week belongs to, you
might inadvertently assume the wrong one!
If
for
instance you are iterating through an interval of dates, you might
assume that the week always belongs to the same year as the
given
date,
which unfortunately is B<WRONG> in some cases!
In many years, the 31st of December
for
instance belongs to week number
one of the B<FOLLOWING> year. Assuming that the year is the same as your
date (31st of December, in this example), sends you back to the first week
of the B<CURRENT> year - the Monday of which, by the way, in case of bad
luck, might actually lie in the year B<BEFORE> the current year!
This actually happens in 2002,
for
example.
So you always need to provide the correct corresponding year number
by other means, keeping track of it yourself.
In case you
do
not understand this, never mind, but then simply
B<DO NOT USE> this function in
scalar
context!
=item *
C<(
$year
,
$month
,
$day
) = Monday_of_Week(
$week
,
$year
);>
This function returns the date of the first day of the
given
week, i.e.,
the Monday.
"C<$year>"
must be greater than or equal to
"C<1>"
, and
"C<$week>"
must
lie in the range
"C<1>"
to
"C<Weeks_in_Year($year)>"
.
Note that you can
write
"C<($year,$month,$day) = Monday_of_Week(Week_of_Year($year,$month,$day));>"
in order to calculate the date of the Monday of the same week as the
given
date.
If you want to calculate any other day of week in the same week as a
given
date,
use
@date
= Add_Delta_Days(Monday_of_Week(Week_of_Year(
@date
)),
$offset
);
where C<
$offset
= 1>
for
Tuesday, C<2>
for
Wednesday etc.
=item *
C<
if
((
$year
,
$month
,
$day
) = Nth_Weekday_of_Month_Year(
$year
,
$month
,
$dow
,
$n
))>
This function calculates the date of the
"C<$n>"
th day of week
"C<$dow>"
in the
given
month
"C<$month>"
and year
"C<$year>"
; such as,
for
example,
the 3rd Thursday of a
given
month and year.
This can be used to
send
a notification mail to the members of a group
which meets regularly on every 3rd Thursday of a month,
for
instance.
(See the section
"RECIPES"
near the end of this document
for
a code
snippet to actually
do
so.)
"C<$year>"
must be greater than or equal to
"C<1>"
,
"C<$month>"
must lie
in the range
"C<1>"
to
"C<12>"
,
"C<$dow>"
must lie in the range
"C<1>"
to
"C<7>"
and
"C<$n>"
must lie in the range
"C<1>"
to
"C<5>"
, or a fatal
error (
with
appropriate error message) occurs.
The function returns an empty list
when
the 5th of a
given
day of week
does not exist in the
given
month and year.
=item *
C<(
$year
,
$week
,
$dow
) = Standard_to_Business(
$year
,
$month
,
$day
);>
This function converts a
given
date from standard notation (year,
month, day (of month)) to business notation (year, week, day of week).
=item *
C<(
$year
,
$month
,
$day
) = Business_to_Standard(
$year
,
$week
,
$dow
);>
This function converts a
given
date from business notation (year,
week, day of week) to standard notation (year, month, day (of month)).
=item *
C<
$Dd
= Delta_Days(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
);>
This function returns the difference in days between the two
given
dates.
The result is positive
if
the two dates are in chronological order,
i.e.,
if
date
if
the order of the two dates is reversed.
The result is zero
if
the two dates are identical.
=item *
C<(
$Dd
,
$Dh
,
$Dm
,
$Ds
) = Delta_DHMS(
$year1
,
$month1
,
$day1
,
$hour1
,
$min1
,
$sec1
,
$year2
,
$month2
,
$day2
,
$hour2
,
$min2
,
$sec2
);>
This function returns the difference in days, hours, minutes and seconds
between the two
given
dates
with
times
.
All four
return
values
will be positive
if
the two dates are in chronological
order, i.e.,
if
date
(in all four
return
values
!)
if
the order of the two dates is reversed.
This is so that the two functions
"C<Delta_DHMS()>"
and
"C<Add_Delta_DHMS()>"
(description see further below) are complementary, i.e., mutually inverse:
Add_Delta_DHMS(
@date1
,
@time1
, Delta_DHMS(
@date1
,
@time1
,
@date2
,
@time2
))
yields
"C<(@date2,@time2)>"
again, whereas
Add_Delta_DHMS(
@date2
,
@time2
,
map
(-
$_
, Delta_DHMS(
@date1
,
@time1
,
@date2
,
@time2
)))
yields
"C<(@date1,@time1)>"
, and
Delta_DHMS(
@date1
,
@time1
, Add_Delta_DHMS(
@date1
,
@time1
,
@delta
))
yields
"C<@delta>"
again.
The result is zero (in all four
return
values
)
if
the two dates and
times
are identical.
=item *
C<(
$Dy
,
$Dm
,
$Dd
) = Delta_YMD(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
);>
This function returns the vector
(
$year2
-
$year1
,
$month2
-
$month1
,
$day2
-
$day1
)
This is called the
"one-by-one"
semantics.
Adding the result of this function to the first date always yields the
second date again, and adding the negative result (where the signs of
all elements of the result vector have been flipped) to the second
date gives the first date. See also the description of the function
"Add_Delta_YMD()"
further below.
Example:
(6,2,-30) == Delta_YMD(1996,1,31, 2002,3,1]);
[1996,1,31] + ( 6, 2,-30) = [2002,3, 1]
[2002,3, 1] + (-6,-2, 30) = [1996,1,31]
An error occurs
if
any of the two
given
dates is invalid.
=item *
C<(
$D_y
,
$D_m
,
$D_d
,
$Dh
,
$Dm
,
$Ds
) = Delta_YMDHMS(
$year1
,
$month1
,
$day1
,
$hour1
,
$min1
,
$sec1
,
$year2
,
$month2
,
$day2
,
$hour2
,
$min2
,
$sec2
);>
This function is based on the function
"Delta_YMD()"
above but additionally
calculates the
time
difference. When a carry over from the
time
difference
occurs, the value of
"C<$D_d>"
is adjusted accordingly, thus giving the
correct total date/
time
difference.
Arguments are expected to be in chronological order to yield a (usually)
positive result.
In any case, adding the result of this function to the first date/
time
value
(C<
$year1
,
$month1
,
$day1
,> C<
$hour1
,
$min1
,
$sec1
>) always gives the second
date/
time
value (C<
$year2
,
$month2
,
$day2
,> C<
$hour2
,
$min2
,
$sec2
>) again,
and adding the negative result (
with
the signs of all elements of the result
vector flipped) to the second date/
time
value gives the first date/
time
value.
See the function
"Add_Delta_YMDHMS()"
further below
for
adding a date/
time
value and a date/
time
difference.
An error occurs
if
any of the
given
two date/
time
values
is invalid.
=item *
C<(
$Dy
,
$Dm
,
$Dd
) = N_Delta_YMD(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
);>
This function returns the difference between the two
given
dates in a
more intuitive way (as far as possible - more on that see a bit further
below) than the function
"Delta_YMD()"
described above.
The
"N"
which precedes its name is meant to signify
"new"
or
"normalized"
.
This function is loosely based on recipe
below near the end of this document).
However, the code of recipe
negative
values
symmetrically and consistently.
This new routine does.
The
return
values
of this function are guaranteed to all have the same
sign (or to be zero). This is why this function is called
"normalized"
.
Moreover, the results are guaranteed to be
"minimal"
, in the sense that
C<|
$Dm
| E<lt> 12> and C<|
$Dd
| E<lt> 31> (which is equivalent to C<
$Dm
>
lying in the range C<[-11..+11]> and C<
$Dd
> lying in the range C<[-30..+30]>).
When the results are applied (i.e., added) to the first
given
date in a
left-to-right order, the second
given
date is guaranteed to be obtained,
provided that intermediary results are truncated, as done by the function
"Add_Delta_YM()"
(see further below), i.e., that invalid intermediate dates
such as e.g. [2009,2,31] will automatically be transformed into [2009,2,28]
(and not
"wrapped"
into the
next
month, e.g. to [2009,3,3]).
This is called the
"left-to-right with truncation"
semantics.
Note that reversing the order of the
given
dates and reversing the sign of
each
of the result
values
will not always add up.
Consider the dates [2008,2,29] and [2009,2,1]: their difference is (0,11,3)
([2008,2,29] plus 11 months is [2009,1,29], which plus 3 days is [2009,2,1]),
but the difference between [2009,2,1] and [2008,2,29] is (0,-11,-1), and
not (0,-11,-3) ([2009,2,1] minus 11 months is [2008,3,1], which minus one
day is [2008,2,29]).
Another example: The difference between [1996,2,29] and [1997,2,28] is (1,0,0)
(observe the truncation of the invalid date [1997,2,29] to [1997,2,28] here!),
whereas the difference between [1997,2,28] and [1996,2,29] is (0,-11,-28)
([1997,2,28] minus 11 months is [1996,3,28], which minus 28 days is not
[1996,3,0] but of course [1996,2,29]).
"Benign"
examples such as
for
instance the difference between [1964,1,3]
and [2009,9,10] are completely symmetrical: The difference in this example
is (45,8,7), whereas the difference between [2009,9,10] and [1964,1,3] is
(-45,-8,-7), as would normally be expected. In this example, the result
is also the same as the one returned by
"Delta_YMD()"
.
All these counter-intuitive effects are due to the fact that months
(and due to leap years, also years)
do
not correspond to a fixed number
of days, so the semantics of
"plus one month"
or
"plus one year"
are in
fact undefined.
The present function is an attempt to provide a definition which is
intuitive most of the
time
, and at least consistent the rest of the
time
.
Other definitions are of course possible, but most often lead to
contradictions (e.g., the results and the
given
first date
do
not
add up to the second
given
date).
See the file
"datecalc.pl"
in the
"examples"
subdirectory of this
distribution
for
a way to play
around
with
this function, or go to
An error occurs
if
any of the two
given
dates is invalid, or
if
any
intermediate result leads to an invalid date (this does not apply
to truncation, however, as explained above).
=item *
C<(
$D_y
,
$D_m
,
$D_d
,
$Dhh
,
$Dmm
,
$Dss
) = N_Delta_YMDHMS(
$year1
,
$month1
,
$day1
,
$hour1
,
$min1
,
$sec1
,
$year2
,
$month2
,
$day2
,
$hour2
,
$min2
,
$sec2
);>
This function essentially does the same as the function
"N_Delta_YMD()"
described immediately above, except that also the difference in hours,
minutes and seconds is taken into account.
This function is loosely based on recipe
below near the end of this document).
However, the code of recipe
negative
values
symmetrically and consistently.
This new routine does.
The
return
values
of this function (including the
time
differences)
are guaranteed to all have the same sign (or to be zero). This is the
reason
for
the
"N"
that precedes the name of this function, which
is intended to mean
"normalized"
(or
"new"
).
Moreover, the results are guaranteed to be
"minimal"
, in the sense that
C<|
$D_m
| E<lt> 12>, C<|
$D_d
| E<lt> 31>, C<|
$Dhh
| E<lt> 24>, C<|
$Dmm
| E<lt> 60>
and C<|
$Dss
| E<lt> 60> (which is equivalent to C<
$D_m
> lying in the range
C<[-11..+11]>, C<
$D_d
> lying in the range C<[-30..+30]>, C<
$Dhh
> lying in the
range C<[-23..+23]>, and C<
$Dmm
> and C<
$Dss
> both lying in the range C<[-59..+59]>).
=item *
C<(
$Dd
,
$Dh
,
$Dm
,
$Ds
) = Normalize_DHMS(
$Dd
,
$Dh
,
$Dm
,
$Ds
);>
This function takes four arbitrary
values
for
days, hours, minutes
and seconds (which may have different signs) and renormalizes them
so that the
values
for
hours, minutes and seconds will lie in the
ranges C<[-23..23]>, C<[-59..59]> and C<[-59..59]>, respectively,
and so that all four
values
have the same sign (or are zero).
The
given
values
are left untouched, i.e., unchanged.
=item *
C<(
$year
,
$month
,
$day
) = Add_Delta_Days(
$year
,
$month
,
$day
,
$Dd
);>
This function
has
two principal uses:
First, it can be used to calculate a new date,
given
an initial date and
an offset (which may be positive or negative) in days, in order to answer
questions like
"today plus 90 days -- which date gives that?"
.
(In order to add a weeks offset, simply multiply the weeks offset
with
"C<7>"
and
use
that as your days offset.)
Second, it can be used to convert the canonical representation of a date,
i.e., the number of that day (where counting starts at the 1st of January
in S<1 A.D.>), back into a date
given
as year, month and day.
Because counting starts at
"C<1>"
, you will actually have to subtract
"C<1>"
from the canonical date in order to get back the original date:
$canonical
= Date_to_Days(
$year
,
$month
,
$day
);
(
$year
,
$month
,
$day
) = Add_Delta_Days(1,1,1,
$canonical
- 1);
Moreover, this function is the inverse of the function
"C<Delta_Days()>"
:
Add_Delta_Days(
@date1
, Delta_Days(
@date1
,
@date2
))
yields
"C<@date2>"
again, whereas
Add_Delta_Days(
@date2
, -Delta_Days(
@date1
,
@date2
))
yields
"C<@date1>"
, and
Delta_Days(
@date1
, Add_Delta_Days(
@date1
,
$delta
))
yields
"C<$delta>"
again.
=item *
C<(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) = Add_Delta_DHMS(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$Dd
,
$Dh
,
$Dm
,
$Ds
);>
This function serves to add a days, hours, minutes and seconds offset to a
given
date and
time
, in order to answer questions like "today and now plus
7 days but minus 5 hours and then plus 30 minutes, what date and
time
gives
that?":
(
$y
,
$m
,
$d
,
$H
,
$M
,
$S
) = Add_Delta_DHMS(Today_and_Now(), +7,-5,+30,0);
=item *
C<(
$year
,
$month
,
$day
) = Add_Delta_YM(
$year
,
$month
,
$day
,
$Dy
,
$Dm
);>
This function can be used to add a year and/or month offset to a
given
date.
In contrast to the function described immediately below
(
"C<Add_Delta_YMD()>"
), this function does
no
"wrapping"
into
the
next
month
if
the day happens to lie outside the valid range
for
the resulting year and month (
after
adding the year and month
offsets). Instead, it simply truncates the day to the
last
possible
day of the resulting month.
Examples:
Adding an offset of 0 years, 1 month to the date [1999,1,31] would result
in the (invalid) date [1999,2,31]. The function replaces this result by
the (valid) date [1999,2,28].
Adding an offset of 1 year, 1 month to the same date [1999,1,31] as above
would result in the (still invalid) date [2000,2,31]. The function replaces
this result by the valid date [2000,2,29] (because 2000 is a leap year).
Note that the year and month offsets can be negative, and that they can
have different signs.
If you want to additionally add a days offset,
use
the function
"C<Add_Delta_Days()>"
before
or
after
calling
"C<Add_Delta_YM()>"
:
@date2
= Add_Delta_Days( Add_Delta_YM(
@date1
,
$Dy
,
$Dm
),
$Dd
);
@date2
= Add_Delta_YM( Add_Delta_Days(
@date1
,
$Dd
),
$Dy
,
$Dm
);
Note that your result may depend on the order in which you call
these two functions!
Consider the date [1999,2,28] and the offsets 0 years, 1 month
and 1 day:
[1999,2,28] plus one month is [1999,3,28], plus one day is
[1999,3,29]. [1999,2,28] plus one day is [1999,3,1], plus
one month is [1999,4,1].
(Which is also the reason why the
"C<Add_Delta_YM()>"
function
does not allow to add a days offset, because this would actually
require
TWO functions: One
for
adding the days offset BEFORE and
one
for
adding it AFTER applying the year/month offsets.)
An error occurs
if
the initial date is not valid.
Note that
"C<Add_Delta_YM( Add_Delta_YM(@date, $Dy,$Dm), -$Dy,-$Dm );>"
will not, in general,
return
the original date
"C<@date>"
(consider
the examples
given
above!).
=item *
C<(
$year
,
$month
,
$day
) = Add_Delta_YMD(
$year
,
$month
,
$day
,
$Dy
,
$Dm
,
$Dd
);>
This function serves to add a years, months and days offset to a
given
date.
(In order to add a weeks offset, simply multiply the weeks offset
with
"C<7>"
and add this number to your days offset.)
Note that the three offsets
for
years, months and days are applied
independently from
each
other. This also allows them to have
different signs.
The years and months offsets are applied first, and the days offset
is applied
last
.
If the resulting date happens to fall on a day
after
the end of the
resulting month, like the 32nd of April or the 30th of February, then
the date is simply counted forward into the
next
month (possibly also
into the
next
year) by the number of excessive days (e.g., the 32nd of
April will become the 2nd of May).
B<BEWARE> that this behaviour differs from that of previous versions
of this module! In previous versions, the day was simply truncated to
the maximum number of days in the resulting month.
If you want the previous behaviour,
use
the new function
"C<Add_Delta_YM()>"
(described immediately above) plus the function
"C<Add_Delta_Days()>"
instead.
B<BEWARE> also that because a year and a month offset is not equivalent
to a fixed number of days, the transformation performed by this function
is B<NOT ALWAYS REVERSIBLE>!
This is in contrast to the functions
"C<Add_Delta_Days()>"
and
"C<Add_Delta_DHMS()>"
, which are fully and truly reversible (
with
the help of the functions
"C<Delta_Days()>"
and
"C<Delta_DHMS()>"
,
for
instance).
Note that
for
this same reason,
@date
= Add_Delta_YMD(
Add_Delta_YMD(
@date
,
$Dy
,
$Dm
,
$Dd
), -
$Dy
,-
$Dm
,-
$Dd
);
will in general B<NOT>
return
the initial date
"C<@date>"
, even
though
@date2
= Add_Delta_YMD(
@date1
, Delta_YMD(
@date1
,
@date2
) );
will always
return
the second date
"C<@date2>"
, and
@date1
= Add_Delta_YMD(
@date2
,
map
(-
$_
, Delta_YMD(
@date1
,
@date2
)) );
which is the same as
@date1
= Add_Delta_YMD(
@date2
, Delta_YMD(
@date2
,
@date1
) );
will always
return
the first date
"C<@date1>"
.
Examples:
[1996,1,31] + ( 6, 1,-2) = [2002,3,1]
[2002,3, 1] + (-6,-1, 2) = [1996,2,3]
(6,2,-30) == Delta_YMD(1996,1,31, 2002,3,1);
[1996,1,31] + ( 6, 2,-30) = [2002,3, 1]
[2002,3, 1] + (-6,-2, 30) = [1996,1,31]
(6,1,-2) == Delta_YMD(1996,2,3, 2002,3,1);
[1996,2,3] + ( 6, 1,-2) = [2002,3,1]
[2002,3,1] + (-6,-1, 2) = [1996,2,3]
Note that this is B<NOT> a program bug but B<NECESSARILY> so,
because of the variable lengths of years and months, and hence
because of the ambiguity of the difference between two dates
in terms of years, months and days, i.e., the fact that the
difference between two dates can be expressed in more than
one way:
[1996,1,31] + (6,1, -2) = [2002,3,1]
[1996,1,31] + (6,2,-30) = [2002,3,1]
=item *
C<(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) = Add_Delta_YMDHMS(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$D_y
,
$D_m
,
$D_d
,
$Dh
,
$Dm
,
$Ds
);>
Same as the function above, except that a
time
offset may be
given
in
addition to the year, month and day offset.
=item *
C<(
$year
,
$month
,
$day
) = Add_N_Delta_YMD(
$year
,
$month
,
$day
,
$Dy
,
$Dm
,
$Dd
);>
This function is actually a shortcut
for
applying the function
"Add_Delta_YM()"
first, followed by the function
"Add_Delta_Days()"
, i.e., this function does
exactly the same as
(
$year
,
$month
,
$day
) = Add_Delta_Days( Add_Delta_YM(
$year
,
$month
,
$day
,
$Dy
,
$Dm
),
$Dd
);
Beware that,
if
necessary, the function
"Add_Delta_YM()"
truncates the
resulting day of the month to the largest allowable value
for
that month,
i.e., the (invalid) result [2009,2,31] is automatically transformed into
[2009,2,28].
For more details on this truncation, see the description of the function
"Add_Delta_YM()"
further above.
This function is meant to be complementary
with
the function
"N_Delta_YMD()"
described further above.
This means that it is guaranteed that the result returned by
Add_N_Delta_YMD(
@date1
, N_Delta_YMD(
@date1
,
@date2
) );
is always identical
with
the
given
date
"C<@date2>"
.
Note however that unlike
with
function
"Add_Delta_YMD()"
,
the
reverse
is not true here, i.e.,
(
$Dy
,
$Dm
,
$Dd
) = N_Delta_YMD(
@date1
,
@date2
);
@date
= Add_N_Delta_YMD(
@date2
, -
$Dy
,-
$Dm
,-
$Dd
);
will B<NOT> always
return
the initial date
"C<@date1>"
.
Example:
(0,11,3) == N_Delta_YMD(2008,2,29, 2009,2,1);
[2008,2,29] + (0, 11, 3) = [2009,2, 1]
[2009,2, 1] + (0,-11,-3) = [2008,2,27]
=item *
C<(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) = Add_N_Delta_YMDHMS(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$D_y
,
$D_m
,
$D_d
,
$Dhh
,
$Dmm
,
$Dss
);>
This function essentially does the same as the function
"Add_N_Delta_YMD()"
described immediately above, except that also the difference in hours,
minutes and seconds is taken into account.
=item *
C<(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$doy
,
$dow
,
$dst
) = System_Clock([
$gmt
]);>
If your operating
system
supports the corresponding
system
calls
(
"C<time()>"
and
"C<localtime()>"
or
"C<gmtime()>"
), this function
will
return
the information provided by your
system
clock, i.e.,
the current date and
time
, the number of the day of year, the number
of the day of week and a flag signaling whether daylight savings
time
is currently in effect or not.
The ranges of
values
returned (and their meanings) are as follows:
$year
: 1970..2038 (or more) [Unix etc.]
$year
: 1904..2040 [MacOS Classic]
$month
: 1..12
$day
: 1..31
$hour
: 0..23
$min
: 0..59
$sec
: 0..59 (0..61 on some systems)
$doy
: 1..366
$dow
: 1..7
$dst
: -1..1
"C<$doy>"
is the day of year, sometimes also referred to as the
"julian date"
, which starts at
"C<1>"
and goes up to the number
of days in that year.
The day of week (
"C<$dow>"
) will be
"C<1>"
for
Monday,
"C<2>"
for
Tuesday and so on
until
"C<7>"
for
Sunday.
The daylight savings
time
flag (
"C<$dst>"
) will be
"C<-1>"
if
this
information is not available on your
system
,
"C<0>"
for
no
daylight
savings
time
(i.e., winter
time
) and
"C<1>"
when
daylight savings
time
is in effect.
If your operating
system
does not provide the necessary
system
calls,
calling this function will result in a fatal "not available on this
system
" error message.
If you want to handle this exception yourself,
use
"C<eval>"
as follows:
eval
{ (
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$doy
,
$dow
,
$dst
) =
System_Clock(); };
if
($@)
{
}
Note that curlies (
"{"
and
"}"
) are used here to delimit the statement to
be
"eval"
ed (which is the way to
catch
exceptions in Perl), and not quotes
(which is a way to evaluate Perl expressions at runtime).
If the optional (boolean) input parameter
"C<$gmt>"
is
given
, a
"true"
value (
"C<1>"
) will cause
"C<gmtime()>"
to be used instead of
"C<localtime()>"
,
internally, thus returning Greenwich Mean Time (GMT, or UTC) instead of
local
time
.
=item *
C<(
$year
,
$month
,
$day
) = Today([
$gmt
]);>
This function returns a subset of the
values
returned by the function
"C<System_Clock()>"
(see above
for
details), namely the current year,
month and day.
A fatal
"not available on this system"
error message will appear
if
the
corresponding
system
calls are not supported by your current operating
system
.
If the optional (boolean) input parameter
"C<$gmt>"
is
given
, a
"true"
value (
"C<1>"
) will cause
"C<gmtime()>"
to be used instead of
"C<localtime()>"
,
internally, thus returning Greenwich Mean Time (GMT, or UTC) instead of
local
time
.
=item *
C<(
$hour
,
$min
,
$sec
) = Now([
$gmt
]);>
This function returns a subset of the
values
returned by the function
"C<System_Clock()>"
(see above
for
details), namely the current
time
(hours, minutes and full seconds).
A fatal
"not available on this system"
error message will appear
if
the
corresponding
system
calls are not supported by your current operating
system
.
If the optional (boolean) input parameter
"C<$gmt>"
is
given
, a
"true"
value (
"C<1>"
) will cause
"C<gmtime()>"
to be used instead of
"C<localtime()>"
,
internally, thus returning Greenwich Mean Time (GMT, or UTC) instead of
local
time
.
=item *
C<(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) = Today_and_Now([
$gmt
]);>
This function returns a subset of the
values
returned by the function
"C<System_Clock()>"
(see above
for
details), namely the current date
(year, month, day) and
time
(hours, minutes and full seconds).
A fatal
"not available on this system"
error message will appear
if
the
corresponding
system
calls are not supported by your current operating
system
.
If the optional (boolean) input parameter
"C<$gmt>"
is
given
, a
"true"
value (
"C<1>"
) will cause
"C<gmtime()>"
to be used instead of
"C<localtime()>"
,
internally, thus returning Greenwich Mean Time (GMT, or UTC) instead of
local
time
.
=item *
C<
$year
= This_Year([
$gmt
]);>
This function returns the current year, according to
local
time
.
A fatal
"not available on this system"
error message will appear
if
the
corresponding
system
calls are not supported by your current operating
system
.
If the optional (boolean) input parameter
"C<$gmt>"
is
given
, a
"true"
value (
"C<1>"
) will cause
"C<gmtime()>"
to be used instead of
"C<localtime()>"
,
internally, thus returning Greenwich Mean Time (GMT, or UTC) instead of
local
time
. However, this will only make a difference within a few hours
around
New Year (
unless
you are on a Pacific island, where this can
be almost 24 hours).
=item *
C<(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$doy
,
$dow
,
$dst
) = Gmtime([
time
]);>
This is Date::Calc
's equivalent of Perl'
s built-in
"gmtime()"
function.
See also L<perlfunc(1)/
gmtime
>.
With the optional argument
"time"
(i.e., seconds since the epoch),
this function will
return
the corresponding
values
for
that particular
time
(instead of the current
time
when
this parameter is omitted).
The ranges of
values
returned (and their meanings) are as follows:
$year
: 1970..2038 (or more) [Unix etc.]
$year
: 1904..2040 [MacOS Classic]
$month
: 1..12
$day
: 1..31
$hour
: 0..23
$min
: 0..59
$sec
: 0..59
$doy
: 1..366
$dow
: 1..7
$dst
: -1..1
"C<$doy>"
is the day of year, sometimes also referred to as the
"julian date"
, which starts at
"C<1>"
and goes up to the number
of days in that year.
The day of week (
"C<$dow>"
) will be
"C<1>"
for
Monday,
"C<2>"
for
Tuesday and so on
until
"C<7>"
for
Sunday.
The daylight savings
time
flag (
"C<$dst>"
) will be
"C<-1>"
if
this
information is not available on your
system
,
"C<0>"
for
no
daylight
savings
time
(i.e., winter
time
) and
"C<1>"
when
daylight savings
time
is in effect.
A fatal
"time out of range"
error will occur
if
the
given
time
value
is out of range C<[0..(~0E<gt>E<gt>1)]>.
If the
time
value is omitted, the
"time()"
function is called instead,
internally.
=item *
C<(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
,
$doy
,
$dow
,
$dst
) = Localtime([
time
]);>
This is Date::Calc
's equivalent of Perl'
s built-in
"localtime()"
function.
See also L<perlfunc(1)/
localtime
>.
The ranges of
values
returned (and their meanings) are as follows:
$year
: 1970..2038 (or more) [Unix etc.]
$year
: 1904..2040 [MacOS Classic]
$month
: 1..12
$day
: 1..31
$hour
: 0..23
$min
: 0..59
$sec
: 0..59
$doy
: 1..366
$dow
: 1..7
$dst
: -1..1
"C<$doy>"
is the day of year, sometimes also referred to as the
"julian date"
, which starts at
"C<1>"
and goes up to the number
of days in that year.
The day of week (
"C<$dow>"
) will be
"C<1>"
for
Monday,
"C<2>"
for
Tuesday and so on
until
"C<7>"
for
Sunday.
The daylight savings
time
flag (
"C<$dst>"
) will be
"C<-1>"
if
this
information is not available on your
system
,
"C<0>"
for
no
daylight
savings
time
(i.e., winter
time
) and
"C<1>"
when
daylight savings
time
is in effect.
A fatal
"time out of range"
error will occur
if
the
given
time
value is
out of range C<[0..(~0E<gt>E<gt>1)]>.
If the
time
value is omitted, the
"time()"
function is called instead,
internally.
=item *
C<
$time
= Mktime(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
);>
This function converts a date into a
time
value, i.e., into the number
of seconds since whatever moment in
time
your
system
considers to be
the
"epoch"
. On Unix and most other systems this is the number of seconds
since January 1st 1970 at midnight (GMT). On MacOS Classic this is the
number of seconds since January 1st 1904 at midnight (
local
time
).
The function is similar to the
"POSIX::mktime()"
function (see L<POSIX(1)/mktime>
for
more details), but in contrast to the latter, it expects dates in the
usual ranges used throughout this module: The year 2001 stays year 2001,
and months are numbered from 1 to 12.
A fatal
"date out of range"
error will occur
if
the
given
date cannot
be expressed in terms of seconds since the epoch (this happens
for
instance
when
the date lies
before
the epoch, or
if
it is later than
S<19-Jan-2038 03:14:07 GMT> on S<32 bit> Unix systems, or later than
S<06-Feb-2040 06:28:15> (
local
time
) on a Macintosh
with
MacOS Classic).
Just like the
"POSIX::mktime()"
function, this function uses the
"mktime()"
system
call, internally.
This means that the
given
date and
time
is considered to be in
local
time
,
and that the value returned by this function will depend on your machine's
local
settings such as the
time
zone, whether daylight savings
time
is
(or was, at the
time
) in effect, and the
system
clock itself.
B<BEWARE> that
"mktime()"
does not always
return
the same
time
value
as fed into
"localtime()"
,
when
you feed the output of
"localtime()"
back into
"mktime()"
, on some systems!
I.e.,
"C<Mktime((Localtime($time))[0..5])>"
will not always
return
the same value as
given
in
"C<$time>"
!
=item *
C<(
$D_y
,
$D_m
,
$D_d
,
$Dh
,
$Dm
,
$Ds
,
$dst
) = Timezone([
time
]);>
This function returns the difference between
"C<localtime(time)>"
and
"C<gmtime(time)>"
, which is the timezone offset in effect
for
the current
location and the
given
"C<time>"
.
This offset is positive
if
you are located to the east of Greenwich,
and is usually negative (except during daylight savings
time
, in some
locations)
if
you are located to the west of Greenwich.
Note that this offset is influenced by all of the relevant
system
settings and parameters on your machine; such as locales, environment
variables (e.g.
"C<TZ>"
) and the
system
clock itself. See the
relevant documentation on your
system
for
more details.
If the
"C<time>"
is omitted, the
"C<time()>"
function will
be called automatically, internally (similar to the built-in
functions
"C<localtime()>"
and
"C<gmtime()>"
in Perl).
A fatal
"time out of range"
error will occur
if
the
given
time
value
is out of range C<[0..(~0E<gt>E<gt>1)]>.
The
last
item of the returned list is a flag which indicates whether
daylight savings
time
is currently in effect. This flag is negative
(-1)
if
this information is not available on your
system
. It is zero
(0)
when
daylight savings
time
is off, and positive (+1)
when
daylight
savings
time
is on.
Thus you can check very quickly whether daylight savings
time
is
currently in effect by evaluating this function in
scalar
context
(in
scalar
context, Perl returns the
last
item of a list):
if
(
scalar
Timezone > 0) {
However, a slightly more efficient way would be this:
if
(
scalar
System_Clock > 0) {
=item *
C<
$time
= Date_to_Time(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
);>
This function is a replacement
for
the BSD function
"timegm()"
(which is not available on all Unix systems), which converts
a
given
date and
time
into a
time
value, i.e., into the number
of seconds since whatever moment in
time
your
system
considers to be
the
"epoch"
. On Unix and most other systems this is the number of seconds
since January 1st 1970 at midnight (GMT). On MacOS Classic this is the
number of seconds since January 1st 1904 at midnight (
local
time
).
Under Unix, the date and
time
are considered to be in UTC
(
"Universal Time Coordinated"
, and so is the resulting
time
value.
UTC is almost the same as GMT (or
"Greenwich Mean Time"
), except
that UTC
has
leap seconds (in order to account
for
small variations
in the rotation of the earth,
for
instance), whereas GMT does not.
Under MacOS Classic, however, both input and output are
considered to be in
local
time
.
The ranges of year and month follow the same rules as throughout
the rest of this module (and not the contorted rules of its Unix
equivalent), i.e., the year
"2001"
stays
"2001"
and the month
ranges from 1 to 12.
A fatal
"date out of range"
error will occur
if
the
given
date cannot
be expressed in terms of seconds since the epoch (this happens
for
instance
when
the date lies
before
the epoch, or
if
it is later than
S<19-Jan-2038 03:14:07 GMT> on S<32 bit> Unix systems, or later than
S<06-Feb-2040 06:28:15> (
local
time
) on a Macintosh
with
MacOS Classic).
This function should be very fast, because it is implemented in
a very straightforward manner and doesn't
use
any internal
system
calls.
Moreover, the functions
"Date_to_Time()"
and
"Time_to_Date()"
are guaranteed to be complementary, i.e., that
"C<Date_to_Time(Time_to_Date($time))>"
and
"C<Time_to_Date(Date_to_Time($year,$month,$day, $hour,$min,$sec))>"
will always
return
the initial
values
.
=item *
C<(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) = Time_to_Date([
time
]);>
This function is an alternative to the POSIX
"gmtime()"
function
(and its built-in Perl equivalent), which converts a
given
time
value into the corresponding date and
time
. The
given
time
value
must be the number of seconds since whatever moment in
time
your
system
considers to be the
"epoch"
. On Unix and most other systems
this is the number of seconds since January 1st 1970 at midnight
(GMT). On MacOS Classic this is the number of seconds since
January 1st 1904 at midnight (
local
time
).
Under Unix, the
given
time
value is considered to be in UTC
(
"Universal Time Coordinated"
, and so is the resulting date
and
time
.
UTC is almost the same as GMT (or
"Greenwich Mean Time"
), except
that UTC
has
leap seconds (in order to account
for
small variations
in the rotation of the earth,
for
instance), whereas GMT does not.
Under MacOS Classic, however, both input and output
are considered to be in
local
time
.
If the input value
"C<time>"
is omitted, the
"C<time()>"
function
will be called automatically, internally (similar to the built-in
functions
"C<localtime()>"
and
"C<gmtime()>"
in Perl).
A fatal
"time out of range"
error will occur
if
the
given
time
value is negative.
This function should be very fast, because it is implemented in
a very straightforward manner and doesn't
use
any internal
system
calls (except
for
"time()"
,
if
the input value is omitted).
Moreover, the functions
"Date_to_Time()"
and
"Time_to_Date()"
are guaranteed to be complementary, i.e., that
"C<Date_to_Time(Time_to_Date($time))>"
and
"C<Time_to_Date(Date_to_Time($year,$month,$day, $hour,$min,$sec))>"
will always
return
the initial
values
.
=item *
C<(
$year
,
$month
,
$day
) = Easter_Sunday(
$year
);>
This function calculates the date of Easter Sunday
for
all years in the
range from 1583 to 2299 (all other year numbers will result in a fatal
"year out of range"
error message) using the method known as the "Gaussian
Rule".
Some related christian feast days which depend on the date of Easter Sunday:
Carnival Monday / Rosenmontag / Veille du Mardi Gras = -48 days
Mardi Gras / Karnevalsdienstag / Mardi Gras = -47 days
Ash Wednesday / Aschermittwoch / Mercredi des Cendres = -46 days
Palm Sunday / Palmsonntag / Dimanche des Rameaux = -7 days
Easter Friday / Karfreitag / Vendredi Saint = -2 days
Easter Saturday / Ostersamstag / Samedi de Paques = -1 day
Easter Monday / Ostermontag / Lundi de Paques = +1 day
Ascension of Christ / Christi Himmelfahrt / Ascension = +39 days
Whitsunday / Pfingstsonntag / Dimanche de Pentecote = +49 days
Whitmonday / Pfingstmontag / Lundi de Pentecote = +50 days
Feast of Corpus Christi / Fronleichnam / Fete-Dieu = +60 days
Use the offsets shown above to calculate the date of the corresponding
feast day as follows:
(
$year
,
$month
,
$day
) = Add_Delta_Days(Easter_Sunday(
$year
),
$offset
));
=item *
C<
if
(
$month
= Decode_Month(
$string
[,
$lang
]))>
This function takes a string as its argument, which should contain the
name of a month in the
given
or currently selected language (see further below
for
details about the multi-language support of this
package
), or any uniquely
identifying abbreviation of a month's name (i.e., the first few letters),
and returns the corresponding number (1..12) upon a successful match, or
"C<0>"
otherwise (therefore, the
return
value can also be used as the
conditional expression in an
"if"
statement).
Note that the input string may not contain any other characters which
do
not
pertain to the month's name, especially
no
leading or trailing whitespace.
Note also that matching is performed in a case-insensitive manner (this may
depend on the
"locale"
setting on your current
system
, though!)
With
"1"
(
"English"
) as the
given
language, the following examples will
all
return
the value
"C<9>"
:
$month
= Decode_Month(
"s"
,1);
$month
= Decode_Month(
"Sep"
,1);
$month
= Decode_Month(
"septemb"
,1);
$month
= Decode_Month(
"September"
,1);
=item *
C<
if
(
$dow
= Decode_Day_of_Week(
$string
[,
$lang
]))>
This function takes a string as its argument, which should contain the
name of a day of week in the
given
or currently selected language (see further
below
for
details about the multi-language support of this
package
), or any
uniquely identifying abbreviation of the name of a day of week (i.e., the
first few letters), and returns the corresponding number (1..7) upon a
successful match, or
"C<0>"
otherwise (therefore, the
return
value can
also be used as the conditional expression in an
"if"
statement).
Note that the input string may not contain any other characters which
do
not pertain to the name of the day of week, especially
no
leading
or trailing whitespace.
Note also that matching is performed in a case-insensitive manner (this may
depend on the
"locale"
setting on your current
system
, though!)
With
"1"
(
"English"
) as the
given
language, the following examples will
all
return
the value
"C<3>"
:
$dow
= Decode_Day_of_Week(
"w"
,1);
$dow
= Decode_Day_of_Week(
"Wed"
,1);
$dow
= Decode_Day_of_Week(
"wednes"
,1);
$dow
= Decode_Day_of_Week(
"Wednesday"
,1);
=item *
C<
if
(
$lang
= Decode_Language(
$string
))>
This function takes a string as its argument, which should contain the
name of one of the languages supported by this
package
(B<IN THIS VERY
LANGUAGE ITSELF>), or any uniquely identifying abbreviation of the name
of a language (i.e., the first few letters), and returns its corresponding
internal number (1..14 in the original distribution) upon a successful match,
or
"C<0>"
otherwise (therefore, the
return
value can also be used as the
conditional expression in an
"if"
statement).
Note that the input string may not contain any other characters which
do
not pertain to the name of a language, especially
no
leading or trailing
whitespace.
Note also that matching is performed in a case-insensitive manner (this may
depend on the
"locale"
setting on your current
system
, though!)
The original distribution supports the following fourteen languages:
English ==> 1 (
default
)
Français (French) ==> 2
Deutsch (German) ==> 3
Español (Spanish) ==> 4
Português (Portuguese) ==> 5
Nederlands (Dutch) ==> 6
Italiano (Italian) ==> 7
Norsk (Norwegian) ==> 8
Svenska (Swedish) ==> 9
Dansk (Danish) ==> 10
suomi (Finnish) ==> 11
Magyar (Hungarian) ==> 12
polski (Polish) ==> 13
Romaneste (Romanian) ==> 14
See the section
"How to install additional languages"
in the file
"INSTALL.txt"
in this distribution
for
how to add more languages
to this
package
.
In the original distribution (
no
other languages installed),
the following examples will all
return
the value
"C<3>"
:
$lang
= Decode_Language(
"d"
);
$lang
= Decode_Language(
"de"
);
$lang
= Decode_Language(
"Deutsch"
);
Note that you may not be able to enter the special international characters
in some of the languages' names over the keyboard directly on some systems.
This should never be a problem, though; just enter an abbreviation of the
name of the language consisting of the first few letters up to the character
before
the first special international character.
=item *
C<
if
((
$year
,
$month
,
$day
) = Decode_Date_EU(
$string
[,
$lang
]))>
This function scans a
given
string and tries to parse any date
which might be embedded in it.
The function returns an empty list
if
it can't successfully
extract a valid date from its input string, or
else
it returns
the date found.
The function accepts almost any
format
, as long as the date is
given
in the european order (hence its name) day-month-year.
Thereby, zero or more B<NON-NUMERIC> characters may B<PRECEDE>
the day and B<FOLLOW> the year.
Moreover, zero or more B<NON-ALPHANUMERIC> characters are permitted
B<BETWEEN> these three items (i.e., between day and month and between
month and year).
The month may be
given
either numerically (i.e., a number from
"C<1>"
to
"C<12>"
), or alphanumerically, i.e., as the name of the month in
the
given
or currently selected language, or any uniquely identifying
abbreviation thereof.
(See further below
for
details about the multi-language support of this
package
!)
If the year is
given
as one or two digits only (i.e.,
if
the year is less
than 100), it is mapped to a
"window"
of +/- 50 years
around
the current
year, as described by the
"Moving_Window()"
function (see further below).
If the day, month and year are all
given
numerically but B<WITHOUT> any
delimiting characters between them, this string of digits will be mapped
to the day, month and year as follows:
Length: Mapping:
3 dmy
4 dmyy
5 dmmyy
6 ddmmyy
7 dmmyyyy
8 ddmmyyyy
(Where
"d"
stands
for
"day"
,
"m"
stands
for
"month"
and
"y"
stands
for
"year"
.)
All other strings consisting purely of digits (without any intervening
delimiters) are rejected, i.e., not recognized.
Examples:
"3.1.64"
"3 1 64"
"03.01.64"
"03/01/64"
"3. Jan 1964"
"Birthday: 3. Jan '64 in Backnang/Germany"
"03-Jan-64"
"3.Jan1964"
"3Jan64"
"030164"
"3ja64"
"3164"
Experiment! (See the corresponding example applications in the
"examples"
subdirectory of this distribution in order to
do
so.)
=item *
C<
if
((
$year
,
$month
,
$day
) = Decode_Date_US(
$string
[,
$lang
]))>
This function scans a
given
string and tries to parse any date
which might be embedded in it.
The function returns an empty list
if
it can't successfully
extract a valid date from its input string, or
else
it returns
the date found.
The function accepts almost any
format
, as long as the date is
given
in the U.S. american order (hence its name) month-day-year.
Thereby, zero or more B<NON-ALPHANUMERIC> characters may B<PRECEDE>
and B<FOLLOW> the month (i.e., precede the month and separate it from
the day which follows behind).
Moreover, zero or more B<NON-NUMERIC> characters are permitted
B<BETWEEN> the day and the year, as well as B<AFTER> the year.
The month may be
given
either numerically (i.e., a number from
"C<1>"
to
"C<12>"
), or alphanumerically, i.e., as the name of the month in
the
given
or currently selected language, or any uniquely identifying
abbreviation thereof.
(See further below
for
details about the multi-language support of this
package
!)
If the year is
given
as one or two digits only (i.e.,
if
the year is less
than 100), it is mapped to a
"window"
of +/- 50 years
around
the current
year, as described by the
"Moving_Window()"
function (see further below).
If the month, day and year are all
given
numerically but B<WITHOUT> any
delimiting characters between them, this string of digits will be mapped
to the month, day and year as follows:
Length: Mapping:
3 mdy
4 mdyy
5 mddyy
6 mmddyy
7 mddyyyy
8 mmddyyyy
(Where
"m"
stands
for
"month"
,
"d"
stands
for
"day"
and
"y"
stands
for
"year"
.)
All other strings consisting purely of digits (without any intervening
delimiters) are rejected, i.e., not recognized.
If only the day and the year form a contiguous string of digits, they
will be mapped as follows:
Length: Mapping:
2 dy
3 dyy
4 ddyy
5 dyyyy
6 ddyyyy
(Where
"d"
stands
for
"day"
and
"y"
stands
for
"year"
.)
Examples:
"1 3 64"
"01/03/64"
"Jan 3 '64"
"Jan 3 1964"
"===> January 3rd 1964 (birthday)"
"Jan31964"
"Jan364"
"ja364"
"1364"
Experiment! (See the corresponding example applications in the
"examples"
subdirectory of this distribution in order to
do
so.)
=item *
C<
$year
= Fixed_Window(
$yy
);>
This function applies a
"fixed window"
strategy to two-digit year
numbers in order to convert them into four-digit year numbers.
All other year numbers are passed through unchanged, except
for
negative year numbers, which cause the function to
return
zero
(
"C<0>"
) instead.
Two-digit year numbers
"C<yy>"
below 70 are converted to
"C<20yy>"
,
whereas year numbers equal to or greater than 70 (but less than 100)
are converted to
"C<19yy>"
.
In the original distribution of this
package
, the base century is
set to
"1900"
and the base year to
"70"
(which is a standard on UNIX
systems), but these constants (also called the
"epoch"
) can actually
be chosen at will (in the files
"DateCalc.c"
and
"DateCalc.h"
) at
compile
time
of this module.
=item *
C<
$year
= Moving_Window(
$yy
);>
This function applies a
"moving window"
strategy to two-digit year
numbers in order to convert them into four-digit year numbers, provided
the necessary
system
calls (
system
clock) are available. Otherwise the
function falls back to the
"fixed window"
strategy described in the
function above.
All other year numbers are passed through unchanged, except
for
negative year numbers, which cause the function to
return
zero
(
"C<0>"
) instead.
Two-digit year numbers are mapped according to a
"window"
of 50 years
in both directions (past and future)
around
the current year.
That is, two-digit year numbers are first mapped to the same century
as the current year. If the resulting year is smaller than the current
year minus 50, then one more century is added to the result. If the
resulting year is equal to or greater than the current year plus 50,
then a century is subtracted from the result.
=item *
C<
$date
= Compress(
$year
,
$month
,
$day
);>
WARNING: This function is legacy code, its
use
is deprecated!
This function encodes a date in 16 bits, which is the value being returned.
The encoding scheme is as follows:
Bit number: FEDCBA9 8765 43210
Contents: yyyyyyy mmmm ddddd
(Where the
"yyyyyyy"
contain the number of the year,
"mmmm"
the number of
the month and
"ddddd"
the number of the day.)
The function returns
"C<0>"
if
the
given
input
values
do
not represent a
valid date. Therefore, the
return
value of this function can also be used
as the conditional expression in an
"if"
statement, in order to check
whether the
given
input
values
constitute a valid date).
Through this special encoding scheme, it is possible to B<COMPARE>
compressed dates
for
equality and order (less than/greater than)
B<WITHOUT> any previous B<DECODING>!
Note however that contiguous dates
do
B<NOT> necessarily have contiguous
compressed representations!
I.e., incrementing the compressed representation of a date B<MAY OR MAY NOT>
yield a valid new date!
Note also that this function can only handle dates within one century.
This century can be chosen at will (at compile
time
of this module)
by defining a base century and year (also called the
"epoch"
). In the
original distribution of this
package
, the base century is set to
"1900"
and the base year to
"70"
(which is standard on UNIX systems).
This allows this function to handle dates from
"1970"
up to
"2069"
.
If the
given
year is equal to,
say
,
"95"
, this
package
will automatically
assume that you really meant
"1995"
instead. However,
if
you specify a year
number which is B<SMALLER> than 70, like
"64"
,
for
instance, this
package
will assume that you really meant
"2064"
.
You are not confined to two-digit (abbreviated) year numbers, though.
The function also accepts
"full-length"
year numbers, provided that they
lie in the supported range (i.e., from
"1970"
to
"2069"
, in the original
configuration of this
package
).
Note that this function is maintained mainly
for
backward compatibility,
and that its
use
is not recommended.
=item *
C<
if
((
$century
,
$year
,
$month
,
$day
) = Uncompress(
$date
))>
WARNING: This function is legacy code, its
use
is deprecated!
This function decodes dates that were encoded previously using the function
"C<Compress()>"
.
It returns the century, year, month and day of the date encoded in
"C<$date>"
if
"C<$date>"
represents a valid date, or an empty list otherwise.
The year returned in
"C<$year>"
is actually a two-digit year number
(i.e., the year number taken modulo 100), and only the expression
"C<$century + $year>"
yields the
"full-length"
year number
(
for
example, C<1900 + 95 = 1995>).
Note that this function is maintained mainly
for
backward compatibility,
and that its
use
is not recommended.
=item *
C<
if
(check_compressed(
$date
))>
WARNING: This function is legacy code, its
use
is deprecated!
This function returns
"true"
(
"C<1>"
)
if
the
given
input value
constitutes a valid compressed date, and
"false"
(
"C<0>"
) otherwise.
Note that this function is maintained mainly
for
backward compatibility,
and that its
use
is not recommended.
=item *
C<
$string
= Compressed_to_Text(
$date
[,
$lang
]);>
WARNING: This function is legacy code, its
use
is deprecated!
This function returns a string of fixed
length
(always 9 characters long)
containing a textual representation of the compressed date encoded in
"C<$date>"
.
This string
has
the form
"dd-Mmm-yy"
, where
"dd"
is the two-digit number
of the day,
"Mmm"
are the first three letters of the name of the month
in the
given
or currently selected language (see further below
for
details
about the multi-language support of this
package
), and
"yy"
is the two-digit
year number (i.e., the year number taken modulo 100).
If
"C<$date>"
does not represent a valid date, the string
"??-???-??"
is
returned instead.
Note that this function is maintained mainly
for
backward compatibility,
and that its
use
is not recommended.
=item *
C<
$string
= Date_to_Text(
$year
,
$month
,
$day
[,
$lang
]);>
This function returns a string containing a textual representation of the
given
date of the form
"www dd-Mmm-yyyy"
, where
"www"
are the first three
letters of the name of the day of week in the
given
or currently selected
language, or a special abbreviation,
if
special abbreviations have been
defined
for
the
given
or currently selected language (see further below
for
details about the multi-language support of this
package
),
"dd"
is the day
(one or two digits),
"Mmm"
are the first three letters of the name of the
month in the
given
or currently selected language, and
"yyyy"
is the number
of the year in full
length
.
If the
given
input
values
do
not constitute a valid date, a fatal "not a
valid date" error occurs.
(See the section
"RECIPES"
near the end of this document
for
a code snippet
for
how to
print
dates in any
format
you like.)
=item *
C<
$string
= Date_to_Text_Long(
$year
,
$month
,
$day
[,
$lang
]);>
This function returns a string containing a textual representation of the
given
date roughly of the form
"Wwwwww, dd Mmmmmm yyyy"
, where
"Wwwwww"
is the name of the day of week in the
given
or currently selected language
(see further below
for
details about the multi-language support of this
package
),
"dd"
is the day (one or two digits),
"Mmmmmm"
is the name of the month
in the
given
or currently selected language, and
"yyyy"
is the number of
the year in full
length
.
The exact
format
of the output string depends on the
given
or currently
selected language. In the original distribution of this
package
, these
formats are
defined
as follows:
1 English :
"Wwwwww, Mmmmmm ddth yyyy"
2 French :
"Wwwwww dd mmmmmm yyyy"
3 German :
"Wwwwww, den dd. Mmmmmm yyyy"
4 Spanish :
"Wwwwww, dd de mmmmmm de yyyy"
5 Portuguese :
"Wwwwww, dia dd de mmmmmm de yyyy"
6 Dutch :
"Wwwwww, dd mmmmmm yyyy"
7 Italian :
"Wwwwww, dd Mmmmmm yyyy"
8 Norwegian :
"wwwwww, dd. mmmmmm yyyy"
9 Swedish :
"wwwwww, dd mmmmmm yyyy"
10 Danish :
"wwwwww, dd. mmmmmm yyyy"
11 Finnish :
"wwwwww, dd. mmmmmmta yyyy"
12 Hungarian :
"dd. Mmmmmm yyyy., wwwwww"
13 Polish :
"Wwwwww, dd Mmmmmm yyyy"
14 Romanian :
"Wwwwww dd Mmmmmm yyyy"
(You can change these formats in the file
"DateCalc.c"
before
building this module in order to suit your personal preferences.)
If the
given
input
values
do
not constitute a valid date, a fatal
"not a valid date"
error occurs.
In order to capitalize the day of week at the beginning of the string
in Norwegian,
use
"C<ucfirst(Date_to_Text_Long($year,$month,$day,8));>"
.
(See the section
"RECIPES"
near the end of this document
for
an example on how to
print
dates in any
format
you like.)
=item *
C<
$string
= English_Ordinal(
$number
);>
This function returns a string containing the (english) abbreviation
of the ordinal number
for
the
given
(cardinal) number
"C<$number>"
.
I.e.,
0
=>
'0th'
10
=>
'10th'
20
=>
'20th'
1
=>
'1st'
11
=>
'11th'
21
=>
'21st'
2
=>
'2nd'
12
=>
'12th'
22
=>
'22nd'
3
=>
'3rd'
13
=>
'13th'
23
=>
'23rd'
4
=>
'4th'
14
=>
'14th'
24
=>
'24th'
5
=>
'5th'
15
=>
'15th'
25
=>
'25th'
6
=>
'6th'
16
=>
'16th'
26
=>
'26th'
7
=>
'7th'
17
=>
'17th'
27
=>
'27th'
8
=>
'8th'
18
=>
'18th'
28
=>
'28th'
9
=>
'9th'
19
=>
'19th'
29
=>
'29th'
etc.
=item *
C<
$string
= Calendar(
$year
,
$month
[,
$orthodox
[,
$lang
]]);>
This function returns a calendar of the
given
month in the
given
year
(somewhat similar to the UNIX
"C<cal>"
command), in the
given
or currently
selected language (see further below
for
details about the multi-language
support of this
package
).
Example:
print
Calendar(1998,5);
This will
print
:
May 1998
Mon Tue Wed Thu Fri Sat Sun
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
If the optional boolean parameter
"C<$orthodox>"
is
given
and true,
the calendar starts on Sunday instead of Monday.
=item *
C<
$string
= Month_to_Text(
$month
[,
$lang
]);>
This function returns the name of the
given
month in the
given
or currently
selected language (see further below
for
details about the multi-language
support of this
package
).
If the
given
month lies outside of the valid range from
"C<1>"
to
"C<12>"
,
a fatal
"month out of range"
error will occur.
=item *
C<
$string
= Day_of_Week_to_Text(
$dow
[,
$lang
]);>
This function returns the name of the
given
day of week in the
given
or
currently selected language (see further below
for
details about the
multi-language support of this
package
).
If the
given
day of week lies outside of the valid range from
"C<1>"
to
"C<7>"
,
a fatal
"day of week out of range"
error will occur.
=item *
C<
$string
= Day_of_Week_Abbreviation(
$dow
[,
$lang
]);>
This function returns the special abbreviation of the name of the
given
day of week, B<IF> such special abbreviations have been
defined
for
the
given
or currently selected language (see further below
for
details
about the multi-language support of this
package
).
(In the original distribution of this
package
, this was only true
for
Portuguese. Starting
with
version 5.1, abbreviations
for
Polish have
also been introduced. Starting
with
version 5.7, the abbreviations
for
Portuguese have been disabled. So Polish is currently the only language
to define such special abbreviations.)
If not, the first three letters of the name of the day of week in the
given
or currently selected language are returned instead.
If the
given
day of week lies outside of the valid range from
"C<1>"
to
"C<7>"
, a fatal
"day of week out of range"
error will occur.
Currently, this table of special abbreviations is only used by the
functions
"C<Date_to_Text()>"
and
"C<Calendar()>"
, internally.
=item *
C<
$string
= Language_to_Text(
$lang
);>
This function returns the name of any language supported by this
package
when
the internal number representing that language is
given
as input.
The original distribution supports the following fourteen languages:
1 ==> English (
default
)
2 ==> Français (French)
3 ==> Deutsch (German)
4 ==> Español (Spanish)
5 ==> Português (Portuguese)
6 ==> Nederlands (Dutch)
7 ==> Italiano (Italian)
8 ==> Norsk (Norwegian)
9 ==> Svenska (Swedish)
10 ==> Dansk (Danish)
11 ==> suomi (Finnish)
12 ==> Magyar (Hungarian)
13 ==> polski (Polish)
14 ==> Romaneste (Romanian)
See the section
"How to install additional languages"
in the file
"INSTALL.txt"
in this distribution
for
how to add more languages
to this
package
.
See the description of the function
"C<Languages()>"
further below
to determine how many languages are actually available in a
given
installation of this
package
.
=item *
C<
$lang
= Language();>
=item *
C<Language(
$lang
);
=item *
C<
$oldlang
= Language(
$newlang
);
This function can be used to determine which language is currently selected,
and to change the selected language (this latter
use
is deprecated, because
this global setting may cause conflicts between threads or modules running
concurrently).
Thereby,
each
language
has
a unique internal number.
The original distribution contains the following fourteen languages:
1 ==> English (
default
)
2 ==> Français (French)
3 ==> Deutsch (German)
4 ==> Español (Spanish)
5 ==> Português (Portuguese)
6 ==> Nederlands (Dutch)
7 ==> Italiano (Italian)
8 ==> Norsk (Norwegian)
9 ==> Svenska (Swedish)
10 ==> Dansk (Danish)
11 ==> suomi (Finnish)
12 ==> Magyar (Hungarian)
13 ==> polski (Polish)
14 ==> Romaneste (Romanian)
See the section
"How to install additional languages"
in the file
"INSTALL.txt"
in this distribution
for
how to add more languages
to this
package
.
See the description of the function
"C<Languages()>"
further below
to determine how many languages are actually available in a
given
installation of this
package
.
B<BEWARE> that in order
for
your programs to be portable, you should B<NEVER>
actually
use
the internal number of a language in this
package
B<EXPLICITLY>,
because the same number could mean different languages on different systems,
depending on what languages have been added to any
given
installation of this
package
.
Therefore, you should always
use
a statement such as
Language(Decode_Language(
"Name_of_Language"
));
or
DateCalc_Function(
@parameters
,Decode_Language(
"Name_of_Language"
));
to
select
the desired language, and
$language
= Language_to_Text(Language());
or
$old_language
= Language_to_Text(Language(
"Name_of_new_Language"
));
to determine the (previously) selected language.
If the so chosen language is not available in the current installation,
this will result in an appropriate error message, instead of silently
using the wrong (a random) language (which just happens to have the
same internal number in the other installation).
B<BEWARE> that
when
using the function
"C<Language()>"
, the selected
language is a global setting, shared by all threads or modules you
might be running concurrently, thus possibly causing conflicts between
them.
In order to avoid these conflicts, you should B<NEVER>
use
the function
"C<Language()>"
, but should B<ALWAYS> pass a language number (as returned
by the function
"C<Decode_Language()>"
) to the functions which are
language-dependent, which are:
"Decode_Month()"
,
"Decode_Day_of_Week()"
,
"Compressed_to_Text()"
,
"Date_to_Text()"
,
"Date_to_Text_Long()"
,
"Calendar()"
,
"Month_to_Text()"
,
"Day_of_Week_to_Text()"
,
"Day_of_Week_Abbreviation()"
,
"Decode_Date_EU()"
,
"Decode_Date_US()"
,
"Decode_Date_EU2()"
,
"Decode_Date_US2()"
,
"Parse_Date()"
.
Note that
when
you pass an invalid number, such as e.g. zero, or
no
language parameter at all, these functions will revert to their behaviour
in the versions of this module prior to 6.0, which means that the global
setting (as set by
"C<Language()>"
) becomes active again (only in case
of an invalid or missing language parameter!).
In the C library
"DateCalc.c"
, where omitting a parameter is not an option,
passing a zero
for
the language is therefore the recommended way to guarantee
backward compatibility.
=item *
C<
$max_lang
= Languages();>
This function returns the (maximum) number of languages which are
currently available in your installation of this
package
.
(This may vary from installation to installation.)
See the section
"How to install additional languages"
in the file
"INSTALL.txt"
in this distribution
for
how to add more languages
to this
package
.
In the original distribution of this
package
there are fourteen built-in
languages, therefore the value returned by this function will be
"C<14>"
if
no
other languages have been added to your particular installation.
=item *
C<
if
((
$year
,
$month
,
$day
) = Decode_Date_EU2(
$string
[,
$lang
))>
This function is the Perl equivalent of the function
"C<Decode_Date_EU()>"
(implemented in C), included here merely as an example to demonstrate how
easy it is to
write
your own routine in Perl (using regular expressions)
adapted to your own special needs, should the necessity arise, and intended
primarily as a basis
for
your own development.
In one particular case this Perl version is actually slightly more permissive
than its C equivalent, as far as the class of permitted intervening (i.e.,
delimiting) characters is concerned.
(Can you
tell
the subtle, almost insignificant difference by looking at
the code? Or by experimenting? Hint: Try the string
"a3b1c64d"
with
both
functions.)
=item *
C<
if
((
$year
,
$month
,
$day
) = Decode_Date_US2(
$string
[,
$lang
))>
This function is the Perl equivalent of the function
"C<Decode_Date_US()>"
(implemented in C), included here merely as an example to demonstrate how
easy it is to
write
your own routine in Perl (using regular expressions)
adapted to your own special needs, should the necessity arise, and intended
primarily as a basis
for
your own development.
In one particular case this Perl version is actually slightly more permissive
than its C equivalent.
(Hint: This is the same difference as
with
the
"C<Decode_Date_EU()>"
and
"C<Decode_Date_EU2()>"
pair of functions.)
In a different case, the C version is a little bit more permissive than its
Perl equivalent.
(Can you
tell
the difference by looking at the code? Or by experimenting?
Hint: Try the string
"(1/364)"
with
both functions.)
=item *
C<
if
((
$year
,
$month
,
$day
) = Parse_Date(
$string
[,
$lang
))>
This function is useful
for
parsing dates as returned by the UNIX
"C<date>"
command or as found in the headers of e-mail (in order to determine the
date at which some e-mail
has
been sent or received,
for
instance).
Example
(
$year
,
$month
,
$day
) = Parse_Date(`/bin/date`);
Example
while
(<MAIL>)
{
if
(/^From \S/)
{
(
$year
,
$month
,
$day
) = Parse_Date(
$_
);
...
}
...
}
The function returns an empty list
if
it can't extract a valid date from
the input string.
=item *
C<
$lower
= ISO_LC(
$string
);>
Returns a copy of the
given
string where all letters of the ISO-Latin-1
character set have been replaced by their lower case equivalents.
Similar to Perl's built-in function
"C<lc()>"
(see L<perlfunc(1)/
lc
>) but
for
the whole ISO-Latin-1 character set, not just plain ASCII.
=item *
C<
$upper
= ISO_UC(
$string
);>
Returns a copy of the
given
string where all letters of the ISO-Latin-1
character set have been replaced by their upper case equivalents.
Similar to Perl's built-in function
"C<uc()>"
(see L<perlfunc(1)/
uc
>) but
for
the whole ISO-Latin-1 character set, not just plain ASCII.
=item *
C<
$string
= Date::Calc::Version();>
This function returns a string
with
the (numeric) version number of the
S<C library> (
"DateCalc.c"
) at the core of this
package
(which is also
(automatically) the version number of the
"Calc.xs"
file).
Note that under all normal circumstances, this version number should be
identical
with
the one found in the Perl variable
"C<$Date::Calc::VERSION>"
(the version number of the
"Calc.pm"
file).
Since this function is not exported, you always have to qualify it explicitly,
i.e.,
"C<Date::Calc::Version()>"
.
This is to avoid possible name space conflicts
with
version functions from
other modules.
=back
=head1 RECIPES
=over 4
=item 1)
How
do
I compare two dates?
Solution
if
(Date_to_Days(
$year1
,
$month1
,
$day1
) <
Date_to_Days(
$year2
,
$month2
,
$day2
))
if
(Date_to_Days(
$year1
,
$month1
,
$day1
) <=
Date_to_Days(
$year2
,
$month2
,
$day2
))
if
(Date_to_Days(
$year1
,
$month1
,
$day1
) >
Date_to_Days(
$year2
,
$month2
,
$day2
))
if
(Date_to_Days(
$year1
,
$month1
,
$day1
) >=
Date_to_Days(
$year2
,
$month2
,
$day2
))
if
(Date_to_Days(
$year1
,
$month1
,
$day1
) ==
Date_to_Days(
$year2
,
$month2
,
$day2
))
if
(Date_to_Days(
$year1
,
$month1
,
$day1
) !=
Date_to_Days(
$year2
,
$month2
,
$day2
))
$cmp
= (Date_to_Days(
$year1
,
$month1
,
$day1
) <=>
Date_to_Days(
$year2
,
$month2
,
$day2
));
Solution
if
(Delta_Days(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
) > 0)
if
(Delta_Days(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
) >= 0)
if
(Delta_Days(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
) < 0)
if
(Delta_Days(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
) <= 0)
if
(Delta_Days(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
) == 0)
if
(Delta_Days(
$year1
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
) != 0)
=item 2)
How
do
I check whether a
given
date lies within a certain range of dates?
$lower
= Date_to_Days(
$year1
,
$month1
,
$day1
);
$upper
= Date_to_Days(
$year2
,
$month2
,
$day2
);
$date
= Date_to_Days(
$year
,
$month
,
$day
);
if
((
$date
>=
$lower
) && (
$date
<=
$upper
))
{
}
else
{
}
=item 3)
How
do
I compare two dates
with
times
? How
do
I check whether two dates
and
times
lie more or less than a
given
time
interval apart?
Solution
@date1
= (2002,8,31,23,59,1);
@date2
= (2002,9,1,11,30,59);
@date3
= Add_Delta_DHMS(
@date1
, 0,12,0,0);
@d2
= ( Date_to_Days(
@date2
[0..2]), (
$date2
[3]*60+
$date2
[4])*60+
$date2
[5] );
@d3
= ( Date_to_Days(
@date3
[0..2]), (
$date3
[3]*60+
$date3
[4])*60+
$date3
[5] );
@diff
= (
$d2
[0]-
$d3
[0],
$d2
[1]-
$d3
[1] );
if
(
$diff
[0] > 0 and
$diff
[1] < 0) {
$diff
[0]--;
$diff
[1] += 86400; }
if
(
$diff
[0] < 0 and
$diff
[1] > 0) {
$diff
[0]++;
$diff
[1] -= 86400; }
if
((
$diff
[0] ||
$diff
[1]) >= 0) {
print
"More than 12 hours.\n"
; }
else
{
print
"Less than 12 hours.\n"
; }
Solution
This solution is only feasible
if
your dates are guaranteed to lie
within the range
given
by your
system
's epoch and overflow date and
time
!
Unix: 1-Jan-1970 00:00:00 to 19-Jan-2038 03:14:07
MacOS: 1-Jan-1904 00:00:00 to 6-Feb-2040 06:28:15
@date1
= (2002,8,31,23,59,1);
@date2
= (2002,9,1,11,30,59);
$d1
= Date_to_Time(
@date1
);
$d2
= Date_to_Time(
@date2
);
if
(
$d1
<=
$d2
) {
print
"The two dates are in chronological order.\n"
; }
else
{
print
"The two dates are in reversed order.\n"
; }
if
(
$d1
+ 12*60*60 <=
$d2
) {
print
"More than 12 hours.\n"
; }
else
{
print
"Less than 12 hours.\n"
; }
=item 4)
How
do
I verify whether someone
has
a certain age?
use
Date::Calc
qw( Decode_Date_EU Today leap_year Delta_Days )
;
$date
= <STDIN>;
(
$year1
,
$month1
,
$day1
) = Decode_Date_EU(
$date
);
(
$year2
,
$month2
,
$day2
) = Today();
if
((
$day1
== 29) && (
$month1
== 2) && !leap_year(
$year2
))
{
$day1
--; }
if
( ((
$year2
-
$year1
) > 18) ||
( ((
$year2
-
$year1
) == 18) &&
(Delta_Days(
$year2
,
$month1
,
$day1
,
$year2
,
$month2
,
$day2
) >= 0) ) )
{
print
"Ok - you are over 18.\n"
;
}
else
{
print
"Sorry - you aren't 18 yet!\n"
;
}
Or, alternatively (substituting the
last
"if"
statement above):
if
((
$year1
+18 <=>
$year2
||
$month1
<=>
$month2
||
$day1
<=>
$day2
) <= 0)
{
print
"Ok - you are over 18.\n"
; }
else
{
print
"Sorry - you aren't 18 yet!\n"
; }
=item 5)
How
do
I calculate the number of the week of month
the current date lies in?
For example:
April 1998
Mon Tue Wed Thu Fri Sat Sun
1 2 3 4 5 = week
6 7 8 9 10 11 12 = week
13 14 15 16 17 18 19 = week
20 21 22 23 24 25 26 = week
27 28 29 30 = week
Solution:
(
$year
,
$month
,
$day
) = Today();
$week
=
int
((
$day
+ Day_of_Week(
$year
,
$month
,1) - 2) / 7) + 1;
=item 6)
How
do
I calculate whether a
given
date is the 1st, 2nd, 3rd, 4th or 5th
of that day of week in the
given
month?
For example:
October 2000
Mon Tue Wed Thu Fri Sat Sun
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
Is Sunday, the 15th of October 2000, the 1st, 2nd, 3rd, 4th or 5th
Sunday of that month?
Solution:
Nth_Weekday_of_Month_Year
Date_to_Text_Long English_Ordinal
Day_of_Week_to_Text Month_to_Text )
;
(
$year
,
$month
,
$day
) = (2000,10,15);
$dow
= Day_of_Week(
$year
,
$month
,
$day
);
$n
=
int
( Delta_Days(
Nth_Weekday_of_Month_Year(
$year
,
$month
,
$dow
,1),
$year
,
$month
,
$day
)
/ 7) + 1;
printf
(
"%s is the %s %s in %s %d.\n"
,
Date_to_Text_Long(
$year
,
$month
,
$day
),
English_Ordinal(
$n
),
Day_of_Week_to_Text(
$dow
),
Month_to_Text(
$month
),
$year
);
This prints:
Sunday, October 15th 2000 is the 3rd Sunday in October 2000.
=item 7)
How
do
I calculate the date of the Wednesday of the same week as
the current date?
Solution
use
Date::Calc
qw( Today Day_of_Week Add_Delta_Days )
;
$searching_dow
= 3;
@today
= Today();
$current_dow
= Day_of_Week(
@today
);
@date
= Add_Delta_Days(
@today
,
$searching_dow
-
$current_dow
);
Solution
Monday_of_Week Week_of_Year )
;
$searching_dow
= 3;
@today
= Today();
@date
= Add_Delta_Days( Monday_of_Week( Week_of_Year(
@today
) ),
$searching_dow
- 1 );
Solution
Business_to_Standard )
;
@business
= Standard_to_Business(Today());
$business
[2] = 3;
@date
= Business_to_Standard(
@business
);
=item 8)
How can I add a week offset to a business date (including across
year boundaries)?
use
Date::Calc
qw( Business_to_Standard Add_Delta_Days
Standard_to_Business )
;
@temp
= Business_to_Standard(
$year
,
$week
,
$dow
);
@temp
= Add_Delta_Days(
@temp
,
$week_offset
* 7);
(
$year
,
$week
,
$dow
) = Standard_to_Business(
@temp
);
=item 9)
How
do
I calculate the
last
and the
next
Saturday
for
any
given
date?
use
Date::Calc
qw( Today Day_of_Week Add_Delta_Days
Day_of_Week_to_Text Date_to_Text )
;
$searching_dow
= 6;
@today
= Today();
$current_dow
= Day_of_Week(
@today
);
if
(
$searching_dow
==
$current_dow
)
{
@prev
= Add_Delta_Days(
@today
,-7);
@next
= Add_Delta_Days(
@today
,+7);
}
else
{
if
(
$searching_dow
>
$current_dow
)
{
@next
= Add_Delta_Days(
@today
,
$searching_dow
-
$current_dow
);
@prev
= Add_Delta_Days(
@next
,-7);
}
else
{
@prev
= Add_Delta_Days(
@today
,
$searching_dow
-
$current_dow
);
@next
= Add_Delta_Days(
@prev
,+7);
}
}
$dow
= Day_of_Week_to_Text(
$searching_dow
);
print
"Today is: "
,
' '
x
length
(
$dow
),
Date_to_Text(
@today
),
"\n"
;
print
"Last $dow was: "
, Date_to_Text(
@prev
),
"\n"
;
print
"Next $dow will be: "
, Date_to_Text(
@next
),
"\n"
;
This will
print
something like:
Today is: Sun 12-Apr-1998
Last Saturday was: Sat 11-Apr-1998
Next Saturday will be: Sat 18-Apr-1998
=item 10)
How can I calculate the
last
business day (payday!) of a month?
Solution
use
Date::Calc
qw( Days_in_Month Day_of_Week Add_Delta_Days )
;
$day
= Days_in_Month(
$year
,
$month
);
$dow
= Day_of_Week(
$year
,
$month
,
$day
);
if
(
$dow
> 5)
{
(
$year
,
$month
,
$day
) =
Add_Delta_Days(
$year
,
$month
,
$day
, 5-
$dow
);
}
Solution
This solution expects a multi-dimensional array
"C<@holiday>"
, which
contains all holidays, as follows:
"C<$holiday[$year][$month][$day] = 1;>"
.
(See the description of the function
"C<Easter_Sunday()>"
further above
for
how to calculate the moving (variable) christian feast days!)
Days which are not holidays remain undefined or should have a value of zero
in this array.
use
Date::Calc
qw( Days_in_Month Add_Delta_Days Day_of_Week )
;
$day
= Days_in_Month(
$year
,
$month
);
while
(1)
{
while
(
$holiday
[
$year
][
$month
][
$day
])
{
(
$year
,
$month
,
$day
) =
Add_Delta_Days(
$year
,
$month
,
$day
, -1);
}
$dow
= Day_of_Week(
$year
,
$month
,
$day
);
if
(
$dow
> 5)
{
(
$year
,
$month
,
$day
) =
Add_Delta_Days(
$year
,
$month
,
$day
, 5-
$dow
);
}
else
{
last
; }
}
Solution
but requires Date::Calendar(3) and Date::Calc::Object(3)):
$calendar
= Date::Calendar->new(
$Profiles
->{
'DE-BW'
} );
@today
= Today();
@nextmonth
= Add_Delta_YM(
@today
[0,1],1, 0,1);
$workaround
=
$calendar
->add_delta_workdays(
@nextmonth
,+1);
$payday
=
$calendar
->add_delta_workdays(
$workaround
,-2);
print
"Pay day = "
, Date_to_Text_Long(
$payday
->date()),
"\n"
;
The
"workaround"
is necessary due to a bug in the method
"add_delta_workdays()"
when
adding a negative number of
workdays.
=item 11)
How
do
I convert a MS Visual Basic
"DATETIME"
value into its date
and
time
constituents?
$datetime
=
"35883.121653"
;
(
$Dd
,
$Dh
,
$Dm
,
$Ds
) = (
$datetime
=~ /^(\d+)\.(\d\d)(\d\d)(\d\d)$/);
(
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
) =
Add_Delta_DHMS(1900,1,1, 0,0,0,
$Dd
,
$Dh
,
$Dm
,
$Ds
);
printf
(
"The given date is %s %02d:%02d:%02d\n"
,
Date_to_Text(
$year
,
$month
,
$day
),
$hour
,
$min
,
$sec
);
This prints:
The
given
date is Tue 31-Mar-1998 12:16:53
Since I
do
not have or
use
Visual Basic, I can't guarantee that
the number
format
assumed here is really the one used by Visual
Basic - but you get the general idea. C<:-)>
Moreover, consider the following:
Morten Sickel <Morten.Sickel
@nrpa
.
no
> wrote:
I discovered a bug in Excel (2000): Excel thinks that 1900 was
a leap year. Users should
use
31-Dec-1899 as the date to add
an Excel date value to in order to get the correct date.
I found out on the web that this bug originated in Lotus 123,
which made 29-Feb-1900 an
"industrial standard"
. MS chose to
keep the bug in order to be compatible
with
Lotus 123. But
they have not mentioned anything about it in the help files.
=item 12)
How can I
send
a reminder to members of a group on the day
before
a meeting which occurs every first Friday of a month?
use
Date::Calc
qw( Today Date_to_Days Add_Delta_YMD
Nth_Weekday_of_Month_Year )
;
(
$year
,
$month
,
$day
) = Today();
$tomorrow
= Date_to_Days(
$year
,
$month
,
$day
) + 1;
$dow
= 5;
$n
= 1;
$meeting_this_month
= Date_to_Days(
Nth_Weekday_of_Month_Year(
$year
,
$month
,
$dow
,
$n
) );
(
$year
,
$month
,
$day
) = Add_Delta_YMD(
$year
,
$month
,
$day
, 0,1,0);
$meeting_next_month
= Date_to_Days(
Nth_Weekday_of_Month_Year(
$year
,
$month
,
$dow
,
$n
) );
if
((
$tomorrow
==
$meeting_this_month
) ||
(
$tomorrow
==
$meeting_next_month
))
{
}
=item 13)
How can I
print
a date in a different
format
than provided by
the functions
"C<Date_to_Text()>"
,
"C<Date_to_Text_Long()>"
or
"C<Compressed_to_Text()>"
?
Day_of_Week Month_to_Text
English_Ordinal )
;
(
$year
,
$month
,
$day
) = Today();
For example
with
leading zeros
for
the day:
"S<Fri 03-Jan-1964>"
printf
(
"%.3s %02d-%.3s-%d\n"
,
Day_of_Week_to_Text(Day_of_Week(
$year
,
$month
,
$day
)),
$day
,
Month_to_Text(
$month
),
$year
);
For example in U.S. american
format
:
"S<April 12th, 1998>"
$string
=
sprintf
(
"%s %s, %d"
,
Month_to_Text(
$month
),
English_Ordinal(
$day
),
$year
);
For example in one of the possible formats as specified by S<ISO 8601>:
@date
= (
$year
,
$month
,
$day
,
$hour
,
$min
,
$sec
);
$date
=
sprintf
(
"%d-%02d-%02d %02d:%02d:%02d"
,
@date
);
(See also L<perlfunc(1)/
printf
> and/or L<perlfunc(1)/
sprintf
>!)
=item 14)
How can I iterate through a range of dates?
@start
= (1999,5,27);
@stop
= (1999,6,1);
$j
= Delta_Days(
@start
,
@stop
);
for
(
$i
= 0;
$i
<=
$j
;
$i
++ )
{
@date
= Add_Delta_Days(
@start
,
$i
);
printf
(
"%4d/%02d/%02d\n"
,
@date
);
}
Note that the loop can be improved; see also the recipe below.
=item 15)
How can I create a (Perl) list of dates in a certain range?
use
Date::Calc
qw( Delta_Days Add_Delta_Days Date_to_Text )
;
sub
date_range
{
my
(
@date
) = (
@_
)[0,1,2];
my
(
@list
);
my
(
$i
);
$i
= Delta_Days(
@_
);
while
(
$i
-- >= 0)
{
push
(
@list
, [
@date
] );
@date
= Add_Delta_Days(
@date
, 1)
if
(
$i
>= 0);
}
return
(
@list
);
}
@range
=
&date_range
(1999,11,3, 1999,12,24);
foreach
$date
(
@range
)
{
print
Date_to_Text(@{
$date
}),
"\n"
;
}
Note that you probably shouldn't
use
this one, because it is much
more efficient to iterate through all the dates (as shown in the
recipe immediately above) than to construct such an array and then
to loop through it. Also, it is much more space-efficient not to
create this array.
=item 16)
How can I calculate the difference in days between dates,
but without counting Saturdays and Sundays?
sub
Delta_Business_Days
{
my
(
@date1
) = (
@_
)[0,1,2];
my
(
@date2
) = (
@_
)[3,4,5];
my
(
$minus
,
$result
,
$dow1
,
$dow2
,
$diff
,
$temp
);
$minus
= 0;
$result
= Delta_Days(
@date1
,
@date2
);
if
(
$result
!= 0)
{
if
(
$result
< 0)
{
$minus
= 1;
$result
= -
$result
;
$dow1
= Day_of_Week(
@date2
);
$dow2
= Day_of_Week(
@date1
);
}
else
{
$dow1
= Day_of_Week(
@date1
);
$dow2
= Day_of_Week(
@date2
);
}
$diff
=
$dow2
-
$dow1
;
$temp
=
$result
;
if
(
$diff
!= 0)
{
if
(
$diff
< 0)
{
$diff
+= 7;
}
$temp
-=
$diff
;
$dow1
+=
$diff
;
if
(
$dow1
> 6)
{
$result
--;
if
(
$dow1
> 7)
{
$result
--;
}
}
}
if
(
$temp
!= 0)
{
$temp
/= 7;
$result
-= (
$temp
<< 1);
}
}
if
(
$minus
) {
return
-
$result
; }
else
{
return
$result
; }
}
This solution is probably of little practical value, however,
because it doesn't take legal holidays into account.
See L<Date::Calendar(3)>
for
how to
do
that.
=item 17)
How can I
"normalize"
the output of the
"Delta_YMDHMS()"
(or
"Delta_YMD()"
)
function so that it contains only positive
values
?
I.e., how can I show a difference in date (and
time
) in a more human-readable
form,
for
example in order to show how much
time
until
(or since) the expiration
of something (e.g. an account, a domain, a credit card, etc.) is left (
has
passed)?
Correct solution: Use the functions
"N_Delta_YMDHMS()"
and
"N_Delta_YMD()"
instead!
The following gives a rudimentary sketch of a (much inferior) solution,
which is maintained here only
for
historical reasons of this module:
a) Delta_YMDHMS():
use
Date::Calc
qw(Today_and_Now Delta_YMDHMS Add_Delta_YMDHMS Delta_DHMS Date_to_Text)
;
my
$today
= [Today_and_Now()];
my
$target
= [2005,1,1,0,0,0];
my
$sign
=
"until"
;
my
$delta
= Normalize_Delta_YMDHMS(
$today
,
$target
);
if
(
$delta
->[0] < 0)
{
$sign
=
"since"
;
$delta
= Normalize_Delta_YMDHMS(
$target
,
$today
);
}
printf
(
"Today is %s %02d:%02d:%02d\n"
, Date_to_Text(@{
$today
}[0..2]), @{
$today
}[3..5]);
printf
(
"%d year%s, %d month%s, %d day%s, %d hour%s, %d minute%s, %d second%s %s %s %02d:%02d:%02d\n"
,
$delta
->[0], ((
$delta
->[0]==1)?
''
:
's'
),
$delta
->[1], ((
$delta
->[1]==1)?
''
:
's'
),
$delta
->[2], ((
$delta
->[2]==1)?
''
:
's'
),
$delta
->[3], ((
$delta
->[3]==1)?
''
:
's'
),
$delta
->[4], ((
$delta
->[4]==1)?
''
:
's'
),
$delta
->[5], ((
$delta
->[5]==1)?
''
:
's'
),
$sign
,
Date_to_Text(@{
$target
}[0..2]),
@{
$target
}[3..5]
);
sub
Normalize_Delta_YMDHMS
{
my
(
$date1
,
$date2
) =
@_
;
my
(
@delta
);
@delta
= Delta_YMDHMS(
@$date1
,
@$date2
);
while
(
$delta
[1] < 0 or
$delta
[2] < 0 or
$delta
[3] < 0 or
$delta
[4] < 0 or
$delta
[5] < 0)
{
if
(
$delta
[1] < 0) {
$delta
[0]--;
$delta
[1] += 12; }
if
(
$delta
[2] < 0)
{
$delta
[1]--;
@delta
[2..5] = (0,0,0,0);
@delta
[2..5] = Delta_DHMS(Add_Delta_YMDHMS(
@$date1
,
@delta
),
@$date2
);
}
if
(
$delta
[3] < 0) {
$delta
[2]--;
$delta
[3] += 24; }
if
(
$delta
[4] < 0) {
$delta
[3]--;
$delta
[4] += 60; }
if
(
$delta
[5] < 0) {
$delta
[4]--;
$delta
[5] += 60; }
}
return
\
@delta
;
}
b) Delta_YMD():
use
Date::Calc
qw(Today Delta_YMD Add_Delta_YM Delta_Days Date_to_Text)
;
my
(
$sign
,
$delta
);
my
$today
= [Today()];
my
$target
= [2005,1,1];
if
(Delta_Days(
@$today
,
@$target
) < 0)
{
$sign
=
"since"
;
$delta
= Normalize_Delta_YMD(
$target
,
$today
);
}
else
{
$sign
=
"until"
;
$delta
= Normalize_Delta_YMD(
$today
,
$target
);
}
print
"Today is "
, Date_to_Text(
@$today
),
"\n"
;
printf
(
"%d year%s, %d month%s, %d day%s %s %s\n"
,
$delta
->[0], ((
$delta
->[0]==1)?
''
:
's'
),
$delta
->[1], ((
$delta
->[1]==1)?
''
:
's'
),
$delta
->[2], ((
$delta
->[2]==1)?
''
:
's'
),
$sign
,
Date_to_Text(
@$target
)
);
sub
Normalize_Delta_YMD
{
my
(
$date1
,
$date2
) =
@_
;
my
(
@delta
);
@delta
= Delta_YMD(
@$date1
,
@$date2
);
while
(
$delta
[1] < 0 or
$delta
[2] < 0)
{
if
(
$delta
[1] < 0) {
$delta
[0]--;
$delta
[1] += 12; }
if
(
$delta
[2] < 0)
{
$delta
[1]--;
$delta
[2] = Delta_Days(Add_Delta_YM(
@$date1
,
@delta
[0,1]),
@$date2
);
}
}
return
\
@delta
;
}
Note that
for
normalizing just a
time
vector, you can
use
the built-in
function
"Normalize_DHMS()"
. However, this will yield either all positive
B<OR> all negative
values
, B<NOT> all positive
values
as above.
=back
=head1 SEE ALSO
Date::Calc::Util(3), Date::Calc::Object(3),
Date::Calendar(3), Date::Calendar::Year(3),
Date::Calendar::Profiles(3).
"The Calendar FAQ"
:
by Claus Tondering <claus
@tondering
.dk>
=head1 BEWARE
When you are using the (deprecated) function
"Language()"
, the language
setting is stored in a global variable.
This may cause conflicts between threads or modules running concurrently.
Therefore, in order to avoid such conflicts, NEVER
use
the function
"Language()"
, but ALWAYS pass a language parameter to the functions
which are language-dependent.
=head1 VERSION
This man page documents
"Date::Calc"
version 6.4.
=head1 AUTHOR
Steffen Beyer
mailto:STBEY
@cpan
.org
=head1 COPYRIGHT
Copyright (c) 1995 - 2015 by Steffen Beyer. All rights reserved.
=head1 LICENSE
This
package
is free software; you can
use
, modify and redistribute
it under the same terms as Perl itself, i.e., at your option, under
the terms either of the
"Artistic License"
or the "GNU General Public
License".
The C library at the core of the module
"Date::Calc::XS"
can, at your
discretion, also be used, modified and redistributed under the terms
of the
"GNU Library General Public License"
.
Please refer to the files
"Artistic.txt"
,
"GNU_GPL.txt"
and
"GNU_LGPL.txt"
in the
"license"
subdirectory of this distribution
for
any details!
=head1 DISCLAIMER
This
package
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.