Adding Months to a date in assembly language
- From: "randyhyde@xxxxxxxxxxxxx" <randyhyde@xxxxxxxxxxxxx>
- Date: Tue, 26 Jun 2007 12:10:44 -0700
Here's a routine that will add some number of months to a date in
assembly language.
Note that adding a month is not as trivial an operation as
incrementing the month field of the HLA data structure (and possibly
handling overflow into some other year):
type
daterec:
union
record
day :uns8;
month :uns8;
year :uns16;
endrecord;
date :dword;
endunion;
Consider, for example, what happens if you add one month to the date
1/31/2007. If you simply increment the month value, you wind up with
2/31/2007 which is an invalid date. Therefore, something has to be
done when adding one month to a date to ensure that you wind up with a
valid date.
To my knowledge, there is no standard, accepted, way of handling this.
Different people (and different library routines) handle this case in
different ways. For example, some algorithms add the number of days in
the current month to the date; for example, adding one month to
1/31/2007 would yield 3/3/2007 (because 2007 is not a leap year). I
have rejected that approach because 1+1 should equal 2, not 3 (that
is, Jan + one month should really be February).
Another approach I've seen is to add the number of days in the *next*
month to the current date. For example, 1/31/2007 plus one month would
properly yield 2/28/2007 using this scheme. However, that scheme fails
when you add 28 (the days in Feb, 2007) to 1/1/2007 (producing
1/29/2007, leaving you in January).
The approach I've gone with is to add the value to the month field
(updating the year field as necessary) and then checking the resulting
date. If the day field contains an inappropriate value for the new
month, I truncate it to the end of the month. Therefore, 1/31/2007 + 1
month would produce 2/31/2007, which I then truncate to 2/28/2007.
This solution is not perfect. If you add one month to a date and then
subtract one month from a date, you may not wind up with the date you
started with. However, as months contain a differing number of days
(depending on month and year), I'm willing to live with this anomaly.
The prototype for the function takes the following form:
procedure addMonths( months:uns32; var theDate:daterec );
Note that the months parameter can be any unsigned value, it is not
limited to a value in the range 1..12. This means that the algorithm
must divide the result by 12 and add the quotient to the year field
and the remainder to the months field (and, if overflow beyond month
12 occurs, update the month and year fields accordingly).
Here's the assembly code to achieve this:
// addMonths-
//
// This function adds the specified number of Months to
// the date passed by reference.
procedure date.addMonths
(
months: uns32;
var theDate:date.daterec
);
@nodisplay;
@noframe;
begin addMonths;
push( ebp);
mov( esp, ebp );
push( eax );
push( ebx );
push( edx );
mov( theDate, ebx );
// Begin by ensuring that the date is valid.
pushd( [ebx] );
call date._validate;
// Get the months component and add the
// months parameter to it.
movzx( (type date.daterec [ebx]).month, eax );
add( months, eax );
// Change the current month so it is in
// the range 0..11 rather than 1..12.
dec( eax );
// Compute the number of years and months
// by dividing the result by 12. The
// number of years will appear in EAX, the
// number of months will appear in EDX.
xor( edx, edx );
div( 12, edx:eax );
add( ax, (type date.daterec [ebx]).year );
inc( dl ); // Adjust back to 1..12
mov( dl, (type date.daterec [ebx]).month );
// At this point, it's quite possible that the day
// is invalid. For example, 1/31/xxxx plus one month
// would produce 2/31/xxxx (which is invalid). If
// the current date is beyond the end of the newly
// computed month, then truncate the date back to
// the end of the month.
mov( date.DaysInMonth[edx*4], eax );
mov( (type date.daterec [ebx]).day, dh );
cmp( al, dh );
jae noTruncateDay;
mov( al, (type date.daterec [ebx]).day );
// Special case for February in leap years:
cmp( dl, 2 );
jne noTruncateDay;
test( %11, (type date.daterec [ebx]).year );
jnz noTruncateDay;
date.isLeapYear( (type date.daterec [ebx]).year );
test( al, al );
jz noTruncateDay;
// Okay, it's February in a leap year.
// Truncate the day to 29 rather than 28:
mov( 29, (type date.daterec [ebx]).day );
noTruncateDay:
// Make sure the result is valid:
pushd( [ebx] );
call date._validate;
pop( edx );
pop( ebx );
pop( eax );
pop( ebp );
ret( _parms_ );
end addMonths;
hLater,
Randy Hyde
.
- Follow-Ups:
- Re: Adding Months to a date in assembly language
- From: cr88192
- Re: Adding Months to a date in assembly language
- From: Betov
- Re: Adding Months to a date in assembly language
- Prev by Date: Re: Beginners and All That Are New To Assembly Language - Read This
- Next by Date: Re: Betov, Randy, Hutcho ... enough is enough yeah?
- Previous by thread: Betov, Randy, Hutcho ... enough is enough yeah?
- Next by thread: Re: Adding Months to a date in assembly language
- Index(es):
Relevant Pages
|