Announcement

Collapse
No announcement yet.

New DateDiff function

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • New DateDiff function

    Credit to Centauri Soldier as this function uses the String.ToTable function from his ABI plugin.

    So I got bored this week and was working on a few things, and decided to tackle the DateDiff function. I know there are a few solutions kicking around but I was just curious to see if I could make it work.

    I took the same approach as Excel, and I covert the date to a datecode then just find the difference between the two.

    It's far from perfect but I think it will work for a lot of people.

    It will work with the following formats:
    M/D/YY
    MM/DD/YYYY
    M-D-YY
    MM-DD-YYYY
    Month D, YY
    DayOfWeek, Month D, YY

    So basically it works with any of the US formats (sorry my Euro and Canadian friends)

    The biggest problem you run into is if you give it one date with a YY and the other with a YYYY format. (It treats "14" as the year 14AD which still works if you give it another 2 digit year, but will obviously give you an incorrect result when comparing to a date that is ~2000 years later)

    The man reason I did it was so I could compare the result of File.GetAttributes().WriteDate to System.GetDate() without having to worry about formatting the date before I tried to manipulate it every time.

    I didn't do exhaustive checking with it, but I compared all of the leap year scenarios I could think of and all of them agreed with the results in Excel.

    So anyway, hopefully someone else might find it handy

    Code:
    function DateDiff(sStartDate,sEndDate)
    
    local intFinal = 0
    
    	--Strip out DayOfWeek, and replace Month with a number
    	sStartDate = String.Replace(sStartDate, "Monday, ", "", false);
    	sStartDate = String.Replace(sStartDate, "Tuesday, ", "", false);
    	sStartDate = String.Replace(sStartDate, "Wednesday, ", "", false);
    	sStartDate = String.Replace(sStartDate, "Thursday, ", "", false);
    	sStartDate = String.Replace(sStartDate, "Friday, ", "", false);
    	sStartDate = String.Replace(sStartDate, "Saturday, ", "", false);
    	sStartDate = String.Replace(sStartDate, "Sunday ", "", false);
    	sStartDate = String.Replace(sStartDate, "January ", "1, ", false);
    	sStartDate = String.Replace(sStartDate, "February ", "2, ", false);
    	sStartDate = String.Replace(sStartDate, "March ", "3, ", false);
    	sStartDate = String.Replace(sStartDate, "April ", "4, ", false);
    	sStartDate = String.Replace(sStartDate, "May ", "5, ", false);
    	sStartDate = String.Replace(sStartDate, "June ", "6, ", false);
    	sStartDate = String.Replace(sStartDate, "July ", "7, ", false);
    	sStartDate = String.Replace(sStartDate, "August ", "8, ", false);
    	sStartDate = String.Replace(sStartDate, "September ", "9, ", false);
    	sStartDate = String.Replace(sStartDate, "October ", "10, ", false);
    	sStartDate = String.Replace(sStartDate, "November ", "11, ", false);
    	sStartDate = String.Replace(sStartDate, "December ", "12, ", false);
    
    	sEndDate = String.Replace(sEndDate, "Monday, ", "", false);
    	sEndDate = String.Replace(sEndDate, "Tuesday, ", "", false);
    	sEndDate = String.Replace(sEndDate, "Wednesday, ", "", false);
    	sEndDate = String.Replace(sEndDate, "Thursday, ", "", false);
    	sEndDate = String.Replace(sEndDate, "Friday, ", "", false);
    	sEndDate = String.Replace(sEndDate, "Saturday, ", "", false);
    	sEndDate = String.Replace(sEndDate, "Sunday, ", "", false);
    	sEndDate = String.Replace(sEndDate, "January ", "1, ", false);
    	sEndDate = String.Replace(sEndDate, "February ", "2, ", false);
    	sEndDate = String.Replace(sEndDate, "March ", "3, ", false);
    	sEndDate = String.Replace(sEndDate, "April ", "4, ", false);
    	sEndDate = String.Replace(sEndDate, "May ", "5, ", false);
    	sEndDate = String.Replace(sEndDate, "June ", "6, ", false);
    	sEndDate = String.Replace(sEndDate, "July ", "7, ", false);
    	sEndDate = String.Replace(sEndDate, "August ", "8, ", false);
    	sEndDate = String.Replace(sEndDate, "September ", "9, ", false);
    	sEndDate = String.Replace(sEndDate, "October ", "10, ", false);
    	sEndDate = String.Replace(sEndDate, "November ", "11, ", false);
    	sEndDate = String.Replace(sEndDate, "December ", "12, ", false);
    	
    	--Search for delimiter and convert string to table
    	if String.Find(sStartDate, "/", 1, false) > 0 then
    		tblStartDate = String.ToTable(sStartDate, "/")
    	elseif 	String.Find(sStartDate, "-", 1, false) > 0 then
    		tblStartDate = String.ToTable(sStartDate, "-")
    	elseif 	String.Find(sStartDate, ", ", 1, false) > 0 then
    		tblStartDate = String.ToTable(sStartDate, ", ")
    	else
    		intFinal = -1
    	end
    	
    	if String.Find(sEndDate, "/", 1, false) > 0 then
    		tblEndDate = String.ToTable(sEndDate, "/")
    	elseif 	String.Find(sEndDate, "-", 1, false) > 0 then
    		tblEndDate = String.ToTable(sEndDate, "-")
    	elseif 	String.Find(sEndDate, ", ", 1, false) > 0 then
    		tblEndDate = String.ToTable(sEndDate, ", ")
    	else
    		intFinal = -1
    	end
    
    
    	--convert strings to number values
    	if #tblStartDate == 3 then
    		tblStartDate[1] = String.ToNumber(tblStartDate[1])
    		tblStartDate[2] = String.ToNumber(tblStartDate[2])
    		tblStartDate[3] = String.ToNumber(tblStartDate[3])
    	else
    		intFinal = -1
    	end	
    
    	if #tblEndDate == 3 then
    		tblEndDate[1] = String.ToNumber(tblEndDate[1])
    		tblEndDate[2] = String.ToNumber(tblEndDate[2])
    		tblEndDate[3] = String.ToNumber(tblEndDate[3])
    	else
    		intFinal = -1
    	end	
    
    	--check to make sure numbers converted correctly
    	if tblStartDate[1] == 0 or tblStartDate[2] == 0 or tblStartDate[3] == 0 then 
    		intFinal = -1
    	end
    	
    
    	if tblEndDate[1] == 0 or tblEndDate[2] == 0 or tblEndDate[3] == 0 then 
    		intFinal = -1
    	end
    	
    	--deal with all of the date math
    	if intFinal == 0 then
    		FnMon1  = tblStartDate[1]
    		FnDay1  = tblStartDate[2]
    		FnYear1 = tblStartDate[3]
    		FnMon2  = tblEndDate[1]
    		FnDay2  = tblEndDate[2]
    		FnYear2 = tblEndDate[3]
    
    		FnDateCode1 = Math.Floor(FnYear1*365.25)+1
    		if      FnMon1 == 1 then
    			FnDateCode1 = FnDateCode1 + FnDay1
    		elseif FnMon1 == 2 then			 		
    			FnDateCode1 = FnDateCode1 + 31 + FnDay1
    		elseif FnMon1 == 3 then			 		
    			FnDateCode1 = FnDateCode1 + 59 + FnDay1
    		elseif FnMon1 == 4 then			 		
    			FnDateCode1 = FnDateCode1 + 90 + FnDay1
    		elseif FnMon1 == 5 then			 		
    			FnDateCode1 = FnDateCode1 + 120 + FnDay1
    		elseif FnMon1 == 6 then			 		
    			FnDateCode1 = FnDateCode1 + 151 + FnDay1
    		elseif FnMon1 == 7 then			 		
    			FnDateCode1 = FnDateCode1 + 181 + FnDay1
    		elseif FnMon1 == 8 then			 		
    			FnDateCode1 = FnDateCode1 + 212 + FnDay1
    		elseif FnMon1 == 9 then			 		
    			FnDateCode1 = FnDateCode1 + 243 + FnDay1
    		elseif FnMon1 == 10 then			 		
    			FnDateCode1 = FnDateCode1 + 273 + FnDay1
    		elseif FnMon1 == 11 then			 		
    			FnDateCode1 = FnDateCode1 + 304 + FnDay1
    		elseif FnMon1 == 12 then			 		
    			FnDateCode1 = FnDateCode1 + 334 + FnDay1
    		end	
    		if Math.Mod(FnYear1, 4) == 0 and FnMon1 >= 3 then
    			FnDateCode1 = FnDateCode1 + 1
    		end
    		
    		FnDateCode2 = Math.Floor(FnYear2*365.25)+1
    		if      FnMon2 == 1 then
    			FnDateCode2 = FnDateCode2 + FnDay2
    		elseif FnMon2 == 2 then			 		
    			FnDateCode2 = FnDateCode2 + 31 + FnDay2
    		elseif FnMon2 == 3 then			 		
    			FnDateCode2 = FnDateCode2 + 59 + FnDay2
    		elseif FnMon2 == 4 then			 		
    			FnDateCode2 = FnDateCode2 + 90 + FnDay2
    		elseif FnMon2 == 5 then			 		
    			FnDateCode2 = FnDateCode2 + 120 + FnDay2
    		elseif FnMon2 == 6 then			 		
    			FnDateCode2 = FnDateCode2 + 151 + FnDay2
    		elseif FnMon2 == 7 then			 		
    			FnDateCode2 = FnDateCode2 + 181 + FnDay2
    		elseif FnMon2 == 8 then			 		
    			FnDateCode2 = FnDateCode2 + 212 + FnDay2
    		elseif FnMon2 == 9 then			 		
    			FnDateCode2 = FnDateCode2 + 243 + FnDay2
    		elseif FnMon2 == 10 then			 		
    			FnDateCode2 = FnDateCode2 + 273 + FnDay2
    		elseif FnMon2 == 11 then			 		
    			FnDateCode2 = FnDateCode2 + 304 + FnDay2
    		elseif FnMon2 == 12 then			 		
    			FnDateCode2 = FnDateCode2 + 334 + FnDay2
    		end	
    		if Math.Mod(FnYear2, 4) == 0 and FnMon2 >= 3 then
    			FnDateCode2 = FnDateCode2 + 1
    		end
    
    		-- **finally** find the difference between the two dates
    		intFinal = Math.Abs(FnDateCode1 - FnDateCode2)
    	end
    	
    	return intFinal
    end

  • #2
    I saw this post and just wanted to write an alternative in OOP.

    HMM, you should try to work with dates as strings.

    I wrote 2 "diff" functions (with different results) and also a way to guess the date format for US dates by using lua patterns.

    NOTE: I have tested the code with the supplied examples.

    PHP Code:
    Date = {
        -- Class 
    variable shared across all objects
        def 
    = {
            
    year = {= {length=4}, = {length 2} },
            
    month = {= {length 2}},
            
    day = {= {length 2}},
            
    hour = {= {length 2}, = {length 2}},
            
    min = {= {length 2}},
            
    sec = {= {length 2}}
        },
        
    times = {
            
    years 365.25*24*60*60,
            
    months 30*24*60*60,
            
    days 24*60*60,
            
    hours 60*60,
            
    minutes 60,
            
    seconds 1
        
    },
        
    specialFormats = {
            
    '(%d%d?)[%/%-](%d%d?)[%/%-](%d%d%d%d)', -- MM[/-]DD[/-]YYYY
            
    '(%w+)%s(%d%d?),%s(%d%d)', -- Month DYY
            
    '%w+, (%w+)%s(%d%d?),%s(%d%d)' -- DayOfWeekMonth DYY
        
    },
        
    months = {January '01',February '02',March '03',April '04',May '05',June '06',July '07',August '08',September '09',October '10',November '11',December '12'}
    }

    function 
    Date.new(dateformat)
        
    local self = {}
        if ( 
    type(date) == 'number' then
            self
    .timestamp date;
        elseif 
    type(date) == 'string' then
            local tableTime 
    = {};
            if 
    not format then
                local found 
    false;
                for 
    kspecial in pairs(Date.specialFormats) do
                    
    date date:gsub(special, function(monthdayyear)
                        
    local f = {(#year==2) and 'y' or "Y", "m","d"}
                        
    format table.concat(f"-");
                        
    found true;
                        return 
    year.."-"..Date.months[month].."-"..day
                    end
    )
                    if 
    found then break end
                end
                
    print(formatdate)
                
    format format or 'Y-m-d H:M:s'
            
    end
            format
    :gsub("%w", function(letter)
                for 
    tipoformatos in pairs(Date.def) do
                    for 
    iformato in pairs(formatos) do
                        if 
    == letter then
                            date 
    date:gsub(string.rep("%d"formato.length), function(number)
                                
    number tonumber(number)
                                if ( 
    == "y"then
                                    
    if (number >= 70 and number <= 99 then
                                        number 
    number 1900
                                    
    else
                                        
    number number 2000
                                    end
                                end
                                tableTime
    [tipo] = number
                                
    return ""
                            
    end1)
                            break;
                        
    end
                    end
                end
                
    return letter;
            
    end)
            
    self.timestamp os.time(tableTime)
        else
            
    self.timestamp os.time()
        
    end

        
    function self.diff(date2)
            
    local diffInSeconds date2.timestamp-self.timestamp;
            
    local constant diffInSeconds;
            
    local m = (diffInSeconds 0) and -or 1;
            
    local times = {
                
    years 0,
                
    months 0,
                
    days 0,
                
    hours 0,
                
    minutes 0,
                
    seconds 0,
            }
            function 
    times.get(kind)
                return 
    times[kind];
            
    end
            
    function times.getTotal(kind)
                return 
    times["t_"..kind];
            
    end

            
    for ikind in pairs({'years''months''days''hours''minutes''seconds'}) do
                
    local t constant/Date.times[kind]
                
    local n diffInSeconds/Date.times[kind]
                
    local absolute math.abs(n)
                
    local value math.floor(absolute);
                if ( 
    absolute 1then
                    times
    [kind] = Date.times[kind];
                    
    diffInSeconds absolute value;
                    
    times[kind] = value m;
                    
    diffInSeconds diffInSeconds Date.times[kind];
                
    end
                
    if (math.abs(t) >= 1then
                    times
    ["t_"..kind] = t;
                else
                    
    times["t_"..kind] = 0;
                
    end
            end
            times
    ["t_seconds"] = constant;
            return 
    times;
        
    end

        
    function self.odiff(date2)
            
    local diffInSeconds date2.timestamp+self.timestamp;
            
    local t1 os.date("*t"date2.timestamp);
            
    local t2 os.date("*t"self.timestamp);
            
    local t3 = {}
            for 
    kv in pairs(t1) do
                if 
    Date.def[kthen
                    
    if ( type(v) == 'number' then
                        t3
    [k] = t2[k] - v;
                    
    end
                end
            end
            
    return t3;
        
    end

        
    function self.format(stringFormat)
            
    stringFormat stringFormat or "c"
            
    return os.date(stringFormat:gsub("([\\]?)(%w)", function(a,b)
                if 
    == "\\" then
                    
    return a..b
                
    else
                    return 
    a.."%"..b
                end
            end
    ), self.timestamp)
        
    end

        
    function self.isLeapYear()
            
    local year tonumber(self.format("Y"));
            return 
    year%4==and (year%100~=or year%400==0);
        
    end

        
    return self
    end

    Date.new("1990-01-30");
    Date.new("1990-01-31");
    Date.new("29-01-90""d-m-y");
    print(
    a.format("Y-m-d"), b.format("Y-m-d"), c.format("Y-m-d"))
    print(
    a.diff(b).getTotal('days'), a.diff(c).getTotal('days'))

    table.foreach(a.odiff(b), print)
    table.foreach(a.odiff(c), print)

    Date.new("August 13, 90")
    print(
    a.format("Y-m-d")) 

    Comment


    • #3
      BTW, you should be using a lua library instead of trying to reinvent the wheel but maybe as you, i also like to write my own code. Maybe you just needed to write a function to guess the format and then return a desired format to then calculate the diff between to dates with another library.

      Another note: I'm out of practice with lua so if you see any typo or something like writing 3 times the self.timestamp = os.time(tableTime) instead of 1 at the end of the "IF statement" or wrote 3 "IF" instead of 1, etc...don't be hard with me :P

      Comment

      Working...
      X