Javascript Date Function

Experts,

Does anyone have a javascript date function that will handle ALL the following scenarios:

1. mm/dd/yy
2. mm/dd/yyyy
3. mmddyy
4. mmddyyyy
LVL 2
michael1174Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

erikTsomikSystem Architect, CF programmer Commented:
can you provide more details . Do you want to validate the date on all this date formats
0
erikTsomikSystem Architect, CF programmer Commented:
0
michael1174Author Commented:
yes, the date should be a valid date.

The following is the code I have now, but it only accepts mm/dd/yyyy.

I also have this on my onblur event of the date textbox:

onblur="if (this.value=='') {} else if (!isDate(this.value)) { this.focus(); }"

function isDate(dtStr){
	var daysInMonth = DaysArray(12)
	var pos1=dtStr.indexOf(dtCh)
	var pos2=dtStr.indexOf(dtCh,pos1+1)
	var strMonth=dtStr.substring(0,pos1)
	var strDay=dtStr.substring(pos1+1,pos2)
	var strYear=dtStr.substring(pos2+1)
	strYr=strYear
	if (strDay.charAt(0)=="0" && strDay.length>1) strDay=strDay.substring(1)
	if (strMonth.charAt(0)=="0" && strMonth.length>1) strMonth=strMonth.substring(1)
	for (var i = 1; i <= 3; i++) {
		if (strYr.charAt(0)=="0" && strYr.length>1) strYr=strYr.substring(1)
	}
	month=parseInt(strMonth)
	day=parseInt(strDay)
	year=parseInt(strYr)
	if (pos1==-1 || pos2==-1){
		alert("The date format should be : mm/dd/yyyy")
		return false
	}
	if (strMonth.length<1 || month<1 || month>12){
		alert("Please enter a valid month")
		return false
	}
	if (strDay.length<1 || day<1 || day>31 || (month==2 &&
day>daysInFebruary(year)) || day > daysInMonth[month]){
		alert("Please enter a valid day")
		return false
	}
	if (strYear.length != 4 || year==0 || year<minYear || year>maxYear){
		alert("Please enter a valid 4 digit year between "+minYear+" and "+maxYear)
		return false
	}
	if (dtStr.indexOf(dtCh,pos2+1)!=-1 ||
isInteger(stripCharsInBag(dtStr, dtCh))==false){
		alert("Please enter a valid date")
		return false
	}
return true
}
 
function daysInFebruary (year){
	// February has 29 days in any year evenly divisible by four,
    // EXCEPT for centurial years which are not also divisible by 400.
    return (((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400
== 0))) ? 29 : 28 );
}
function DaysArray(n) {
	for (var i = 1; i <= n; i++) {
		this[i] = 31
		if (i==4 || i==6 || i==9 || i==11) {this[i] = 30}
		if (i==2) {this[i] = 29}
   } 
   return this
}
 
function stripCharsInBag(s, bag){
	var i;
    var returnString = "";
    // Search through string's characters one by one.
    // If character is not in bag, append to returnString.
    for (i = 0; i < s.length; i++){   
        var c = s.charAt(i);
        if (bag.indexOf(c) == -1) returnString += c;
    }
    return returnString;
}

Open in new window

0
The Ultimate Tool Kit for Technolgy Solution Provi

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy for valuable how-to assets including sample agreements, checklists, flowcharts, and more!

contactkarthiCommented:
have a look here

they have script for validating different formats

http://javascript.about.com/library/bldate.htm
0
b0lsc0ttIT ManagerCommented:
The script below should do this for you.  Let me know if you have a question or notice a problem.  It checks the format and will even validate the date itself.

bol
<script type="text/javascript">
function isDate(dtStr) {
	if (dtStr.match(/\//)) {
		if (!dtStr.match(/(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/(20)?\d{2}/)) {
			alert("Invalid date format. dd/mm/yyyy");
			return false;
		}
		var dtVals = dtStr.split("/");
	} else {
		if (!dtStr.match(/(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])(20)?\d{2}/)) {
			alert("Invalid date format. ddmmyyyy");
			return false;
		}
		var dtVals = new Array();
		dtVals[0] = dtStr.substr(0,2);
		dtVals[1] = dtStr.substr(2,2);
		dtVals[2] = dtStr.substr(4);
	}
	return isGoodDate(dtVals[1],dtVals[0],dtVals[2]);
}
function isGoodDate(day,month,yr) {
	if (!yr.toString().match(/^(19|20)?[0-9]{2}$/)) {
		alert('Invalid year');
		return false;
	}
	var dte = new Date();
	month = month - 1;
	dte.setFullYear(yr);
	dte.setMonth(month);
	dte.setDate(day);
	return ((dte.getDate() != day || dte.getMonth() != month || dte.getFullYear() != yr)? false : true);
}
</script>

Open in new window

0
HonorGodSoftware EngineerCommented:
How about this:


---------- FormatDate.js ----------
  //------------------------------------------------------------------
  // Name: D2()
  // Role: Prepend a digit (if necessary) to return a two digit number
  // Note: Extremely simple test, that can return garbage if the input
  //       value is not a non-degative number.
  //------------------------------------------------------------------
  function D2( val ) {
    return ( val < 10 ) ? '0' + val : val;
  }

  //------------------------------------------------------------------
  // Name: D3()
  // Role: Prepend necessary digits to return a three digit number
  // Note: Extremely simple test, that can return garbage if the input
  //       value is not a non-degative number.
  //------------------------------------------------------------------
  function D3( val ) {
    var result = '' + D2( val );
    return ( result.length < 3 ) ? '0' + result : result;
  }

  //------------------------------------------------------------------
  // Name: LongDay()
  // Role: Given a 3 letter/character day value (e.g., "Thu"), return
  //       the equivalent long (full) day name (i.e., "Thursday")
  //------------------------------------------------------------------
  var Days = ( 'Sunday,Monday,Tuesday,Wednesday,' +
               'Thursday,Friday,Saturday' ).split( ',' );
  function LongDay( Day ) {
    for ( var d = 0; d < Days.length; d++ ) {
      if ( Day == Days[ d ].substr( 0, 3 ) ) {
        return Days[ d ];
      }
    }
    return '';
  }
 
  //------------------------------------------------------------------
  // Name: LongMonth()
  // Role: Given a 3 letter/character month value (e.g., "Oct"), return
  //       the equivalent long (full) month name (i.e., "October")
  //------------------------------------------------------------------
  var Months = ( 'January,February,March,April,May,June,July,August,' +
                 'September,October,November,December' ).split( ',' );
  function LongMonth( Mon ) {
    for ( var m = 0; m < Months.length; m++ ) {
      if ( Mon == Months[ m ].substr( 0, 3 ) ) {
        return Months[ m ];
      }
    }
    return '';
  }
 
  //------------------------------------------------------------------
  // Name: Miltary()
  // Role: Return a Military time given a Date object
  //------------------------------------------------------------------
  function Military( date ) {
    return D2( date.getHours() ) + ':' + D2( date.getMinutes() );
  }
 
  //------------------------------------------------------------------
  // Name: getDOY()
  // Role: Provide Day Of Year function for Date object
  // Note: 24 * 60 * 60 * 1000 == # milliseconds in a day (86,400,000)
  //------------------------------------------------------------------
  Date.prototype.getDOY = function() {
    var Jan1st = new Date( this.getFullYear(), 0, 1 );
    return Math.ceil( ( this - Jan1st ) / 86400000 ) + 1;
  }
 
  //------------------------------------------------------------------
  // Name: FormatDateTime
  // Role: Format the specified value using the request pattern
  //
  // Note:
  //   FormatInfo| Comment / Description           | e.g.,
  //   ----------+---------------------------------+-------
  //   MMMM      | Long  Month Name                | October
  //   MMM       | Short Month Name                | Oct
  //   MM        | Two Digit Month Number          | 01..12
  //   M         | One or Two Digit Month Number   | 1..12
  //   DDDD      | Long  Day   Name                | Thursday
  //   DDD       | Short Day   Name                | Thu
  //   DD        | Two  Digit Day Number           | 01..31
  //   D         | One or Two Digit Day Numer      | 1..31
  //   dd        | Two  Digit Day Of Week          | 00..06
  //   d         | One  Digit Day Of Week          | 0..6
  //   YY        | Two  Digit Year                 | 00..99
  //   YYYY      | Four Digit Year                 | 2008
  //   yyy       | Three Digit Day Of Year Number  | 001..366
  //   yy        | Two..Three Digit Day Of Year #  | 01..366
  //   y         | One..Three Digit Day Of Year #  | 1..366
  //   ----------+---------------------------------+-------
  //   :         | Time delimiter                  |
  //   - or /    | Date field delimiter            |
  //   ,         | Text delimiter                  |
  //   ----------+---------------------------------+-------
  //   HH        | Military Hours                  | 00..23
  //   hh        | Two Digit "Regular" Hours       | 01..12
  //   h         | One or Two Digit Regular Hours  | 1..12
  //   mm        | Two Digit Minutes               | 00..59
  //   m         | One or Two Digit Minutes        | 0..59
  //   ss        | Two Digit Seconds               | 00.59
  //   s         | One or two Digit Seconds        | 0..59
  //   ----------+---------------------------------+-------
  //   PM        | AM or PM                        |
  //   pm        | am or pm                        |
  //   ----------+---------------------------------+-------
  //   1         | General   == DDDD, MMMM D, YYYY | Thursday, January 29, 2008
  //   2         | Standard  == MM/DD/YY           | 01/29/08
  //   3         | Timestamp == hh:mm PM           | 01:01 PM
  //   4         | Military  == HH:MM              | 00:00 .. 23:59
  //   5         | Hyphens   == DD-MMM-YY          | 29-Jan-08
  //   6         | Sortable  == YY/MM/YY           | 08/01/29
  //
  // Examples:
  //   FomatStr       | Sample Result
  //   ---------------+-----------------------------------
  //   MMMM DD, YYYY  | October 07, 1954
  //   MM-DD-YY       | 10-07-54
  //   MM/DD/YYYY     | 10/07/1954
  //   DDD MM/DD/YYYY | Mon 01/02/2003
  //   hh:mm pm       | 01:23 pm
  //   h:m PM         | 1:1 AM  (Why you would do this, I don't know)
  //  
  //------------------------------------------------------------------
  function FormatDateTime( datetime, FormatStr ) {

    //----------------------------------------------------------------
    // Expect the user to supply a legal and valid input date.  Which
    // can be a Date object, in which case, we simply use it.  If not,
    // we try to create a Date object using the specified value.  If
    // an error occurs, use current system clock instead.
    //----------------------------------------------------------------
    if ( datetime instanceof Date ) {
      var myDate = datetime;
    } else {
      myDate = new Date( datetime );
      if ( ( myDate == 'Invalid Date' ) || ( myDate == 'NaN' ) ) {
        myDate = new Date();
      }
    }

    //----------------------------------------------------------------
    // Determine the Day of Week and Month of Year Name values (both
    // short, and long form) from the Date object
    //   LMname = Long  Month name (e.g., October)
    //   SMname = Short Month name (e.g., Oct)
    //   LDname = Long  Day   name (e.g., Thursday)
    //   SDname = Short Day   name (e.g., Thu)
    //----------------------------------------------------------------
    var MonNum  = myDate.getMonth() + 1;
    var LMname  = Months[ MonNum - 1 ];
    var SMname  = LMname.substr( 0, 3 );
    var DayNum  = myDate.getDay();
    var DateNum = myDate.getDate();
    var LDname  = Days[ DayNum ];
    var SDname  = LDname.substr( 0, 3 );
    var Fyear   = myDate.getFullYear();
    var Syear   = Fyear % 100;
    var MilHrs  = myDate.getHours();
    var Hours   = ( MilHrs > 12 ) ? MilHrs - 12 : ( MilHrs == 0 ) ? 12 : MilHrs;
    var Minutes = myDate.getMinutes();
    var Seconds = myDate.getSeconds();
    var ampm    = ( MilHrs > 11 ) ? 'pm' : 'am';

    //----------------------------------------------------------------
    // Information formatted using supported patterns
    //----------------------------------------------------------------
    var Data   = { 'MMMM' : LMname,                // Long Month Name
                   'MMM'  : SMname,                // Short Month Name
                   'MM'   : D2( MonNum ),          // Two digit Month # (01..12)
                   'M'    : MonNum,                // One or Two digit Month # (1..12)
                   'DDDD' : LDname,                // Long  Day Name
                   'DDD'  : SDname,                // Short Day Name
                   'DD'   : D2( DateNum ),         // Two Digit Day # (01..31)
                   'D'    : DateNum,               // One or Two Digit Day # (1..31)
                   'dd'   : D2( DayNum ),          // Two Digit Day of Week (01..06)
                   'd'    : DayNum,                // One Digit Day of Week (1..6)
                   'YYYY' : Fyear,                 // Four digit year (full - e.g., 2008)
                   'YY'   : D2( Syear ),           // Two digit year (short - e.g., 08)
                   'HH'   : D2( MilHrs ),          // Military Hours (00..23)
                   'hh'   : D2( Hours ),           // Two digit hours (01..12)
                   'h'    : Hours,                 // One or Two digit hours (1..12)
                   'mm'   : D2( Minutes ),         // Two digit minutes (00..59)
                   'm'    : Minutes,               // One or Two digit minutes (0..59)
                   'ss'   : D2( Seconds ),         // Two digit seconds (00.59)
                   's'    : Seconds,               // One or Two digit seconds (0..59)
                   'yyy'  : D3( myDate.getDOY() ), // Three digit Day of Year (001..366)
                   'yy'   : D2( myDate.getDOY() ), // Two+ digit Day of Year (01..366)
                   'y'    : myDate.getDOY(),       // Day of Year (1..366)
                   'PM'   : ampm.toUpperCase(),    // 'AM' or 'PM'
                   'pm'   : ampm,                  // 'am' or 'pm'
                   '1'    : LDname + ', ' + LMname + ' ' + DateNum + ', ' + Fyear,
                   '2'    : D2( MonNum ) + '/' + D2( DateNum ) + '/' + D2( Syear ),
                   '3'    : D2( Hours ) + ':' + D2( Minutes ) + ' ' + ampm.toUpperCase(),
                   '4'    : Military( myDate ),
                   '5'    : D2( DateNum ) + '-' + SMname + '-' + D2( Syear ),
                   '6'    : D2( Syear ) + '/' + D2( MonNum ) + '/' + D2( DateNum )
    };
   
    //----------------------------------------------------------------
    // Supported Data elements
    // - pat == all patterns, including delimiter characters
    // - del == delimiter characters
    //----------------------------------------------------------------
    var pat = /([DMY]{1,4}|HH|[hs]{1,2}|PM|[- :,\/]|[1-6])/i;
    var del = /^[- :,\/]$/;

    //----------------------------------------------------------------
    // Process the FormatString using a RegExp to extract recognized
    // patterns, and using these patterns to generate the requested
    // output sequence.
    //----------------------------------------------------------------
    // token = next pattern from FormatStr
    // while ( token ) {
    //   Use token to append corresponding value to result string
    //   token = next pattern from FormatStr
    // }
    //----------------------------------------------------------------
    var result = '';
    var tok = FormatStr.match( pat );
    while ( tok ) {
      if ( tok[ 0 ].search( del ) == 0 ) {
        result += tok[ 0 ];
      } else {
        if ( tok[ 0 ] in Data ) {
          result += Data[ tok[ 0 ] ];
        }
      }
      FormatStr = FormatStr.replace( tok[ 0 ], '' );
      tok = FormatStr.match( pat );
    }
   
    return result;
  }
----------------------------------------------------------------------

------------------------------ FormatTest.html ------------------------------
<!doctype html public "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title> FormatDateTime Testing </title>

<script type='text/javascript' src='../objDisplay.js'></script>
<script type='text/javascript' src='FormatDate.js'></script>
<script type='text/javascript'>
  function field( id ) {
    var obj = document.getElementById( id );
    if ( !obj ) {
      alert( 'Specified element not found.  id = "' + id + '"' );
    }
    return obj;
  }

  function test( id1, id2 ) {
    var here  = field( id1 );
    var there = field( id2 );
    var where = field( 'result' );
    if ( here && there ) {
//    alert( 'Date: "' + there.value + '"' );
//    alert( FormatDateTime( there.value, here.value ) );
      where.value = FormatDateTime( there.value, here.value );
    }
  }
</script>

</head>
<body>

<pre>
   Formats   | Comment / Description           | e.g.,
   ----------+---------------------------------+-------
   MMMM      | Long  Month Name                | October
   MMM       | Short Month Name                | Oct
   MM        | Two Digit Month Number          | 01..12
   M         | One or Two Digit Month Number   | 1..12
   DDDD      | Long  Day   Name                | Thursday
   DDD       | Short Day   Name                | Thu
   DD        | Two  Digit Day Number           | 01..31
   D         | One or Two Digit Day Numer      | 1..31
   dd        | Two  Digit Day of Week          | 01..06
   d         | One  Digit Day of Week          | 1..6
   
   Capital Y | Comment / Description           | e.g.,
   ----------+---------------------------------+-------
   YYYY      | Four Digit Year                 | 2008
   YY        | Two  Digit Year                 | 00..99
   ----------+---------------------------------+-------
   
   Lowercase | Comment / Description           | e.g.,
   ----------+---------------------------------+-------
   yyy       | Three Digit Day of Year         | 001..366
   yy        | Two  Digit Day of Year          | 01..366
   y         | One  Digit Day of Year          | 1..366
   
   Delimiter | Comment / Description           | e.g.,
   ----------+---------------------------------+-------
   :         | Time delimiter                  |
   - or /    | Date field delimiter            |
   , ' '     | Text delimiter (comma or space) |
   
   Time      | Comment / Description           | e.g.,
   ----------+---------------------------------+-------
   HH        | Military Hours                  | 00..23
   hh        | Two Digit "Regular" Hours       | 01..12
   h         | One or Two Digit Regular Hours  | 1..12
   mm        | Two Digit Minutes               | 00..59
   m         | One or Two Digit Minutes        | 0..59
   ss        | Two Digit Seconds               | 00.59
   s         | One or two Digit Seconds        | 0..59
   PM        | AM or PM                        |
   pm        | am or pm                        |
   
   Common    | Comment / Description           | e.g.,
   ----------+---------------------------------+-------
   1         | General    : DDDD, MMMM D, YYYY | Thursday, January 29, 2008
   2         | Standard   : MM/DD/YY           | 01/29/08
   3         | Timestamp  : hh:mm PM           | 01:01 PM
   4         | Military   : HH:MM              | 00:00 .. 23:59
   5         | Hyphenated : DD-MMM-YY          | 29-Jan-08
   6         | Sortable   : YY/MM/YY           | 08/01/29

 Examples:
   FomatStr          | Sample Result
   ------------------+-----------------------------------
   MMMM DD, YYYY     | October 07, 1954
   MM-DD-YY          | 10-07-54
   MM/DD/YYYY        | 10/07/1954
   DDD MM/DD/YYYY    | Mon 01/02/2003
   hh:mm pm          | 01:23 pm
   h:m PM            | 1:1 AM  (Why you would do this, I don't know)
   6 3               | 08/01/29 09:51 PM
   DDD 6 hh:mm:ss pm | Thu 54/10/07 12:34:56 pm

</pre>

<table border='1'>
  <tr>
    <td>Format String</td>
        <td>
      <input type='text' id='format' size='40' onChange='test("format","date");'>
    </td>
  </tr>
  <tr>
    <td>Date String</td>
        <td>
      <input type='text' id='date' size='40' onChange='test("format","date");'>
    </td>
  </tr>
  <tr>
    <td>Result</td>
        <td>
      <input type='text' id='result' size='40' readonly>
    </td>
  </tr>
</table>
<div id='debug'></div>
</body>
</html>
----------------------------------------------------------------------
0
HonorGodSoftware EngineerCommented:
Sorry, I missed your update... you want it validated... (oops)

bol's is good

0
michael1174Author Commented:
b0lsc0tt, thanks.  how would i go about adding the slashes "/" if the date entered doesn't contain the slashes? I basically want to reformat the date to always contain slashes...

so if 020308 is entered then I want it to reformat it to 02/03/08
if 02012008 is entered, then 02/01/2008


0
b0lsc0ttIT ManagerCommented:
Your welcome!  There are a few ways to do that but I modified the validate function a bit to include it (see snippet).  Let me know if you have a question.

bol
function isDate(dtStr) {
	if (dtStr.match(/\//)) {
		if (!dtStr.match(/(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/(20)?\d{2}/)) {
			alert("Invalid date format. dd/mm/yyyy");
			return false;
		}
		var dtVals = dtStr.split("/");
	} else {
		if (!dtStr.match(/(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])(20)?\d{2}/)) {
			alert("Invalid date format. ddmmyyyy");
			return false;
		}
		var dtVals = new Array();
		dtVals[0] = dtStr.substr(0,2);
		dtVals[1] = dtStr.substr(2,2);
		dtVals[2] = dtStr.substr(4);
		dtStr = dtVals[0] + "/" + dtVals[1] + "/" + dtVals[2];
	}
	return (isGoodDate(dtVals[1],dtVals[0],dtVals[2]))? dtStr : '';
}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
michael1174Author Commented:
b0lsc0tt, thanks again.  I replaced the IsDate function, but didn't see the date get reformatted with the slashes.  I am calling IsDate on the onblur event... would that be the correct event to call?


onblur="if (this.value=='') {} else if (!isDate(this.value)) { this.focus(); }"

Open in new window

0
b0lsc0ttIT ManagerCommented:
Yes, but you need to let the function return the reformatted date.  Use the following for your onblur:

onblur="if (this.value != '') this.value = isDate(this.value);"

I don't really know what the focus is for at the end so I left it out.  Let me know what it does and when if you need it.  The onblur will run the function if the field has a value and return the result, which will be formatted or empty (if not valid).

Let me know how it works or if you have a question.

bol
0
michael1174Author Commented:
b0lsc0tt, thanks.  I just needed to keep the textbox focus if the date was invalid so the user would be force to enter a valid date.  I modified the onblur event, and I think I got it working.

onblur="if (this.value=='') {} else if (!isDate(this.value)) { this.focus(); } else {this.value = isDate(this.value);}"

One other thing I need to do now.... if they enter in 010101 or 01/01/01... i need to format it to the 4 digit year... 01/01/2001..... if they entered in 010198..it would need to be 01/01/1998.
0
b0lsc0ttIT ManagerCommented:
How about opening a new question for that.  It is a new issue and actually the second "modification" from the original question.  Feel free to post the URL of the new question here and I'll be happy to look at it.  It shouldn't be hard to add that to the code.

I would suggest that you decide either what year to use as a cutoff or to always assume a 2 digit year is 2000+.  In other words to do what you want we need a year to use as a break, for example if greater than 50 then 1900 otherwise 2000.  I think it better to just assume 2000 but that may depend on the type of date being entered.  A 3rd option but more complicated for the script is to treat any 2 digit year greater than the 2 digit year for this year as 1900.

In the new question you can post the code you have and let us know which method you prefer to use with 2 digit years.  Let me know if you have a question.

bol
0
michael1174Author Commented:
my new question about formatting the year to be always a 4 digit year:


http:Q_23145576.html

** edited link to fix it - b0lsc0tt **
0
b0lsc0ttIT ManagerCommented:
I'll take a look at it.  Thanks for the grade, the points and the fun question.  I'm glad I could help.

bol
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
JavaScript

From novice to tech pro — start learning today.