View Raw SPL
/*****************************************************************************
*                                                                            *
*   JULYMD.SPL   Copyright (C) 1998-2024 DSP Development Corporation         *
*                               All Rights Reserved                          *
*                                                                            *
*   Author:      Randy Race                                                  *
*                                                                            *
*   Synopsis:    Converts a series to Julian dates                           *
*                                                                            *
*   Revisions:    9 Jul 1998  RRR  Creation                                  *
*                17 Mar 2005  RRR  julvec iyyy assignment fix                *
*                14 Nov 2024  RRR  option for separate Y, M, D input         *
*                                                                            *
*****************************************************************************/


#define IGREG (15+31*(10+12*1582))
#define JGREG 2299161


#if @HELP_JULYMD

    JULYMD

    Purpose: Converts a series of yymmdd values to Julian dates

    Syntax:  Julymd(y, m, d)

              y - A scalar or series, the year values.

              m - A scalar or series, the month values.

              d - A scalar or series, the day values.


    Alternate Syntax:  Julymd(dtser, format)

              dtser  - a series containing date/time values

              format - an optional integer, the value format.

                        0:yymmdd (default)
                        1:yyyymmdd

    Returns: A series or array of Julian integers

    Example:
             W1: {1998, 1999, 2000}
             W2: {1, 1, 1}
             W3: {10, 11, 15}
             W4: julymd(w1, w2, w3)

             W1 contains the year values.

             W2 contains the month values.

             W3 contains the day values.

             W4 contains the date series:

                1-10-1998
                1-11-1999
                1-15-2000

    Example:
             W1: julymd(1998..2000, 1, {10, 11, 15})

             Same as above except the year, month and day values are entered
             directly as series.

             W1 contains the date series:

                1-10-1998
                1-11-1999
                1-15-2000

    Example:
             W1: julymd(1998..2000, 1, {10, 11, 45})

             Same as above except the the last day value of 45 causes the
             resulting month to rollover.

             W1 contains the date series:

                1-10-1998
                1-11-1999
                2-14-2000

    Example:
             W1: {980101, 980102, 980103, 980112}
             W2: julymd(w1);
             W3: xy(w2, {1, 2, 3, 4})

             W2 contains the series {2450815, 2450816, 2450817, 2450826}
             Each value is the Julian representation of the original
             date/time values of W1. In this case, W1 is specified in
             composite YYMMDD format.

    Example:
             W1: {19980101, 19980102, 19980103, 19980112}
             W2: julymd(w1, 1);
             W3: xy(w2, {1, 2, 3, 4})

             Same as the first example, but the date/time format is
             composite YYYYMMDD.

    Example:
             W1: julymd(1998, 1, {1..3, 12})
             W2: xy(w1, {1, 2, 3, 4})

             Same as above except the year, month and day inputs are
             entered as separate values.

    Remarks:
             JULYMD creates a date series by combining individual year,
             month and day values or by extracting year, month and day
             values from composite YYYYMMDD integers.

             JULYMD attempts to recognizes the YYYYMMDD and YYMMDD
             date/time formats for composite inputs.

             For separate Y, M, D inputs, out of range month and day values
             are rolled over.
             
             For composite YYYYMMDD inputs, 0 is returned for date/time values
             that cannot be converted.

    See Also:
             Julstr

#endif


/* convert year, month, day to Julian */
julymd(y, m, d)
{
        local val, str, format, len;

        if (not(argc == 3))
        {
                if (isstring(y))
                {
                        return(julstr(y));
                }
                else if (not(isarray(y)))
                {
                        error(sprintf("%s - series required", __FUNC__));
                }

                /* figure out format */
                if (argc < 2)
                {
                        val = castint(y[1]);
                        str = sprintf("%ld", castint(y[1]));
                
                        if (strlen(str) == 8)
                        {
                                format = 1;
                        }
                        else
                        {
                                format = 0;
                        }
                }
                else
                {
                        format = m;
                }

                /* get individual yy mm dd series */
                if (format == 0)
                {
                        /* yymmdd */
                        m = int(y / 100) % 100;
                        d = int(y % 100);

                        /* convert 2 digit year to 4 digit */
                        y = int(y / 10000);
                        y = yytoyyyy(y);

                }
                else if (format == 1)
                {
                        /* yyyymmdd */
                        m = int(y / 100) % 100;
                        d = int(y % 100);
                        y = int(y / 10000);

                }
                else
                {
                        error(sprintf("julymd - invalid format %d", format));
                }
        }
        else
        {
                if (isarray(y) || isarray(m) || isarray(d))
                {
                        len = vmax(length(y), length(m), length(d));

                        if (isscalar(y))
                        {
                                y = rep({y}, len);
                        }

                        if (isscalar(m))
                        {
                                m = rep({m}, len);
                        }

                        if (isscalar(d))
                        {
                                d = rep({d}, len);
                        }
                }
        }

        /* internal conversion */        
        jmd = julfromymd(y, m, d);
        
        return(jmd);
}


/* vectorized Julian conversion */
julvec(mm, id, iyyy)
{
        local jul;
        local ja, jy, jm;
        local t;

        if (not(all(mm) && all(id) && all(iyyy)))
        {
                return(zeros(length(mm), 1));
        }

        t = iyyy == 0;
        
        // no Julian year 0
        iyyy = iyyy * not(t) + t;

        t    = iyyy < 0;
        iyyy = iyyy * not(t) + (iyyy + 1) * t;

        t = mm > 2;

        jy = t * iyyy + (iyyy - 1) * not(t);
        jm = t * (mm + 1) + not(t) * (mm + 13);

        jul = int((floor(365.25 * jy) + floor(30.6001 * jm) + id + 1720995));

        t = (id + 31 * (mm + 12 * iyyy)) >= IGREG;

        ja  = int(0.01 * jy);
        inc = 2 - ja + int(0.25 * ja);

        jul = int(t * (jul + inc) + not(t) * jul);

        if (isarray(jul))
        {
                setvunits(jul, "Daily", -1);
        }

        return(jul);
}


/* convert two digit year to four digit year */
yytoyyyy(yy)
{
        local y2kmode, locale, y2kmode, dt_y2k_offset;
        local current_century, base_century, year;

        y2kmode = getconf("dt_y2k_format") == "1";
        dt_y2k_offset = numstr(getconf("dt_y2k_offset"));
        locale = getconf("locale_idate");

        /* use yyyy-mm-dd to get current year */
        setconf("locale_idate", "2");
        year = numstr(getdate());
        setconf("locale_idate", locale);

        if (y2kmode)
        {
                /* use 50 year window */
                locale = getconf("locale_idate");

                base_century = int(100 * int((year + dt_y2k_offset) / 100));
                
                if (yy <= dt_y2k_offset)
                {
                        yy += base_century;
                }
                else
                {
                        yy += base_century - 100;
                }
        }
        else
        {
                /* just use current century */
                current_century = int(100 * int(year / 100));
                yy += current_century;
        }
        
        return(yy);
}