# Inverting date

Hi,

I would like to know if anyone could provide me a code sample to invert European date DD.MM.YYYY to American date YYYY.MM.DD and vice versa. I don’t want to use day, month and year variables. It seems that there is an easy way to solve it using calculus, for example, multiplying the values. maybe one operation would be enough to invert the date instead of several moves.

If anyone knows how to do it, I’d appreciate receiving the code.

Thanks.

You could just move the first date value with a MOVE EDITED to a date variable (D), then MOVE EDITED to what ever second format was needed.

``````define data
local
01 #date-E (A10) init<'12.25.2007'>
01 #date-i (A10)
01 #date (d)
end-define
move edited #date-e to #date (em=mm.dd.yyyy)
move edited #date (em=yyyy-mm-dd) to #date-i
display #date-e #date-i #date (em=mm/dd/yyyy)
end``````

Hi Douglas,

Thank you for the code.

Even though this code is very simply, the code (I’m looking for) used some calculus (math) to get the date (inverted). I have seen this code before, but I cannot remember…

Anyway, if anyone knows how to do it, please let me know.

Thanks.

There is a reason that you should take a longer look at Douglas’s code.

Suppose you have an alpha variable with contents DD.MM.YYYY. Consider what a mess this would be to convert via a numeric algorithm.

First, you would have to “extract” the values for DD, MM, and YYYY form the alpha variable. This would, of course, be complicated by the possibility that dates exist in a form such as 8.3.2007 rather than 08.03.2007.

Even if you can be assured that such variation does not exist, you would still be faced with the prospects of extracting (e.g. via the use of VAL) numeric values from an alpha field.

This requirement is one of the major uses for Date Format (as in Douglas’s code).

steve

Steve, That’s not exactly fair. The above complication would befuddle Douglas’ code as well.

It’s an interesting puzzle to try to do this in one line of code. In order to do this using math, I would have to assume that the numbers are stored consistently in N8 fields with leading zeros on day and month. Then I can take advantage of Natural’s penchant for truncating computations and do:

``````
DEFINE DATA LOCAL
1 #CYMD (N8) INIT <20071215>
1 #DMCY (N8)
END-DEFINE
*
COMPUTE #DMCY =
(#CYMD - (#CYMD / 100 * 100)) * 1000000          /* dd portion
+ (#CYMD - (#CYMD / 10000 * 10000)) / 100 * 10000  /* mm portion
+ (#CYMD / 10000)                                  /* cy portion
*
DISPLAY #CYMD (EM=9999'.'99'.'99)
#DMCY (EM=99'.'99'.'9999)                                    ``````

Which gives:

``````   #CYMD      #DMCY
---------- ----------
2007.12.31 31.12.2007  ``````

Hi Jerome;

Your assumption is not exactly fair either

The original statement of the problem shows what must be alpha fields (e.g. DD.MM.YYYY). Hence, if nothing else, your code, imaginative as it is, would first require something like EXAMINE #CYMD-ALPHA FOR ‘.’ DELETE; followed by a MOVE of sorts to create a numeric field in the N8 format; whoops, still a problem with the possible anomolies like D.M.YYYY

A SEPARATE of #CYMD-ALPHA into #DAY #M #Y WITH DELIMITER ‘.’ would set up the components for a COMPRESS into the desired formats without any MOVE EDITED statements.

Yes, I saw John’s note to avoid month, day and year variables. I simply do not understand why one would write a lengthy arithmetic statement, following extraction of numeric values from an alpha field, rather than a simple SEPARATE-COMPRESS duo.

steve

I didn’t say it was a good solution, just an interesting puzzle. I would use Douglas’ code, with the addition of a mask check before the first move edited to prevent Nat 1143 errors.

``````
move edited #date-e to #date (em=mm.dd.yyyy)
move edited #date (em=yyyy.mm.dd) to #date-i
end-if``````

You can accomplish the inversion with one operation by using the COMPRESS statement and by redefining the date variable according to the american date YYYY.MM.DD and the european date DD.MM.YYYY.

``````DEFINE DATA LOCAL
1 #DATE                (A10) INIT <'2007.12.25'>
1 REDEFINE #DATE             /* redefinition for YYYY.MM.DD
2 #YYYY-A            (A4)
2 #MM-A              (A4)
2 #DD-A              (A2)
1 REDEFINE #DATE             /* redefinition for DD.MM.YYYY
2 #DD-E              (A2)
2 #MM-E              (A4)
2 #YYYY-E            (A4)
END-DEFINE
WRITE #DATE '<-- initial YYYY.MM.DD format'
*
COMPRESS  #DD-A #MM-A #YYYY-A INTO #DATE   LEAVING NO
WRITE  #DATE '<-- DD.MM.YYYY date format'
*
COMPRESS #YYYY-E #MM-E #DD-E INTO #DATE   LEAVING NO
WRITE  #DATE '<-- converted back to YYYY.MM.DD date format'
*
END``````

Your mask check only works, if the compiler option MASKCME is set to ON. If not, the check will not recognize invalid years to prevent NAT1143.
(valid years are in the range of 1582 - 2699).

If the compiler option MASKCME is set to OFF, you should code the mask check:

``````IF #DATE-E EQ MASK(MM.DD.YYYY)

Hi Helmut;

Thought about the single COMPRESS. However, it does not “solve” the problem of dates that do not have leading zeroes (e.g. 3.7.2007, or, what I have also seen a lot of; 10/3/07).

The SEPARATE before the COMPRESS takes care of values that do not “fill” the typical allocated space.

steve

Hi,

Thank for this good discussion. I have to say that everyone is correct in your position. Jerome solved the problem using one line and math, even though we have some limitations. Douglas wrote an excellent code and it is complete.

If anyone have other suggestion how to solve it, please let me know.

Thanks for everyone.

"Douglas wrote an excellent code and it is complete. "

And it is also over ten times as expensive (elapsed and cpu) as it should be. See the following timing comparison.

I used the COMPRESS without the SEPARATE since the computation code also assumes that the data is precisely in YYYYMMDD format without any missing digits.

Please note the COMPRESS is also just one statement; unless you want to count the REDEFINE variables.

DEFINE DATA LOCAL
1 #CYMD (N8) INIT <20071215>
1 REDEFINE #CYMD
2 #Y (A4)
2 #M (A2)
2 #D (A2)
1 #DMCY (N8)
1 #OUT (A8)
1 #LOOP (I4)
1 #CPU-START (I4)
1 #CPU-ELAPSED (I4)
END-DEFINE
*
MOVE CPU-TIME TO #CPU-START
SETA. SETTIME
FOR #LOOP = 1 TO 10000
COMPUTE #DMCY =
(#CYMD - (#CYMD / 100 * 100)) * 1000000 /
dd portion
+ (#CYMD - (#CYMD / 10000 * 10000)) / 100 * 10000 /* mm portion
+ (#CYMD / 10000) /* cy portion
END-FOR
COMPUTE #CPU-ELAPSED = *CPU-TIME - #CPU-START
WRITE 5T ‘compute’ *TIMD (SETA.) #CPU-ELAPSED
*
MOVE *CPU-TIME TO #CPU-START
SETB. SETTIME
FOR #LOOP = 1 TO 10000
COMPRESS #M #D #Y INTO #OUT LEAVING NO SPACE
END-FOR
COMPUTE #CPU-ELAPSED = *CPU-TIME - #CPU-START
WRITE 5T ‘compress’ *TIMD (SETB.) #CPU-ELAPSED
*
MOVE *CPU-TIME TO #CPU-START
SETC. SETTIME
FOR #LOOP = 1 TO 10000
IGNORE
END-FOR
COMPUTE #CPU-ELAPSED = *CPU-TIME - #CPU-START
WRITE 5T ‘FOR loop’ *TIMD (SETC.) #CPU-ELAPSED
END

Page 1 07-12-20 06:54:38

``````compute       12         120
compress        1           8
FOR loop        0           2
``````

steve

Sorry. Mea Culpa. The arithmetic code was Jerome’s, not Doug’s.

Got that backwards.

The COMPRESS also is more efficient than the two MOVE EDITED solution

Page 1 07-12-20 07:08:40

``````move edited        2          18
compress        1           8
FOR loop        0           2
``````

However, the MOVE EDITED is nowhere near as slow as the arithmetic solution

steve

One more note; then I will drop this. The times above were on my PC. Since arithmetic is proportionately faster on the mainframe (compared to complex statements like COMPRESS), I also just ran the code on the mainframe. The times below are for ten times the number of iterations:

First the COMPUTE versus the COMPRESS

compute 5 45
compress 1 15
FOR loop 0 6

Now the COMPUTE versus the MOVE EDITED

move edited 3 33
compress 1 15
FOR loop 0 6

Although the ratios have changed, the COMPRESS is still at least twice as fast as the MOVE EDITED and three times as fast as the COMPUTE.

steve

But, if you are really looking for efficiency, and we can redefine the date fields as needed, MOVE BY NAME is much better. Starting with Steve’s test program, I redefined the dates as follows:

``````1 #CYMD (N8) INIT <20071215>
1 REDEFINE #CYMD
2 #CYMD-A (A8)
1 REDEFINE #CYMD
2 #CYMD-G
3 #Y (A4)
3 #M (A2)
3 #D (A2)
1 #DMCY (N8)
1 REDEFINE #DMCY
2 #DMCY-A (A8)
1 REDEFINE #DMCY
2 #DMCY-G
3 #D (A2)
3 #M (A2)
3 #Y (A4)                ``````

Then added another for loop with MOVE BY NAME #CYMD-G TO #DMCY-G
I had to increase the iterations to 500,000 for our mainframe and got the following results:

``````compute             4          34
compress            3          28
move edited        17         139
move by name        0           4
FOR loop            0           4``````

I can’t explain the relative differences between my numbers and Steve’s. My compute is much closer to compress than his and move edited for me is significantly less efficient. But it is clear on our system that move by name has almost no overhead.

Taking this to its logical conclusion; I decided to test the MOVE BY NAME versus three MOVE’s. The following are PC V6 times

move by name 5 54
three move 5 48
FOR loop 2 19

Later today I will logon to the mainframe and run the code there. also will check the output from the other runs. i too do not understand the differences between Jerome’s times and mine. If nothing else, the CPU-TIMEs should be proportionate, whereas the elapsed times could reflect different background “noise”

steve

while you’re at it - are any of the mainframe times using the Optimizing Compiler (NOC)?

Douglas, that thought just occurred to me. Our site recently installed NOC and that makes the difference between my numbers and Steve’s. Here is a comparison:

``````NOC Disabled:
compute            23         226
compress            8          78
move edited        16         164
move by name        5          55
FOR loop            3          36

NOC Enabled:
compute             3          34
compress            2          28
move edited        14         139
move by name        0           4
FOR loop            0           4  ``````

It is interesting that MOVE EDITED does not gain much from NOC.

Hi Jerome;

well that does certainly clear up one mystery.

On the mainframe my results (500,000 iterations; pc numbers were only 100,000)

move by name 5 47
three move 6 57
FOR loop 4 30

So these two approaches “flip flopped” from the PC. (why?? behind the scenes the MOVE BY NAME has to be doing the same three moves).

The two MOVE EDITEDs to/from date format are basically just computations (no. of years * 365 + extra leap year days - century days + century days/4). I too would have thought they would benefit from NOC.

The point of all this is how much CPU time can be saved for a seemingly insignificant piece of code. Depending on the application, this could be performed millions of times a day. Pretty soon, it all adds up to a significant amount of time.

steve