BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
art297
Opal | Level 21

The part of the log you posted wasn't the same as I had posted. One of the errors was for $date10. which wasn't used in the code I had suggested.  Please run the full code I sent and post your entire log. The $pdate informat should have been created with the code I sent.

zana
Calcite | Level 5

Dear Art, yes that's right. When i found an error, i tried to change that. However, you can see the output of your suggested code:

227  data want;

228    informat persian_date1 persian_date2 $10.;

229    input persian_date1 persian_date2;

230    format sd1 sd2 date9.;

231    sd1=input(persian_date1,$pdate10.);

                                                    ---------

                                                       48

232    sd2=input(persian_date2,$pdate10.);

                                                     ---------

                                                       48

ERROR 48-59: The informat $PDATE was not found or could not be loaded.

233    days_between=sd2-sd1;

234    cards;

Hardly thanks for all your post.

Zana

art297
Opal | Level 21

The part of the log I'd like to see is the part showing the results of running the code that creates the informat.

By the way, somehow I think you've meant to say "Hardy thanks" rather than "Hardly thanks". The meanings are quite different Smiley Happy

jakarman
Barite | Level 11

Zana, I included a SAS source (zipped) in my post. Arthur is following the same high level idea.
I think we need to do some explanation of that.

1/ Dates are integer numbers in sas with the 0-reference point date=0 1jan1960 for the Gregorian calendar.

    When we can transform any calendar notation to that number vice/versa we have a standard date value in SAS (no calendar just a integer day number count).

2/ SAS is supporting those conversions from internal representation to something readable by using formats (from SAS to human readable) and informats (human readable to internal).

    This concept is very unusual to programming languages (SAS is coming from 1970's) . The idea is coming back with css and html giving an other layout when the css changes. (no not the text). 

    Do you have this concept understood?  (then proceed).

3/ The question can be translated to define own made formats/informats for Persian dates. These are some kind of functions/routines that can be stored somewhere.

    The fmtsearch system options is influencing that one. Did someone have crashed that one than the internal defined will work but not the one ones.

    By default the saswork can be used for temporary formats. We did assume this is working at your installation.

4/ Defining a format could benefit from the fcmp (defining an own function) but as you are on 9.1.3 we needed to go back to those as limitation.

    Having an official license there should be nor reason to not upgrading as newer version are included in your contract, at least this is common approach here.

    The format can be defined by building up all unique values in a table (start-end) an the label.

    In my program you find an proc format that just is defining several dates (informat/format) manually. Then by using the output-cntrl dataset as template adding all calculated values (every date 66 year form the list)

  

   Having the formats stored in a catalo (work.formats) and that formats being active than the last step is using those formats.

   That is the last test program. If all previous has been ok, than there must be something in the way formats are getting activated at your installation.

---->-- ja karman --<-----
art297
Opal | Level 21

: The one problem I noticed in your results was with respect to the following line in the log:

hiridat=19854 dategrg=19864 _ERROR_=0 _N_=3

1393-02-21    11MAY2014 ---  21MAY2014 1393-02-31

jakarman
Barite | Level 11

Excellent Arthur. I made a typo in the test-data copied of the 5 cases. The hiri/Persian date should be 1393-02-31 and not the 21 of the 21 may after conversion.

Reading that faulty copies Persian date and displaying at gregorious one is 10 day-s off.  reading the gregorious data 21 mays it show that the 31 correct.

A typo in the test-data always nice to alert on what is happening. 

There are 2 formats created so it is in both directions.

With some more work I could add the letters for each month in Persian using latin1 chars or using the Utf-8 the Arabic chars.

---->-- ja karman --<-----
FriedEgg
SAS Employee

Hijri, Islamic lunar calendar and the Jalali/Persian/Iranian calendar are very different.  Below is a PROC FCMP algorithmic conversion tool between SAS dates and the Jalali calendar.

proc fcmp;

    function to_jalali(sasdate) $ 200;

        y=year(sasdate)-1600;

        m=month(sasdate)-1;

        d=day(sasdate)-1;

        gd=365*y+floor((y+3)/4)-floor((y+99)/100)+floor((y+399)/400);

        array gmond[12] _temporary_ (31, 28, 31, 30, 31, 30, 31, 31,30, 31, 30, 31);

  

        do i=1 to m;

            gd+gmond;

        end;

        if m>1 and ((mod(y,4)=0 and mod(y,100)^=0) or (mod(y,400)=0)) then gd+1;

        gd+d;

        jd=gd-79;

        jnp=floor(jd/12053);

        jd=mod(jd,12053);

        jy=979+33*jnp+4*floor(jd/1461);

        jd=mod(jd,1461);

        if jd>=366 then do;

            jy+floor((jd-1)/365);

            jd=mod(jd-1,365);

        end;

        array jmond[12] _temporary_ (31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29);

        do i=1 by 1 while(i<12 and jd>=jmond);

            jd+-jmond;

        end;

        jm=i;

        jd=jd+1;

        return (catx('-',jy,jm,jd));

    endsub;

    function from_jalali(_jy, _jm, _jd);

        jy = _jy-979;

        jm = _jm-1;

        jd = _jd-1;

        jdn=365*jy+floor(jy/33)*8+floor((mod(jy,33)+3)/4);

        array jmond[12] _temporary_ (31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29);

        do i=1 to jm;

            jdn+jmond;

        end;

        jdn+jd;

        gd=jdn+79;

        gy=1600+400*floor(gd/146097);

        gd=mod(gd,146097);

        leap=1;

        if gd>=36525 then do;

            gd+-1;

            gy+100*floor(gd/36524);

            gd=mod(gd,36524);

            if gd>=365 then gd+1;

            else leap=0;

        end;

       

        gy+4*floor(gd/1461);

        gd = mod(gd, 1461);

        if gd>=366 then do;

            leap=0;

            gd+-1;

            gy+floor(gd/365);

            gd=mod(gd,365);

        end;

        array gmond[12] _temporary_ (31, 28, 31, 30, 31, 30, 31, 31,30, 31, 30, 31);

        do i=1 by 1 while(gd>=gmond+(i=2 and leap));

            gd+-gmond+(i=2 and leap);

        end;

        gm=i;

        gd=gd+1;

        return(mdy(gm,gd,gy));

    endsub;

length x $200;

x=to_jalali('27OCT2014'd);

put x=;

y=from_jalali(1393,8,5);

put y= date9.;

quit;

x=1393-8-5

y=27OCT2014

FriedEgg
SAS Employee

Since the OP is using 9.1.3 and I believe the request here is the Jalali/Gregorian converter as a macro:

%macro to_jalali(in, out);

    %local lidx y m d gd gmond i jd jdn jy jm jmond;

    %let lidx=&sysindex.;

    %let y=__y&lidx.;

    %let m=__m&lidx.;

    %let d=__d&lidx.;

    %let gd=__gd&lidx.;

    %let gmond=__gmond&lidx.;

    %let i=__i&lidx.;

    %let jd=__jd&lidx.;

    %let jdn=__jdn&lidx.;

    %let jy=__jy&lidx.;

    %let jm=__jm&lidx.;

    %let jmond=__jmond&lidx.;

    &y.=year(&in.)-1600;

    &m.=month(&in.)-1;

    &d.=day(&in.)-1;

    &gd.=365*&y.+floor((&y.+3)/4)-floor((&y.+99)/100)+floor((&y.+399)/400);

    array &gmond.[12] _temporary_ (31, 28, 31, 30, 31, 30, 31, 31,30, 31, 30, 31);

    do &i.=1 to &m.;

        &gd.+&gmond.[&i.];

    end;

    if &m.>1 and ((mod(&y.,4)=0 and mod(&y.,100)^=0) or (mod(&y.,400)=0)) then &gd.+1;

    &gd.+&d.;

    &jd.=&gd.-79;

    &jdn.=floor(&jd./12053);

    &jd.=mod(&jd.,12053);

    &jy.=979+33*&jdn.+4*floor(&jd./1461);

    &jd.=mod(&jd.,1461);

    if &jd.>=366 then do;

        &jy.+floor((&jd.-1)/365);

        &jd.=mod(&jd.-1,365);

    end;

    array &jmond.[12] _temporary_ (31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29);

    do &i.=1 by 1 while(&i.<12 and &jd.>=&jmond.[&i.]);

        &jd.+-&jmond.[&i.];

    end;

    &jm.=&i.;

    &jd.=&jd.+1;

    &out.=catx('-',&jy.,&jm.,&jd.);

    drop &y. &m. &d. &gd. &i. &jd. &jdn. &jy. &jm.;

%mend;

%macro from_jalali(year, month, day, out);

    %local lidx jy jm jd jdn jmond i gd gy gmond leap;

    %let lidx=&sysindex.;

    %let jy=__jy&lidx.;

    %let jm=__jm&lidx.;

    %let jd=__jd&lidx.;

    %let jdn=__jdn&lidx.;

    %let jmond=__jmond&lidx.;

    %let i=__i&lidx.;

    %let gd=__gd&lidx.;

    %let gy=__gy&lidx.;

    %let gm=__gm&lidx.;

    %let leap=__leap&lidx.;

    %let gmond=__gmond&lidx.;

    &jy.=&year.-979;

    &jm.=&month.-1;

    &jd.=&day.-1;

    &jdn.=365*&jy.+floor(&jy./33)*8+floor((mod(&jy.,33)+3)/4);

    array &jmond.[12] _temporary_ (31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29);

    do &i.=1 to &jm.;

        &jdn.+&jmond.[&i.];

    end;

    &jdn.+&jd.;

    &gd.=&jdn.+79;

    &gy.=1600+400*floor(&gd./146097);

    &gd.=mod(&gd.,146097);

    &leap.=1;

    if &gd.>=36525 then do;

        &gd.+-1;

        &gy.+100*floor(&gd/36524);

        &gd.=mod(&gd.,36524);

        if &gd.>=365 then &gd.+1;

        else &leap.=0;

    end;

  

    &gy.+4*floor(&gd./1461);

    &gd. = mod(&gd., 1461);

    if &gd.>=366 then do;

        &leap.=0;

        &gd.+-1;

        &gy.+floor(&gd./365);

        &gd.=mod(&gd.,365);

    end;

    array &gmond.[0:11] _temporary_ (31, 28, 31, 30, 31, 30, 31, 31,30, 31, 30, 31);

    do &i.=0 to 11 by 1 while(&gd.>=&gmond.[&i.]+(&i.=2 and &leap.));

        &gd.+-&gmond.[&i.]+(&i.=2 and &leap.);

    end;

    &gm.=&i.+1;

    &gd.=&gd.+1;

    &out.=mdy(&gm.,&gd.,&gy.);

    drop &jy. &jm. &jd. &jdn. &i. &gd. &gm. &gy. &leap.;

%mend;

data foo;

    format from_jalali date9.;

    array jmond[12] _temporary_ (31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29);

    do year=1393 to 1393;

    do month=1 to 12;

    do day=1 to jmond[month];

    if year=1393 and month=8 and day>5 then stop;

    %from_jalali(year, month, day, from_jalali)

    output;

    end;

    end;

    end;

run;

Message was edited by: FriedEgg This code contains typos and is not accurate.  Please see other posts in this thread.

zana
Calcite | Level 5

Thank you so much. But i want convert form 1365 to 1393, when i change 'day year= 1365 to 1393' your program had more error. For example;

1391-10-09 = 31DEC2012  (it is wrong it is equal 29DEC2012)

1391-10-10= .

1391-10-11= .

1391-10-12= 01JAN2013

Did you have any idea for me?

Best reagrds

art297
Opal | Level 21

: I just realized that one reason you may not have been able to run the code I originally proposed is that the tab characters weren't copied correctly. Try the following version that uses a comma delimited file:

data ctrl (keep=fmtname type start label);

  informat gdates $31.;

  infile cards dlm=',';

  input _pyear $ gdates &;

  retain fmtname 'pdate' type 'j';

  leap=index(_pyear,'*');

  pyear=input(compress(_pyear,'*'),12.);

  bday=input(scan(gdates,1,'.'),12.);

  byear=input(scan(gdates,3),12.);

  bmonth=3;

  if _n_ eq 1 then label=mdy(bmonth,bday,byear)-1;

  do pmonth=1 to 12;

    if pmonth le 6 then eday=31;

    else if pmonth le 11 then eday=30;

    else if leap then eday=30;

    else eday=29;

    do pday=1 to eday;

      label+1;

      start=catx('-',pyear,put(pmonth,z2.),put(pday,z2.));

      output;

    end;

  end;

  cards;

1354*,21. March 1975 – 20. March 1976

1355,21. March 1976 – 20. March 1977

1356,21. March 1977 – 20. March 1978

1357,21. March 1978 – 20. March 1979

1358*,21. March 1979 – 20. March 1980

1359,21. March 1980 – 20. March 1981

1360,21. March 1981 – 20. March 1982

1361,21. March 1982 – 20. March 1983

1362*,21. March 1983 – 20. March 1984

1363,21. March 1984 – 20. March 1985

1364,21. March 1985 – 20. March 1986

1365,21. March 1986 – 20. March 1987

1366*,21. March 1987 – 20. March 1988

1367,21. March 1988 – 20. March 1989

1368,21. March 1989 – 20. March 1990

1369,21. March 1990 – 20. March 1991

1370*,21. March 1991 – 20. March 1992

1371,21. March 1992 – 20. March 1993

1372,21. March 1993 – 20. March 1994

1373,21. March 1994 – 20. March 1995

1374,21. March 1995 – 19. March 1996

1375*,20. March 1996 – 20. March 1997

1376,21. March 1997 – 20. March 1998

1377,21. March 1998 – 20. March 1999

1378,21. March 1999 – 19. March 2000

1379*,20. March 2000 – 20. March 2001

1380,21. March 2001 – 20. March 2002

1381,21. March 2002 – 20. March 2003

1382,21. March 2003 – 19. March 2004

1383*,20. March 2004 – 20. March 2005

1384,21. March 2005 – 20. March 2006

1385,21. March 2006 – 20. March 2007

1386,21. March 2007 – 19. March 2008

1387*,20. March 2008 – 20. March 2009

1388,21. March 2009 – 20. March 2010

1389,21. March 2010 – 20. March 2011

1390,21. March 2011 – 19. March 2012

1391*,20. March 2012 – 20. March 2013

1392,21. March 2013 – 20. March 2014

1393,21. March 2014 – 20. March 2015

1394,21. March 2015 – 19. March 2016

1395*,20. March 2016 – 20. March 2017

1396,21. March 2017 – 20. March 2018

1397,21. March 2018 – 20. March 2019

1398,21. March 2019 – 19. March 2020

1399*,20. March 2020 – 20. March 2021

1400,21. March 2021 – 20. March 2022

1401,21. March 2022 – 20. March 2023

1402,21. March 2023 – 19. March 2024

1403*,20. March 2024 – 20. March 2025

1404,21. March 2025 – 20. March 2026

1405,21. March 2026 – 20. March 2027

1406,21. March 2027 – 19. March 2028

1407,20. March 2028 – 19. March 2029

1408*,20. March 2029 – 20. March 2030

1409,21. March 2030 – 20. March 2031

1410,21. March 2031 – 19. March 2032

1411,20. March 2032 – 19. March 2033

1412*,20. March 2033 – 20. March 2034

1413,21. March 2034 – 20. March 2035

1414,21. March 2035 – 19. March 2036

1415,20. March 2036 – 19. March 2037

1416*,20. March 2037 – 20. March 2038

1417,21. March 2038 – 20. March 2039

1418,21. March 2039 – 19. March 2040

1419,20. March 2040 – 19. March 2041

;

proc format library=work cntlin=ctrl;

run;

data want;

  informat persian_date1 persian_date2 $10.;

  input persian_date1 persian_date2;

  format sd1 sd2 date9.;

  sd1=input(persian_date1,$pdate10.);

  sd2=input(persian_date2,$pdate10.);

  days_between=sd2-sd1;

  cards;

1392-10-11 1393-01-01

1393-02-31 1393-06-30

1393-08-02 1395-03-02

1391-10-09 1391-10-12

;

zana
Calcite | Level 5

Thank you Arthur, this works!

You are fabulous Smiley Happy

Shams16
Calcite | Level 5

Thank you Art297 for this program. I was having the same problem and I tried using your program. I had following errors and in addition, the program returns incorrect dates, for example,  For example for today's date in SH calendar : 1396/05/03  it should return 2017/07/25 but instead it returns 07MAY2002

 
See log attached for errors
See output attached for incorrect results

See SAS codes downloaded from forum.

 

Thank yo very much for your help and advise in this regard.

 

Shams

FriedEgg
SAS Employee

There were some typos in my previous posting of the Jalali/Solar Hijri/Persian/Kuwaiti calendar algorithm.  I believe the below is now accurate.

%macro from_jalali(year, month, day, out);

array m2d[2,0:11] _temporary_ (

    /*jalali*/      31 31 31 31 31 31 30 30 30 30 30 29

    /*gregorian*/   31 28 31 30 31 30 31 31 30 31 30 31

);

j_y=&year.;

j_m=&month.;

j_d=&day.;

jy=j_y-979;

jm=j_m-1;

jd=j_d-1;

j_day_no = 365*jy + floor(jy/33)*8 + floor((mod(jy, 33)+3)/4);

do i=0 to jm-1;

    j_day_no + m2d[1,i];

end;

j_day_no + jd;

g_day_no = j_day_no + 79;

gy = 1600 + 400*floor(g_day_no/146097);

g_day_no = mod(g_day_no, 146097);

leap = 1;

if g_day_no >= 36525 then do;

    g_day_no+-1;

    gy + 100*floor(g_day_no/36524);

    g_day_no = mod(g_day_no, 36524);

    if g_day_no >= 365 then g_day_no+1;

    else leap = 0;

end;

gy+4*floor(g_day_no/1461);

g_day_no = mod(g_day_no, 1461);

if g_day_no >= 366 then do;

    leap = 0;

    g_day_no+-1;

    gy+floor(g_day_no/365);

    g_day_no=mod(g_day_no, 365);

end;

do i=0 by 1 while (g_day_no >= m2d[2,i]+(i=1 and leap));

    g_day_no+-(m2d[2,i]+(i=1 and leap));

end;

gm = i+1;

gd = g_day_no+1;

&out.=mdy(gm,gd,gy);

%mend;

data _null_;

%from_jalali(1391,10,9,foo)

put foo date9.; /*29DEC2012*/

run;

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 42 replies
  • 5125 views
  • 6 likes
  • 9 in conversation