Adding time to date time in UNIX shell scipting

I needed some help in adding a duration (in seconds) to a start time (in hhmmss format) and a start date (in mmddyy format) in order to get an end date and end time. The concept of a leap year is also to be considered while incrementing the day. The code/ function that I have formed so far is as follows, but there appears to be an error in the function calling. I also wanted to check if this script designed by me is full proof.

MIN_TIME = start_time; 
                        secs=substr(MIN_TIME,5,2);
            mins=substr(MIN_TIME,3,2);
            hrs=substr(MIN_TIME,1,2);
            durn=p;
            d=substr(MIN_DATE,3,2);
            m=substr(MIN_DATE,1,2);
            y=substr(MIN_DATE,5,2);
            date=d;
            mone=m;
            ye=y;
            durn=p;

 function incrementday (d, m, y)

      {  if(d<28)                                       #increment day
        { date=d+1;
        mone=m;
        ye=y;}

        else if(d == 28)
        { if(m==2)
            { if(y%4==0)
                {date=d+1; mone=m; ye=y;}
          else {date=1;
            mone=3;
                ye=y;}}
        else {date = d+1;
         mone=m;
         ye=y;}}
    
    else if(d==29)
    { if(m==2)
        {date=1;
         mone=03;
         ye=y;}
      else
          {date=d+1;
           mone=m;
           ye=y;}}

    else if(d==30)
    { if(m==1||m==3||m==5||m==7||m==8||m==10||m==12)
        {date=d+1;
        ye=y;
        mone=m;}
    else {date=01;
        mone=m+1;
        ye=y;}}

    else {if(m==12)
        {date=01;
        mone=01;
        ye=y+1;
        }
        else {date=01;
            mone=m+1; ye=y;
        }}  }                            #incremented day
          

                
                        sece=secs+durn;
                        
                        if ((sece>=60) && (sece<3600))  # greater than a minute but less than an hour
                          { mine=mins+sece/60;
                                if (mine>=60)
                                    {hre=hrs+1;
                                            if (hre>=24)
                                                {hre=hre-24;
                                                                                                incrementday($d $m $y); }
        
                                     mine=mine-60;}
                            sece=sece%60;}

                        else if ((sece>=3600) && (sece<86400))  #greater than an hour but less than a day
                          { mine=mins+sece/60;
                            if(mine>=60)
                               {hre=hrs+mine/60;
                                if(hre>=24)
                                    {hre=hre-24;
                                                               incrementday($d $m $y); }         
                                mine=mine%60;}
                            sece=sece%60;}

                        else if (sece>=86400)    #greater than a day
                          {mine=mins+sece/60;
                            if (mine>=60)
                                {hre=hrs+mine/60;
                                    {hre=hre-24;
                                                               incrementday($d $m $y);}
                                 mine=mine%60;
                           sece=sece%60;}        

---------- Post updated 11-30-12 at 12:07 AM ---------- Previous update was 11-29-12 at 10:40 PM ----------

Let me just elaborate on the variables used in the above code:

MIN_TIME- start time (in hhmmss format)
MIN_DATE- start date (in mmddyy format)
durn- duration in seconds, 6digits (eg 373687 seconds or 000037 seconds)
d-start day  
m-start month
y-start year
date- end day
mone- end month
ye- end year
secs- starting seconds
mins- starting minutes
hrs- starting hours
sece- ending seconds
mine- ending minutes
hre- ending hours

I hope this information makes it easier to understand the code and help me out in correcting/improvising it.

Can you please give us more information on what kind of error are you observing while function calling?

If possible, I always use the inbuilt date command for date arithmetic, as it handles boundary transitions for you. Can't say I have tested it against a leap year but I dare say it works for that too.

If you want to add a duration in seconds to a date then most people convert the date to epoch seconds first. Then it is a question of simple arithmetic to add the duration you want -

#! /bin/bash
##############################################################
##
## Name                    : m_get_epoch_seconds
## Author                   : Bradley Atkins
## Description            : Return a date time as epoch seconds
## Date                      : 24/09/2012
## Args                      : 1- Time string, e.g. -
##                                    "Jan 1, 1980 00:00:01"
##                                    "January 1, 1980 23:59:59"
##                                    "January 1 1980 23:59:59"
##                                    "1980/1/1 00:00:01"
## Status                    : Reviewed [n]
##                                 Tested [n]
##                                 Released [n]
##############################################################
m_get_epoch_seconds()
{
     [[ $# -gt 1 ]] && return 1
     local TIME="$(date +%s)"
     if [[ $# -eq 1 ]]
     then
            TIME=$(date +%s -d "${1}" 2>/dev/null)
             [[ $? -eq 0 ]] || return 2
     fi
 
     echo "${TIME}"
}
m_get_epoch_seconds "Jan 1, 1980 00:00:01"
m_get_epoch_seconds "January 1, 1980 23:59:59"
m_get_epoch_seconds "January 1 1980 23:59:59"
m_get_epoch_seconds "1980/1/1 00:00:01"
m_get_epoch_seconds

Output

315532801
315619199
315619199
315532801
1354478648

Similarly, you can use the date command to convert epoch seconds to a valid date format -

#! /bin/bash
##############################################################
##
## Name                  : m_get_date_from_epoch
## Author                 : Bradley Atkins
## Description          : Convert an epoch time string (secs) to
##                              a human readable date format.
## Date                    : 24/09/2012
## Args                     : 1 - Valid date format string
##                               2 - Time (Epoch Seconds) '@nnnnnnnn'
## e.g. -
##                                  "%d/%m/%Y" "@2147483647"
##                                  "%d/%m/%Y %H:%M:%S" "@2147483647"
##                                  "%d/%m/%y" "@2147483647"
## Status                  : Reviewed [n]
##                               Tested [n]
##                               Released [n]
##############################################################
m_get_date_from_epoch()
{
     ## Usage
     [[ $# -eq 2 ]] || return 1
     local FORMAT= EPOCH= 
     FORMAT="+\"${1}\""
     [[ ${2} =~ ^[@]+[[:digit:]]+$ ]] || return 2
     eval date --date="${2}" "${FORMAT}" || return 3
}
 
m_get_date_from_epoch "%d/%m/%Y" "@2147483647"
m_get_date_from_epoch "%d/%m/%Y %H:%M:%S" "@2147483647"
m_get_date_from_epoch "%d/%m/%y" "@2147483647"

Output -

19/01/2038
19/01/2038 03:14:07
19/01/38

Hope this helps

Brad

Brad, the %s format to date is not standard. GNU and [Free|Net]BSD have it, other variants might not.

These bash functions I just wrote do time arithmetic:

time2seconds() #@ Calculate no. of seconds from [H]H:[M]M:S result_var
{
  local s hours minutes seconds var IFS=:
  set -- $*
  hours=${1#0}
  minutes=${2#0}
  seconds=${3#0}
  var=${4:-_t2s}
  s=$(( hours * 3600 + minutes * 60 + seconds ))
  printf -v "$var" "%d" "$s"
}

seconds2time() #@ Convert number of seconds to [DAYS ]HH:MM:SS
{
  local s days hours minutes seconds var
  s=$1
  var=${2:-_s2t}
  seconds=$(( s % 60 ))
  hours=$(( s / 3600 ))
  minutes=$(( (s - hours * 3600) / 60 ))

  if [ $hours -gt 24 ]
  then
    days=$(( hours / 24 ))
    hours=$(( hours % 24 ))
  fi

  printf -v "$var" "%s%02d:%02d:%02d" ${days:+"$days "} "$hours" "$minutes" "$seconds"
}

addseconds() #@ To HH:MM:SS add SECS
{
  local time=$1 secs=$2
  time2seconds "$time"
  seconds2time "$(( _t2s + secs ))"
}

They can be combined with the date functions from The Dating Game to solve your problem.