Tải bản đầy đủ (.pdf) (6 trang)

Date and Time Manipulation

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (72.22 KB, 6 trang )

19
■ ■ ■
CHAPTER 3
Date and Time Manipulation
O
n occasion, the need for date math arises, such as when you’re trying to calculate a
time interval between events. The calculations seem easy enough because there are pre-
cise numbers of seconds, minutes, and hours in a day. It gets tricky, though, when you
consider the fact that values have to roll over when, for example, Monday turns into Tues-
day or June becomes July.
For instance, calculating the difference in minutes between 6:53 am and 7:04 am is easy
enough: you can multiply the hours (6 and 7) by 60 for each value, add the minutes that do
not make up the next full hour, then subtract to find the difference. But what if you want
the difference in minutes between 11:57 pm and 1:13 am? This calculation is more com-
plex because it involves a change in day. And the complexity only increases when the date
interval spans months or years.
Date in Days
The following script shows one way to make date and time calculations much easier.
Because UNIX and Linux calculate time based on a starting point of January 1, 1970, the
script measures time in seconds since that date.
Although the use of seconds may seem cumbersome, the math is simple, as you’ll see
in the bit of code in the “Days Since Epoch” section of this chapter. You reduce the date
and time values to numbers of seconds elapsed since the base date then manipulate these
values. All of the issues that arise when spanning across calendar increments, such as days
or months, simply disappear.
You might use this type of calculation when determining the age of a user’s password.
The third field of an account entry in a system /etc/shadow file contains the day value on
which the password was changed for a particular account, as counted from 1/1/1970 (the
epoch). This number can be used for various purposes—for example, to determine when
passwords are about to expire so as to facilitate user notifications. You can find an exam-
ple of this in Chapter 36 in connection with password aging.


Converting all temporal quantities to elapsed time also reduces the complexity of
making time comparisons. Suppose, for example, that you would like to monitor time
synchronization between multiple network nodes. When you convert the time on a
20
CHAPTER 3

DATE AND TIME MANIPULATION
system to seconds elapsed since the beginning of the UNIX epoch, the calculation
becomes a simple subtraction.

Caution
Yes, the Network Time Protocol (NTP) keeps system clocks in sync. However, not all systems
run NTP implementations. Also, clocks on some aging hardware keep such poor time that even NTP can‘t
keep them in sync. NTP implementations can generally keep system clocks synchronized, but if a particular
clock drifts beyond the panic threshold, NTP will not update the clock. Additionally, even where NTP is ubiq-
uitous, systems can fail.
The following “Days Since Epoch” script calculates the number of days between two dates.
The valid dates for this equation (taken from the Gregorian calendar) range from October 15,
1582 to December 31, 9999. Dates outside this range (or dates from different calendars)
require a different equation. This script is a fairly longhand way of getting these values, but the
benefit is that it will run on most any system using ksh or bash. The alternatives may not.
The script is based on the following formula. When the program runs, it calculates and
displays the number of days that have elapsed since January 1, 1970 by determining the
number for 1/1/1970 and subtracting that from the number for the current date.
(Year*365)+(Year/4)-(Year/100)+(Year/400)+(Month*306001/10000)+(Day)
There are a couple of caveats to using this formula to account for dates that land on a
number line. In that case, before you perform the calculation, the values of Month and Year
may need to be altered: for the months of January (1) and February (2), you must add 13
to Month and subtract 1 from Year; for all other months you simply add 1 to Month to return
the correct value. The Day value to be used is always the day of the month. Thus, the equa-

tion applied to January 1, 1970, is as follows:
(1969*365)+(1969/4)-(1969/100)+(1969/400)+(14*306001/10000)+1
Days Since Epoch
The start of the script sets the variables for the current time and date. Since the epoch
(1/1/1970) is fixed, its value can be calculated once and the constant 719591 used in its
place, thus saving some CPU cycles.
#!/bin/sh
epoch_days=719591
second=`date +'%S'`
minute=`date +'%M'`
hour=`date +'%k'`
day=`date +'%d'`
month=`date +'%m' | sed 's/0*//'`
year=`date +'%Y'`
CHAPTER 3

DATE AND TIME MANIPULATION
21
You could improve the script’s performance as follows, although it reduces readability.
Instead of performing a date call to set each time and date variable, you could make one
date call that outputs space-delimited values, then place those values into an array.
To initialize the array in ksh, use
set -A DATE `date +"%S %M %k %d %m %Y"
In bash, use
declare -a DATE=( `date +"%S %M %k %d %m %Y"` )
For example, to access the third array element in either bash or ksh, use something like
echo ${DATE[2]}
where 2 is referencing the third element in the DATE array. Note that the first element in an
array would be accessed with a subscript of 0.
The following code makes the initial changes to the month and year variables the equa-

tion needs:
if [ $month -gt 2 ]
then
month=$(($month+1))
else
month=$(($month+13))
year=$(($year-1))
fi
If the month is not January or February (greater than 1 or 2), you have to add 1 to the
month. Otherwise you have to add 13 to the month and subtract 1 from the year.
The following code calculates the day value for today. Once you know this, you subtract
the epoch value from that value to get the number of days since the start of the epoch. The
script then outputs that value. The output is left unformatted in case you want to use the
number as input for another command or process.
today_days=$((($year*365)+($year/4)-\ ($year/100)+($year/400)+\
($month*306001/10000)+$day))
days_since_epoch=$(($today_days-$epoch_days))
echo $days_since_epoch
You may find it useful to have two versions of this script: one that outputs the elapsed
time in days and the other that outputs it in seconds.
seconds_since_epoch=`echo "($days_since_epoch*86400)+\
($hour*3600)+($minute*60)+$second" | bc`
The calculation to convert from days to seconds is fairly trivial. It may also be useful to
turn the code for the calculations into functions and put them in your central library as
discussed in Chapter 2. You then would need only to source this library into your current
environment and call the function whenever needed.
22
CHAPTER 3

DATE AND TIME MANIPULATION

Alternatives for Finding the Date in Seconds
There are two other ways to calculate the number of seconds since the epoch. Both of
them are much simpler than the preceding script, but they require system utilities that
you may not have installed, such as Perl and the latest GNU utilities. Most administrators
would probably install whatever is needed to get the job done, but there are controlled
production environments where it’s not that simple—sometimes many requirements
must be met and testing must be performed before any changes are made to a system.
In those cases, it is simpler to come up with a solution that utilizes existing resources as
opposed to installing more-advanced tools.
The first alternative uses the GNU version of the date command. If you have this ver-
sion, you can produce output that is almost identical to that of the script discussed in the
section “Days Since Epoch” except that, because the number of seconds since epoch in
that script is calculated based on GMT, it may be out of sync with your local time zone. If
so, you may want to add the appropriate number of seconds for your local time zone.
(This may not be necessary if you’re using the values to simply calculate the difference
between two arbitrary dates/times in which the local time zone information is irrele-
vant.)The following date command is much simpler than deriving the calculations
manually. This returns the number of seconds since epoch directly.
gnu_seconds_since_epoch=`date +%s`
There is also a Perl function for performing the same task. You can access it like this:
perl_seconds_since_epoch=`perl -e 'print time'`
Evaluating for the Current Day and Time
Say you want to schedule a job, such as a system monitor, to run at particular times or
on certain days. You want to know whether there are issues on the system, but you don’t
necessarily want to be jarred awake by your pager simply to learn that the message is non-
critical; you’d like to get those routine notices by page during the day and by e-mail at
other times.
The following script determines whether the current day and hour are within a certain
time frame that you set. This code would be called from another script, which actually
performs the notifications.

Two sets of day and hour parameters (for a total of four) are passed to the script when
it is called. These parameters specify a range of days (Sunday through Monday) and a
range of hours when pages may be sent during those days. The script returns a 0 if the cur-
rent day and hour are within those parameters and gives the user a message stating the
CHAPTER 3

DATE AND TIME MANIPULATION
23
same. If the current day and hour values do not lie within the given range, a different mes-
sage is output and the function returns a 1 (representing failure).
#!/bin/sh
if [ $# -ne 4 ]
then
echo Usage: $0 {day begin} {day end} {hour begin} {hour end}
echo " Days are 0-6 where 0 is Sunday."
echo " Hours are 0-23."
exit
fi
The script starts by determining how many parameters have been sent to the script.
Recall that four is the expected number. If four parameters haven’t been sent, such as
if the script calling this code were written incorrectly, you should output a usage message
containing some explanation of how the script should be invoked. The usage explanation
provided here shows that the four parameters that should be passed are DAY_BEGIN,
DAY_END, HOUR_BEGIN, and HOUR_END. All of these values are integers in which the day values
range from 0–6 where Sunday is 0, and the hours range from 0–23.
If the parameter count is correct, the code assigns the parameters to variables with
more meaningful names, such as DAY_BEGIN and DAY_END instead of 1 and 2. Making this
change helps the readability and it is easier to see what is happening.
DAY_BEGIN=$1
DAY_END=$2

HOUR_BEGIN=$3
HOUR_END=$4
Next, the variables for the current day and hour need to be set.
DAY=`date +%w`
HOUR=`date +%H`
The code here is the main check to determine whether it is time to notify the adminis-
trator. The large if statement compares the current DAY and HOUR values with the values
that were passed to the script.
if [ $DAY -ge $DAY_BEGIN -a $DAY -le $DAY_END\
-a $HOUR -ge $HOUR_BEGIN -a $HOUR -le $HOUR_END ]
then
echo "It is time to notify"
return 0
else
echo "It is not time to notify"
return 1
fi

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×