View Raw SPL
/****************************************************************************
*                                                                           *
*   DTOFFSET.SPL  Copyright 2024 (C) DSP Development Corporation            *
*                                                                           *
*   Author:       Randy Race                                                *
*                                                                           *
*   Synopsis:    Add or subtract years, months, days, hours, seconds        *
*                                                                           *
*   Revisions:    10 Nov 2024     RRR     Creation                          *
*                                                                           *
****************************************************************************/


#if @HELP_DTOFFSET

    DTOFFSET

    Purpose: Adds or subtracts years, months, days, hours, minutes, seconds
             to date time values.

    Syntax:  DTOFFSET(datetime, years, months, days, hours, minutes, seconds)

             datetime - A date time series.

                years - A scalar or series, the year values. Defaults to 0.

               months - A scalar or series, the month values. Defaults to 0.

                 days - A scalar or series, the day values. Defaults to 0.

                hours - A scalar or series, the hours values. Defaults to 0.

              minutes - A scalar or series, the minutes values. Defaults to 0.
 
              seconds - A scalar or series, the seconds values. Defaults to 0.


    Alternate Syntax:  DTOFFSET(date, time, years, months, days, hours,
                                minutes, seconds)

                 date - A scalar or series of integer Julian date values.
      
                 time - A scalar or array of time of day values.

                years - A scalar or series, the year values. Defaults to 0.

               months - A scalar or series, the month values. Defaults to 0.

                 days - A scalar or series, the day values. Defaults to 0.

                hours - A scalar or series, the hours values. Defaults to 0.

              minutes - A scalar or series, the minutes values. Defaults to 0.
 
              seconds - A scalar or series, the seconds values. Defaults to 0.


    Returns: A date time series.

    Example:
             W1: ymdhms2dt(2030, 1, 1, 12, 0, 0)
             W2: dtoffset(w1, 1, 1, 1, 1, 1, 1)

             W1 contains the date time series:

                1-1-2030  12:00:00

             W2 adds 1 year, 1 month, 1 day, 1 hour, 1 minute and 1 second
             to the date time in W1. The resulting date time is:

                2-2-2031 13:01:01
    Example:
             W1: ymdhms2dt(2030, 1, 1, 12, 0, 0)
             W2: dtoffset(w1, 1, 1, 1, 1, 1, 1)
             W3: dtoffset(w2, -1, -1, -1, -1, -1, -1)

             Same as above except W3 removes the offset in W2 to return
             the same date time as in W1:

                1-1-2030 12:00:00

    Example:
             W1: grand(3, 1);setdate(w0, "1-1-2030");settime(w0, "12:00:00")
             W2: getdt(w1)
             W3: dtoffset(w2, days:-3, hours:1..3)

             W1 generates a 3 point random series sampled at 1 sample per
             second. The start date is set to 1-1-2030 and the start
             time is 12:00:00.

             W2 obtains the date and time values for each sample of W1.
             W2 contains the date time values:

               1-01-2030 12:00:01
               1-01-2030 12:00:02
               1-01-2030 12:00:03

             W3 subtracts 3 days from each date time value and adds 1 hour
             to the first value, 2 hours to the second value and 3 hours
             to the last date time value. The offset values are specified
             using the PARAMETER:VALUE syntax. The resulting date time
             values are:

               12-29-2029 13:00:00
               12-29-2029 14:00:01
               12-29-1029 15:00:02

    Example:
             W1: grand(3, 1);setdate(w0, "1-1-2030");settime(w0, "12:00:00")
             W2: getdt(w1)
             W3: dtoffset(w2, days:-3, hours:1..3)
             W4: dtperiod(w2, w3)
             W5: dtoffset(w3, w4)
             W6: w2 == w5

             Same as above except W4 computes the period in years, months,
             days, hours, minutes and seconds between the date time values
             in W2 and the date time values in W3. The difference is 2 days
             and 23 hours for the first value, 2 days and 22 hours for the
             second value and 2 days 21 hours for the last value.

             W5 adds the offset values in W4 to the date time values in W3.
             The resulting date time values are the same as the original
             values in W2. In this case, subtracting 3 days and adding 1,
             2 and 3 hours to the date time values in W2 is the same as
             subtracting 2 days and subtracting 23, 22, and 21 hours to the
             date time values in W2,

             W6 compares the date time values of W2 and W5, verifying the
             values are identical.

    Remarks:
             DTOFFSET adds or subtracts years, months, days, hours, minutes
             and seconds from a date time series.

             The offset values can be specified using the PARAMETER:VALUE
             syntax. For example:

               dtoffset(dt, minutes:5, months:3)

             adds 3 months and 5 minutes to the date time series DT.

             The offset values are always added in years, months, days,
             hours, minutes and seconds order.

    See Also:
             DATEOFFSET
             DTDURATION
             DTPERIOD
             TIMEOFFSET
             GETDT
#endif


/* add years, months, days, hours, minutes, seconds offset to dt */
dtoffset(date, time, years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0)
{
        local ts, dateonly, timeonly;

        /* parse args */
        (date, time, years, months, days, hours, minutes, seconds, dateonly, timeonly) = 
                dtoffset_parse_args(argc, date, time, years, months, days, hours, minutes, seconds);

        if (dateonly)
        {
                dt = dateoffset(date, years, months, days);
        }
        else if (timeonly)
        {
                dt = timeoffset(time, hours, minutes, seconds);
        }
        else
        {
                /* date time */
                date = dateoffset(date, years, months, days);

                /* total tod */
                time = time + 3600 * hours + 60 * minutes + seconds;

                /* convert to/from timestamp for time rollovers */
                ts = dt2timestamp(date, time);
                dt = timestamp2dt(ts);
        }

        return(dt);
}


/* parse args */
dtoffset_parse_args(nargs, date, time, years, months, days, hours, minutes, seconds)
{
        local y, mo, d, h, mi, s, dateonly, timeonly;

        y  = years;
        mo = months;
        d  = days;
        h  = hours;
        mi = minutes;
        s  = seconds;

        dateonly = timeonly = FALSE;

        if (nargs < 2)
        {
                error(sprintf("%s - date time and offset values required", __CALLER__));
        }
        else if (nargs == 2)
        {
                if (not(isunspecified(time)))
                {
                        /* parse time input */
                        if (isdatetime(time))
                        {
                                (y, mo, d, h, mi, s) = dt2ymdhms(time);
                        }
                        else if (isdate(time))
                        {
                                (y, mo, d) = date2ymd(time);
                        }
                        else if (istime(time))
                        {
                                (h, mi, s) = time2hms(time);
                        }
                        else
                        {
                                /* DT, y, m, d, ... */
                                if (isarray(time))
                                {
                                        if (numcols(time) > 1 && numcols(time) <= 6)
                                        {
                                                try
                                                {
                                                        y  = col(time, 1);
                                                        mo = col(time, 2);
                                                        d  = col(time, 3);
                                                        h  = col(time, 4);
                                                        mi = col(time, 5);
                                                        s  = col(time, 6);
                                                }
                                                catch
                                                {
                                                }
                                        }
                                        else
                                        {
                                                y = time;
                                        }
                                }
                                else
                                {
                                        y = time;
                                }
                        }
                }

                /* parse date */
                if (isdatetime(date))
                {
                        time = date[.., 2..2..end];
                        date = date[.., 1..2..end];
                }
                else if (isdate(date))
                {
                        if (not(any(h) || any(mi) || any(s)))
                        {
                                dateonly = TRUE;
                        }

                        time = 0;
                }
                else if (istime(date))
                {
                        time = refseries(date);
                        date = -1;

                        if (not(any(y) || any(mo) || any(d)))
                        {
                                timeonly = TRUE;
                        }
                }
        }
        else
        {
                /* nargs > 2 */
                if (isdatetime(date) || isdate(date))
                {
                        if (not(isunspecified(time)))
                        {
                                try
                                {
                                        y  = time;
                                        mo = years;
                                        d  = months;
                                        h  = days;
                                        mi = hours;
                                        s  = minutes;
                                }
                                catch
                                {
                                }
                        }

                        if (isdatetime(date))
                        {
                                time = date[.., 2..2..end];
                                date = date[.., 1..2..end];
                        }
                        else
                        {
                                time = 0;

                                if (not(any(h) || any(mi) || any(s)))
                                {
                                        dateonly = TRUE;
                                }
                        }
                }
                else if (istime(date))
                {
                        if (not(isunspecified(time)))
                        {
                                try
                                {
                                        y  = time;
                                        mo = years;
                                        d  = months;
                                        h  = days;
                                        mi = hours;
                                        s  = minutes;
                                }
                                catch
                                {
                                }
                        }

                        time = refseries(date);
                        date = -1;

                        if (not(any(y) || any(mo) || any(d)))
                        {
                                timeonly = TRUE;
                        }
                }
                else
                {
                        error(sprintf("%s - date time and offset values required", __CALLER__));
                }
        }

        return(date, time, y, mo, d, h, mi, s, dateonly, timeonly);
}