CALLNAT Parameter addressing

Greetings again, my friends.

In this Date subroutine I am working on (EOM, prior EOM, etc), DATE-IN and DATE-OUT and individual parameters for maximum flexibility.

I found that, since Natural checks for same length and format by default, I had to use ‘BY VALUE’ for the DATE-IN parm and ‘BY VALUE RESULT’ for the DATE-OUT parm in said Date subroutine.

I am permitting the user to code the same field/area for DATE-IN and DATE-OUT, but only if the FORMAT-OUT = FORMAT-IN (which is the default) since otherwise it gets complicated.

My problem is that to enforce this requirement I need to know whether the address for DATE-IN is the same as the address for DATE-OUT. Is there a way I can do so under the parameter circumstance described ?

Thanks again for your wonderful help.

EDIT: Unexpectedly, when I tested I found that if I don’t change DATE-OUT in the subprogram (say, format error), there is no result, no spaces even, moved to the Date out address in the calling program. This is good! This means I don’t have to enforce the edit described above. But I would still like to know if I can determine the Parameter address.

Adam :slight_smile: ,

first: addresses can be evaluated by the POS function (not documented for this purpose :!: )

second: if you use the POS function to evaluate the addresses of the parameter, you will get the addresses of the copies of the parameter as they are passed by value.

What I do not understand: why this will get complicated? For your program you always work on different variables. :?

Thanks, Wilfried, for your reply.

For ‘different variables’ in the calling program, there is no problem. But I had envisioned a problem if the calling program used the SAME VARIABLE/AREA for DATE-IN and DATE-OUT. But it seems this is not a problem either (please see the edit I made to my post above). It appears that Natural creates a result variable equal to the length of the value you move to the result parameter; no value moved, length=0.

I appreciate your help.

Adam,

here is an example to easily calculate the EOM:
Edit: Example moved to Open Source Forum
You may adapt it for your needs.

Thanks, Wilfried. Is this a test? That example fails if the input date is Jan 30th or March 31st for example …

No, not a test but a program created in a hurry. But, Adam, the community has given the correct and elegant solution.

[quote="Wilfried B

Please let us know your results. Could be interesting …

Well, I completed performance tests comparing two mini date routines.

  1. Mini Date Routine 1: DATE-IN format is tested using MASK and converted to Natural’s native date format then to intermediate date format YYYYMMDD on which all conversion requests (EOM etc) are effected except CHANGE-BY-DAYS which is always last (documented). Then intermediate YYYY is tested for range, YYYYMMDD is converted to D format, and CHANGE-BY-DAYS applied. D format is tested for valid range and converted to output format.

  2. Mini Date Routine 2: DATE-IN format, which is redefined in all allowed input formats, is moved BY NAME (e.g YYYY, MM, DD - except input format D which is tested for validity first ) to intermediate date format YYYYMMDD. Intermediate date is tested using MASK (‘valid’ separator concerns for date formats like mm/dd/yyyy are ignored). All conversion requests (EOM etc) are effected except CHANGE-BY-DAYS. If CHANGE-BY-DAYS is not zero, intermediate date is converted to D format, DAYS applied, and back to intermediate. YYYY of intermediate date is tested for range and then output date is created by move BY POSITION (including separators - and except D format out).

The test program had a subroutine (performed 50,000 times) with two calls:

  1. first call requested EOM for YYYYMMDD input and MMDDYYYY output. Input date was 20000101.
  2. second call request ‘next EOM’ for YYYYMMDD input and MMDDYYYY output. It also include a CHANGE-BY-DAYS request of +1. Input date was 20031201.

The results:
Mini Date Routine 1: 13.04 CPU secs
Mini Date Routine 2: 11.76 CPU secs

Natural’s conversion routine (to D and back) is very efficient so I am sticking with it for ‘elegance’ and for consistent conversion of YY to YYYY.

For EOM I use (#DATE-YYYYMMDD is intermediate date):
DECIDE ON FIRST VALUE OF #DATE-MM
VALUE 01, 03, 05, 07, 08, 10, 12
#DATE-DD := 31
VALUE 04, 06, 09, 11
#DATE-DD := 30
VALUE 02
#DATE-DD := 29
IF #DATE-YYYYMMDD NE MASK(YYYYMMDD) #DATE-DD := 28 END-IF
NONE
IGNORE (date already verified)
END-DECIDE

Matthias’ solution should be just as efficient.

If you want to get even faster, think about implementing an own leap-year-logic instead of using MASK. I’m not sure, but maybe you’ll get better results…

Why not implement this algorithm in assembler instead of Natural. It’s much faster. :?

[quote="Wilfried B

I don’t think that my own leap year logic will beat Natural’s MASK because to be so efficient Natural must have an indexed table behind the scenes with all the YYYY (and D days) from 1582 thru 2699. I would assume that LEAP years are already tagged in this table.

(There must also be an indexed table [maybe 2, one for leap yrs] of DDD by MM)

My routine would have to divide by 4 and test the remainder (and if the year ended in 00, divide by 400 and test the remainder) to determine leap year. Natural’s indexed table lookup may be faster. It’s definitely fast enough.