Home AMX User Forum AMX General Discussion

Useful Time Functions

Here's some useful time functions. There are similar functions out there but I can never find them when I need them so I made these.

This uses the standard TIME keyword format so you can just pass it "TIME" and it will return that time in total amount of seconds so you can do math on it.
DEFINE_FUNCTION LONG fn24HourTimeTo_Seconds(CHAR iTime[])

     {
     RETURN ((((ATOI(REMOVE_STRING(iTime,"':'",1)) * 60) * 60) + ATOI(REMOVE_STRING(iTime,"':'",1)) * 60) + ATOI(iTime)) ;
     }
Once you do your math on the seconds you can convert it back to the "TIME" format with this.
DEFINE_FUNCTION CHAR[8] fnSecondsTo_24HourTime(LONG iTimeInSeconds)

     {
     RETURN   "right_string("'0',ITOA(iTimeInSeconds / 3600)",2),':',
		    right_string("'0',ITOA((iTimeInSeconds % 3600) / 60)",2),':',
			 right_string("'0',ITOA((iTimeInSeconds % 3600) % 60)",2)" ;
     }
Here's a function which uses these functions to add tiime to the current time. In this case to create an expiration time 10 minutes later.
DEFINE_CONSTANT

CHAR WEB_MONTH[12][3] 	= {'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'} ;
  
DEFINE_FUNCTION fnUpdateDATE_TIME()

     {
     STACK_VAR CHAR cLDATE[10] ;
     STACK_VAR INTEGER nMonth ;
     STACK_VAR INTEGER nDay ;
     STACK_VAR CHAR cYear[4] ;
     
     cLDATE = LDATE ;
     nMonth = atoi(REMOVE_STRING(cLDATE,"'/'",1)) ;
     nDay = atoi(REMOVE_STRING(cLDATE,"'/'",1)) ;
     cYear = cLDATE ;
       
     cWebCurDateTime = "DAY,', ',itoa(nDay),' ',WEB_MONTH[nMonth],' ',cLDATE,' ',TIME,' EST'" ;
     cWebExpDateTime = "DAY,', ',itoa(nDay),' ',WEB_MONTH[nMonth],' ',cLDATE,' ',
			 fnSecondsTo_24HourTime(fn24HourTimeTo_Seconds(TIME) + EXPIRES_IN_SECONDS),' EST'" ;
    
     RETURN ;
     }
In the above example EXPIRES_IN_SECONDS is a constant of 600. Which is 600 seconds or ten minutes that gets added to the current time to create the expiration time.

Comments

  • Joe HebertJoe Hebert Posts: 2,159
    You may or may not be aware but it should be noted that the posted code will generate undesirable results if the start time + the offset time roll over into a new day.

    Using your example, cWebExpDateTime will not be correct if the function is called to add the 10 minutes at say 11:55 PM. The first problem (which has an easy enough fix) is that the time will come back as 24:05, an illegal time. The day of the month will definitely be wrong as will the day of the week. It?s also possible the month may be wrong and for that matter the year will also be wrong if the function is called on Dec. 31st at 11:55 PM.

    Unless the application can live with these limitations the only real way to go in my mind is to work with UNIX time functions when time/date addition/subtraction come into play.
  • viningvining Posts: 4,368
    I'll have to take a closer looke when I get a chance.
  • mpullinmpullin Posts: 949
    DEFINE_FUNCTION CHAR[6] itoa_zf(INTEGER n,INTEGER len){
        // returns ASCII string representing n, that is zero filled to length len
        STACK_VAR CHAR result[6]; // max return size
        STACK_VAR INTEGER i;
        
        result = itoa(n);
        while(LENGTH_STRING(result)<len){
    	result = "'0',result";
        }
        
        return result;
    }
    DEFINE_FUNCTION CHAR[8] TIME_MILITARY_TO_REAL(CHAR cT[]){
        // given '13:23' returns '1:23 PM'
        STACK_VAR INTEGER h;
        STACK_VAR INTEGER m;
        STACK_VAR CHAR t[5];
        
        t = cT;
        h = atoi("REMOVE_STRING(t,':',1)");
        m = atoi("t");
        
        if(h > 12){
    	// PM time
    	return "itoa(h-12),':',itoa_zf(m,2),' PM'";
        } else if(h==0){
    	// 12:00am-12:59am
    	return "'12:',itoa_zf(m,2),' AM'";
        } else if(h==12){
    	// 12:00pm-12:59pm
    	return "'12:',itoa_zf(m,2),' PM'";
        } else{
    	// 1:00am-11:59am
    	return "itoa(h),':',itoa_zf(m,2),' AM'";
        }
        return 'Never'; // unreachable
    }
    DEFINE_FUNCTION CHAR[6] TIME_MIN_TO_HRMIN(INTEGER nT){
        // given 61 returns '1:01'
        STACK_VAR INTEGER h;
        STACK_VAR INTEGER m;
        
        m = nT;
        h = 0;
        while(m > 59){
    	m = m - 60;
    	h++;
        }
        return "itoa(h),':',itoa_zf(m,2)";
    }
    
  • Joe HebertJoe Hebert Posts: 2,159
    <FORMAT_PSA>

    What?s wrong with the FORMAT command? It?s way more flexible and powerful and there isn?t a 6 character limitation.

    Instead of this:
    DEFINE_FUNCTION CHAR[6] itoa_zf(INTEGER n,INTEGER len){
        // returns ASCII string representing n, that is zero filled to length len
        STACK_VAR CHAR result[6]; // max return size
        STACK_VAR INTEGER i;
        
        result = itoa(n);
        while(LENGTH_STRING(result)<len){
    	result = "'0',result";
        }
        
        return result;
    }
    
    answer = itoa_zf(number,length)
    

    All you really need is this:
    answer = FORMAT("'%0',ITOA(length),'d'",number)
    

    I?ve been hired as the 2009 FORMAT spokesperson so I was compelled to respond and spread the good word.

    </FORMAT_PSA>
  • a_riot42a_riot42 Posts: 1,624
    Joe Hebert wrote: »
    <FORMAT_PSA>
    What?s wrong with the FORMAT command? It?s way more flexible and powerful and there isn?t a 6 character limitation.

    That's cheating.
    Paul
  • viningvining Posts: 4,368
    This function seems to solve the rollover problems but needs some more testing since I only had a couple hours this eveining to play. I included a test line in define program so you can open variable debug and just drop time in seconds into the nTestSecondsAdded variable and then set the nTestTime variable to 1 to see it print in diagnostics.

    DEFINE_CONSTANT
    
    NUM_SECONDS_DAY		    = 86400 ;
    CHAR WEB_MONTH[12][3] 	    = {'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'} ; 
    LONG DAYS_MONTHs[12]        = {31   ,28   ,31   ,30   ,31   ,30   ,31   ,31   ,30   ,31   ,30   ,31} ;
    LONG DAYS_LEAPMONTHs[12]    = {31   ,29   ,31   ,30   ,31   ,30   ,31   ,31   ,30   ,31   ,30   ,31} ;
    CHAR WEB_DAYS[7][3]	    = {'SUN','MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'} ;
    
    DEFINE_VARIABLE //GENERAL VARS
    
    VOLATILE LONG     	nTestSecondsAdded ;
    VOLATILE INTEGER        nTestTime ;
    
    DEFINE_FUNCTION CHAR[30] fnDo24hrTimeAddition(CHAR iLDate[],CHAR i24HourTime[],LONG iSecondsAdded)     
         
         {
         STACK_VAR INTEGER nYear ;      
         STACK_VAR INTEGER nMonth ;
         STACK_VAR INTEGER nLeap ;
         STACK_VAR CHAR c24HrTime[8] ;
         STACK_VAR CHAR cNewDate[10] ;
         STACK_VAR LONG nDay ;
         STACK_VAR LONG nDaysOver ;
         STACK_VAR LONG nSecondsRemain ;
         STACK_VAR LONG nTotalTimeSeconds ;
         
         nTotalTimeSeconds = fn24HourTimeTo_Seconds(i24HourTime) + iSecondsAdded ;
         nMonth = atoi("REMOVE_STRING(iLDATE,"'/'",1)") ;
         nDay   = atoi("REMOVE_STRING(iLDATE,"'/'",1)") ;
         nYear  = atoi("iLDATE") ;
         
         if(nTotalTimeSeconds > NUM_SECONDS_DAY)
    	  {
    	  STACK_VAR INTEGER i ;
    	  
    	  nDaysOver = nTotalTimeSeconds / NUM_SECONDS_DAY ;
    	  nSecondsRemain = nTotalTimeSeconds % NUM_SECONDS_DAY ;
    	  if(!(nYear % 4))//http://en.wikipedia.org/wiki/Leap_year
    	       {
    	       nLeap = 1 ;
    	       if(!(nYear % 100))//double check these rules?????? 
    		    {
    		    if(nYear % 400)
    			 {
    			 nLeap = 0 ;
    			 }
    		    }
    	       }
    	  if(nLeap)
    	       {
    	       if((nDaysOver + nDay) > DAYS_LEAPMONTHs[nMonth])
    		    {
    		    nDaysOver = nDaysOver - (DAYS_LEAPMONTHs[nMonth] - nDay) ;
    		    nMonth++ ;
    		    
    		    for(nMonth = nMonth ; nDaysOver > DAYS_LEAPMONTHs[nMonth] ; nMonth++)
    			 {
    			 nDaysOver = (nDaysOver - DAYS_LEAPMONTHs[nMonth]) ;
    			 if(nMonth == 12)
    			      {
    			      nMonth = 1 ;
    			      nYear ++ ;
    			      }
    			 }
    		    nDay = nDaysOver ;
    		    }
    	       else
    		    {
    		    nDay = nDaysOver + nDay ;
    		    }
    	       }
    	  else
    	       {
    	       if((nDaysOver + nDay) > DAYS_MONTHs[nMonth])
    		    {
    		    nDaysOver = nDaysOver - (DAYS_MONTHs[nMonth] - nDay) ;
    		    nMonth++ ;
    		    
    		   for(nMonth = nMonth ; nDaysOver > DAYS_MONTHs[nMonth] ; nMonth++)
    			 {
    			 nDaysOver = (nDaysOver - DAYS_MONTHs[nMonth]) ;
    			 if(nMonth == 12)
    			      {
    			      nMonth = 1 ;
    			      nYear ++ ;
    			      }
    			 }
    		    nDay = nDaysOver ;
    		    }
    	       else
    		    {
    		    nDay = nDaysOver + nDay ;
    		    }
    	       }
    	  c24HrTime = fnSecondsTo_24HourTime(nSecondsRemain) ; 
    	  }
         else
    	  {
    	  c24HrTime = fnSecondsTo_24HourTime(nTotalTimeSeconds) ;
    	  }
         cNewDate = "right_string("'0',ITOA(nMonth)",2),'/',right_string("'0',ITOA(nDay)",2),'/',right_string("'0',ITOA(nYear)",2)" ;
    	   
         RETURN "WEB_DAYS[TYPE_CAST(DAY_OF_WEEK(cNewDate))],', ',itoa(nDay),' ',WEB_MONTH[nMonth],' ',itoa(nYear),' ',c24HrTime,' EST'" ;
         }
    
    
    DEFINE_PROGRAM
    
    if(nTestTime)
         {
         SEND_STRING 0, "'WEB_DEBUG: ',fnDo24hrTimeAddition(LDATE,TIME,nTestSecondsAdded)" ;
         nTestTime = 0 ;
        }
    
    
  • Joe HebertJoe Hebert Posts: 2,159
    vining wrote: »
    This function seems to solve the rollover problems but needs some more testing since I only had a couple hours this eveining to play.
    I took the code out for a quick spin and it looks good from here.
  • a_riot42a_riot42 Posts: 1,624
    Does it take into account daylight savings time, as that would change the results depending on your location, time of year and local DST rules, no?
    Paul
  • yuriyuri Posts: 861
    shouldn't we all use the i!timemanager module? :D
  • viningvining Posts: 4,368
    a_riot42 wrote:
    Does it take into account daylight savings time, as that would change the results depending on your location, time of year and local DST rules, no?
    I didn't even think of that but that should be fairly easy to impliment.

    yuri wrote:
    shouldn't we all use the i!timemanager module?
    I use it for Sunrise/Sunset and have been doing so for ever but I didn't know it did anything else. Does it? If it does I might have used it and save myself from having to think a little.

    I also wondered why there's the built in function "DAY_OF_WEEK" which can return what the day of the week is for any date you plug but there's no other standard functions for time manipulation.
  • a_riot42a_riot42 Posts: 1,624
    vining wrote: »
    a_riot42 wrote:
    I didn't even think of that but that should be fairly easy to impliment.

    :)

    You're kidding right?
    Paul
  • viningvining Posts: 4,368
    I'd just need to create DATE to DAY_OF_YEAR function. Have variable for the nDST_DOY_START and nDST_DOY_END and if my DAY_OF_YEAR of the resulting date falls bewteen the start and stop while the initial date falls before the start I could adjust the resulting hour. If it spans several years or just both the start and stop days they'll cancel each other and no fuss.

    To figure out these days I'll do a DAY_OF_WEEK on 03/01/cCurYear which will tell me the day of the week 1-7 that date is and I'll do math to figure out when the 2nd "1" would be. Then do the same thing for 11/01/cCurYear and then figure out when the first "1" would be.

    I thinks this would work and be fairly simple but I've been wrong before........... :)
  • a_riot42a_riot42 Posts: 1,624
    You have your work cut out for you. DST is a b*tch, since it depends on location and the DST rules that change periodically. Some locations don't have DST, some states do or have numerous time zones. Without longitude and latitude, I can't see how you can make it work for any arbitrary location and time zone. Time functions are very non-trivial to write correctly, so if you can do it, you will have accomplished something few programmers can do.
    Paul
  • viningvining Posts: 4,368
    a_riot42 wrote:
    Time functions are very non-trivial to write correctly
    I didn't say anything about doing it correctly! :D

    If I was to attempt DST I would just make work for my area and my needs and if others want to use it they could do the same for theirs. Otherwise it would be a real pain to make universal. Still if I did it like iTimeManager and make rules that the programmer has to fill in based on their region that still might not be so bad. DST yes/no, begins/ends. I once wanted to do a module that would figure holidays and when I started looking at Muslim holidays I had to give up since they'll change for any number of reasons like rain or just overcast skies.

    As for my needs, I only wanted to add 10 minutes to the current time for a web page expiration time which I don't even need to include in my web page header. I just figured why not just for the heck of it and now we're talking DST rules for the world. :eek:
Sign In or Register to comment.