24
Aug
07

Date math for lazy people

I’ve always hated doing simple date manipulation and having to create wacky variables like “millisecondsPerDay” where you multiply milliseconds by seconds by hours by whatever else. So here’s a useful (and largely untested) function, dateAdd(), which is similar to the function that ColdFusion-ers have enjoyed for years.

Full code (and instructions) after the jump.

In addition to my code being untested, I should point out that the datepart strings are different than those in ColdFusion. Where ColdFusion uses formats such as: “yyyy”, “q”, “m”, “y”, “d”, “w”, etc. I’ve cut corners and used the property names from the Date class (”fullYear”, “month”, “date”, “hours”, “minutes”, “seconds”, “milliseconds”) instead. This let me use:

returnDate[datepart] += number;

Which is basically a shorthand for something like:

returnDate["months"] += 3;

If you wanted to use the ColdFusion type notation it should be easy enough to extend the dateAdd() function and add another simple switch statement which converts the “yyyy” to “fullYear”, and so on.

Using the dateAdd() function is [hopefully] pretty straight forward.

  • All three parameters are optional. If no parameters are passed, the current date is returned (similar to the Date class constructor).
  • If only the first parameter (datepart) is passed, the current date is returned.
  • If the first two parameters (datepart and number) are passed, the function adds the specified number of date parts to the current date. For example, if you called dateAdd("month", 3) the function would return the current date plus 3 months. If you called dateAdd("month", -3) the function would return the current date minus 3 months.
  • If the first three parameters (datepart, number, and date) are passed, the function adds the specified number of date parts to the specified date. For example, if you called dateAdd("day", 15, calendarStartDate) the function would return the value of the calendarStartDate variable (which must be a Date object) plus 15 days.

View MXML

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2007/08/24/date-math-for-lazy-people/ -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="vertical"
        verticalAlign="middle"
        backgroundColor="white"
        creationComplete="init()">

    <mx:Script>
        <![CDATA[
            import mx.controls.dataGridClasses.DataGridColumn;
            private function init():void {
                var zeroDate:Date = new Date(0);

                /* fullYear */
                arrColl.addItem({label:"+2 years", data:dateAdd("fullYear", 2, zeroDate)});
                arrColl.addItem({label:"-2 years", data:dateAdd("fullYear", -2, zeroDate)});
                /* month */
                arrColl.addItem({label:"+11 months", data:dateAdd("month", 11)});
                arrColl.addItem({label:"-11 months", data:dateAdd("month", -11)});
                /* date */
                arrColl.addItem({label:"+4 date", data:dateAdd("date", 4)});
                arrColl.addItem({label:"-4 date", data:dateAdd("date", -4)});
                /* hours */
                arrColl.addItem({label:"+6 hours", data:dateAdd("hours", 6)});
                arrColl.addItem({label:"-6 hours", data:dateAdd("hours", -6)});
                /* minutes */
                arrColl.addItem({label:"+45 minutes", data:dateAdd("minutes", 45)});
                arrColl.addItem({label:"-45 minutes", data:dateAdd("minutes", -45)});
                /* seconds */
                arrColl.addItem({label:"+900 seconds", data:dateAdd("seconds", 900)});
                arrColl.addItem({label:"-900 seconds", data:dateAdd("seconds", -900)});
                /* milliseconds */
                arrColl.addItem({label:"+720000 milliseconds", data:dateAdd("milliseconds", 720000)});
                arrColl.addItem({label:"-720000 milliseconds", data:dateAdd("milliseconds", -720000)});
            }

            private function dateAdd(datepart:String = "", number:Number = 0, date:Date = null):Date {
                if (date == null) {
                    /* Default to current date. */
                    date = new Date();
                }

                var returnDate:Date = new Date(date.time);;

                switch (datepart.toLowerCase()) {
                    case "fullyear":
                    case "month":
                    case "date":
                    case "hours":
                    case "minutes":
                    case "seconds":
                    case "milliseconds":
                        returnDate[datepart] += number;
                        break;
                    default:
                        /* Unknown date part, do nothing. */
                        break;
                }
                return returnDate;
            }

            private function data_labelFunc(item:Object, column:DataGridColumn):String {
                return dateFormatter.format(item[column.dataField]);
            }
        ]]>
    </mx:Script>

    <mx:ArrayCollection id="arrColl" />

    <mx:DateFormatter id="dateFormatter"
            formatString="YYYY/MM/DD HH:NN:SS" />

    <mx:Label text="Now: {dateAdd()}" />
    <mx:DataGrid id="dataGrid"
            dataProvider="{arrColl}"
            sortableColumns="false"
            width="400">
        <mx:columns>
            <mx:DataGridColumn dataField="label"
                    headerText="Label"
                    width="150" />
            <mx:DataGridColumn dataField="data"
                    headerText="YYYY/MM/DD HH:NN:SS"
                    labelFunction="data_labelFunc" />
        </mx:columns>
    </mx:DataGrid>

</mx:Application>

View source is enabled in the following example.

As a bonus tip… I believe you could do this with the Date constructor in ActionScript 2.0 as well, but I always find this handy. The following snippet creates a new Date object for January 45th, 2007. ActionScript cleverly adjusts that for you and returns the date of February 14th, 2007.

const JANUARY:uint = 0;
// ...
const DECEMBER:uint = 11;

var d:Date = new Date(2007, JANUARY, 45);
trace(d.toDateString()); // Wed Feb 14 2007

Although DO note that the Date.toDateString() method (as well as uint data type and const keyword) are specific to ActionScript 3.0.

Happy Flexing!


12 Responses to “Date math for lazy people”


  1. 1 peterd Aug 24th, 2007 at 11:55 am

    I think this should work for the various date aliases, bringing my dateAdd() function a bit closer in line with ColdFusion’s (with the exception of support for “q” (quarter) and “ww” (week)):

    private function dateAdd(datepart:String = "", number:Number = 0, date:Date = null):Date {
        if (date == null) {
            /* Default to current date. */
            date = new Date();
        }
    
        var returnDate:Date = new Date(date.time);
    
        switch(datepart.toLocaleLowerCase()) {
            case "yyyy":
            case "year":
                datepart = "fullYear";
                break;
            case "m":
                datepart = "month";
                break;
            case "y":
            case "d":
            case "w":
            case "day":
                datepart = "date";
                break;
            case "h":
                datepart = "hours";
                break;
            case "n":
                datepart = "minutes";
                break;
            case "s":
                datepart = "seconds";
                break;
            case "l":
                datepart = "milliseconds";
                break;
        }
    
        switch (datepart.toLowerCase()) {
            case "fullyear":
            case "month":
            case "date":
            case "hours":
            case "minutes":
            case "seconds":
            case "milliseconds":
                returnDate[datepart]  = number;
                break;
            default:
                /* Unknown date part, do nothing. */
                break;
        }
        return returnDate;
    }
    
  2. 2 Ross MacLachlan Dec 10th, 2007 at 8:17 am

    “fullyear” does not work. It’s actualy “fullYear” with the capital Y.
    To fix this remove the “.toLowerCase()” and pass in correct case. change the
    line-> case “fullyear”:
    to-> case “fullYear”:

    Thanks for the example. I find this site more helpful than the Adobe site for finding the exact tweek you need.

    Keep up the great work.

  3. 3 Prath Dec 12th, 2007 at 4:48 am

    Thanks dude. you made my day.

  4. 4 kpli Dec 27th, 2007 at 8:30 pm

    Thanks for the info bro…i was searching for this for a long time…u’re certainly pro at this…

  5. 5 i0n May 13th, 2008 at 11:55 am

    A optimize version of the code.

    Note: I do not use a ColdFusion Server. Modify it has your require for that.

    public static function dateAdd(datepart:String = "", number:Number = 0, date:Date = null):Date {
    		    date = date == null ? new Date() : new Date(date.time);
    
    		    switch(datepart.toLocaleLowerCase()) {
    
    		    	case "y":
    		    	case "fullyear":
    		        case "yyyy":
    		        case "year":
    		            datepart = "fullYear";
    		            break;
    
    		        case "m":
    		        case "month":
    		            datepart = "month";
    		            break;
    
    		        case "d":
    		        case "day":
    		        case "date":
    		            datepart = "date";
    		            break;
    
    		        case "h":
    		        case "hours":
    		            datepart = "hours";
    		            break;
    
    	            case "minutes":
    		        case "n":
    		            datepart = "minutes";
    		            break;
    
    	            case "seconds":
    		        case "s":
    		            datepart = "seconds";
    		            break;
    
    		        case "milliseconds":
    		        case "l":
    		            datepart = "milliseconds";
    		            break;
    	            default:
    		            datepart = null;
    		    }
    
    		    if(datepart != null)
    		    	date[datepart]  += number;
    		    return date;
    		}
    
  6. 6 Vesta Oct 7th, 2008 at 12:07 pm

    I hate to gush, but this is terrific. Now a part of my utilities from now until forever!

  7. 7 peterd Oct 7th, 2008 at 12:21 pm

    Vesta,
    That’s OK, gush away.

    Peter

  8. 8 Konstantin Shilov Dec 4th, 2008 at 3:34 am

    To add a week support, you can modify your script, and add this lines:

    switch(datepart.toLocaleLowerCase())
    {
        ...
    
        case "week":
            datepart = "date";
            number = number * 7;
            break;
    
        ...
    }
    
  9. 9 Dave Shuck Jan 15th, 2009 at 12:21 pm

    I notice that it gets a little goofy calculating month adds when the current month is longer than the target month. For instance:
    dateAdd(”m”,1,’1/31/2009′) ends up as 3/2/2009

    Any thoughts on a workaround there? I ended up writing my own like this:

    date = new Date(date[”fullYear”]+Math.floor((date[”month”]+number)/12),(date[”month”]+number)%12,date[”date”]);

    However, I am a Flex noob and figured that you might have a more glamorous approach. :)

  10. 10 Dave Shuck Jan 15th, 2009 at 12:23 pm

    crap… I just realized I posted an earlier iteration of that solution. On the one I ended up going with I determined whether or not the max days in month of the target month was less than date[”date”], and if so, I would use that value instead of date[”date”] like you see above.

  11. 11 vinit Feb 13th, 2009 at 12:21 am

    You are awesome…I always find everything here i want in flex….please dont stop this as you are helping many students like me…

  12. 12 Ben Fraley May 8th, 2009 at 8:07 am

    Great function! Thanks so much for posting this!

Leave a Reply

This blog is terrible at eating HTML tags. If you plan on posting code/XML, please escape your "<" characters as "&lt;" and your ">" characters as "&gt;".




Badge Farm

  • Powered by Redoable 1.2
  • Cornify
  • Feeds burnt by Feedburner
  • Feed