You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by sk...@apache.org on 2007/12/12 15:24:06 UTC

svn commit: r603625 - /myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/calendar/resource/date.js

Author: skitching
Date: Wed Dec 12 06:24:05 2007
New Revision: 603625

URL: http://svn.apache.org/viewvc?rev=603625&view=rev
Log:
Add support for "ww" and "xxxx" patterns.

Modified:
    myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/calendar/resource/date.js

Modified: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/calendar/resource/date.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/calendar/resource/date.js?rev=603625&r1=603624&r2=603625&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/calendar/resource/date.js (original)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/calendar/resource/date.js Wed Dec 12 06:24:05 2007
@@ -17,6 +17,135 @@
  * under the License.
  */
 
+//----------------------------------------------------------------------------
+// A javascript implementation of most of the java.text.SimpleDateFormat class,
+// written for the purposes of implementing the Apache MyFaces Tomahawk
+// calendar control.
+//
+// This file defines a javascript class, org_apache_myfaces_SimpleDateFormat.
+// An instance of this class can be constructed, then used to parse strings
+// into dates, and format dates into strings.
+//
+// Note that there is one difference from SimpleDateFormat in the formatting
+// string pattern; this code adopts the JODA "xxxx" pattern for weekYear. If
+// the date 01/01/2010 is output using format "ww/xxxx" then the result is
+// "53/2007", because that date is actually in the last week 2007 is the year
+// in which week01 of 2008 starts. The alternative is to implement the
+// java.text.SimpleDateFormat approach where what "yyyy" displays varies
+// depending on whether "ww" is also present in the pattern. Yecch. That
+// also makes patterns like "xxxx-ww  yyyy-mm-dd" impossible.
+//----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+// Return the week# represented by the specified date (1..53).
+//
+// This implements the ISO-8601 standard for week numbering, as documented in
+// Klaus Tondering's Calendar document, version 2.8:
+//   http://www.tondering.dk/claus/calendar.html
+//
+// For dates in January and February, calculate:
+//
+//    a = year-1
+//    b = a/4 - a/100 + a/400
+//    c = (a-1)/4 - (a-1)/100 + (a-1)/400
+//    s = b-c
+//    e = 0
+//    f = day - 1 + 31*(month-1)
+//
+// For dates in March through December, calculate:
+//
+//    a = year
+//    b = a/4 - a/100 + a/400
+//    c = (a-1)/4 - (a-1)/100 + (a-1)/400
+//    s = b-c
+//    e = s+1
+//    f = day + (153*(month-3)+2)/5 + 58 + s
+//
+// Then, for any month continue thus:
+//
+//    g = (a + b) mod 7
+//    d = (f + g - e) mod 7
+//    n = f + 3 - d
+//
+// We now have three situations:
+//
+//    If n<0, the day lies in week 53-(g-s)/5 of the previous year.
+//    If n>364+s, the day lies in week 1 of the coming year.
+//    Otherwise, the day lies in week n/7 + 1 of the current year.
+//
+// This algorithm gives you a couple of additional useful values:
+//
+//    d indicates the day of the week (0=Monday, 1=Tuesday, etc.)
+//    f+1 is the ordinal number of the date within the current year.
+//
+// Note that ISO-8601 specifies that week1 of a year is the first week in
+// which the majority of days lie in that year. An equivalent description
+// is that it is the first week including the 4th of january. This means
+// that the 1st, 2nd and 3rd of January might lie in the last week of the
+// previous year, and that the last week of a year may include the first
+// few days of the following year.
+//
+// ISO-8601 also specifies that the first day of the week is always Monday.
+//
+// This function returns the week number regardless of which year it lies in.
+// That means that asking for the week# of 01/01/yyyy might return 52 or 53,
+// and asking for the week# of 31/12/yyyy might return 1.
+//----------------------------------------------------------------------------
+function org_apache_myfaces_SimpleDateFormat_weekNbr(n)
+{
+    var year = n.getFullYear();
+    var month = n.getMonth() + 1;
+    var day = n.getDate();
+
+    var a,b,c,d,e,f,g;
+
+    if (month <= 2)
+    {
+        a = year - 1;
+        b = Math.floor(a/4) - Math.floor(a/100) + Math.floor(a/400);
+        c = Math.floor((a-1)/4) - Math.floor((a-1)/100) + Math.floor((a-1)/400);
+        s = b - c;
+        e = 0;
+        f = day - 1 + 31*(month-1);
+    }
+    else
+    {
+        a = year;
+        b = Math.floor(a/4) - Math.floor(a/100) + Math.floor(a/400);
+        c = Math.floor((a-1)/4) - Math.floor((a-1)/100) + Math.floor((a-1)/400);
+        s = b - c;
+        e = s + 1;
+        f = day + Math.floor((153*(month-3) + 2)/5) + 58 + s;       
+    }
+
+    g = (a + b) % 7;
+    d = (f + g - e) % 7;
+    n = f + 3 - d;
+
+    var week;
+    if (n<0)
+    {
+        // previous year
+        week = 53 - Math.floor((g-s)/5);
+    }
+    else if (n > (364+s))
+    {
+        // next year
+        week = 1;
+    }
+    else
+    {
+        // current year
+        week = Math.floor(n/7) + 1;
+    }
+    
+    return week;
+}
+
+//----------------------------------------------------------------------------
+// Constructor for a simple object that contains locale-specific constants
+// used for date parsing and formatting.
+//----------------------------------------------------------------------------
 org_apache_myfaces_DateFormatSymbols = function()
 {
         this.eras = new Array('BC', 'AD');
@@ -37,6 +166,11 @@
         this.twoDigitYearStart = threshold;
 }
 
+//----------------------------------------------------------------------------
+// Constructor for a simple object that contains the current parsing or
+// formatting state. This encapsulates the properties of a SimpleDateFormat
+// which are modified during a parse or format call.
+//----------------------------------------------------------------------------
 org_apache_myfaces_SimpleDateFormatParserContext = function()
 {
         this.newIndex=0;
@@ -51,14 +185,30 @@
         this.sec=0;
         this.ampm=0;
         this.dateStr="";
+
+        this.weekYear=0;
+        this.weekOfWeekYear=0;
 }
 
+//----------------------------------------------------------------------------
+// Constructor method.
+//
+// param pattern defines the pattern to be used for parsing or formatting.
+//
+// param dateSymbols is optional. It defines the "locale"; if not defined
+// then default settings (english locale) are used.
+//----------------------------------------------------------------------------
 org_apache_myfaces_SimpleDateFormat = function(pattern, dateFormatSymbols)
 {
         this.pattern = pattern;
         this.dateFormatSymbols = dateFormatSymbols ? dateFormatSymbols :
                 new org_apache_myfaces_DateFormatSymbols();
 }
+
+//----------------------------------------------------------------------------
+// Handle both parsing and formatting of dates, by walking the pattern and
+// invoking _handlePatternSub for each "segment" of the pattern.
+//----------------------------------------------------------------------------
 org_apache_myfaces_SimpleDateFormat.prototype._handle = function(dateStr, date, parse)
     {
         var patternIndex = 0;
@@ -80,8 +230,19 @@
             context.hour = date.getHours();
             context.min = date.getMinutes();
             context.sec = date.getSeconds();
+
+            context.weekOfWeekYear = this._weekNbr(date);
+            if ((context.weekOfWeekYear > 50) && (context.month==0))
+                context.weekYear = context.year - 1;
+            else if ((context.weekOfWeekYear == 1) && (context.month==12))
+                context.weekYear = context.year + 1;
+            else
+                context.weekYear = context.year;
         }
 
+        // Walk through the date pattern char by char. Gather together runs of identical
+        // chars, and when the char changes then invoke _handlePatternSub passing the
+        // current run of chars.
         while (patternIndex < this.pattern.length)
         {
             currentChar = this.pattern.charAt(patternIndex);
@@ -132,10 +293,12 @@
                         patternIndex++;
                     }
                 }
-                else
+                else // we are processing an escaped character
                 {
                     if(parse)
                     {
+                        // input string being parsed must exactly match the corresponding char
+                        // in the pattern.
                         if(this.pattern.charAt(patternIndex)!=dateStr.charAt(dateIndex))
                         {
                             //invalid character in dateString
@@ -144,6 +307,7 @@
                     }
                     else
                     {
+                        // just output the escaped char literally into the result
                         context.dateStr+=this.pattern.charAt(patternIndex);
                     }
 
@@ -161,6 +325,10 @@
         return context;
     };
 
+//----------------------------------------------------------------------------
+// Parse a string using the configured pattern, and return a normal javascript
+// Date object.
+//----------------------------------------------------------------------------
 org_apache_myfaces_SimpleDateFormat.prototype.parse = function(dateStr)
     {
         if(!dateStr || dateStr.length==0)
@@ -175,11 +343,64 @@
 
         return this._createDateFromContext(context);
     };
+
+//----------------------------------------------------------------------------
 org_apache_myfaces_SimpleDateFormat.prototype._createDateFromContext=function(context)
     {
-        return new Date(context.year, context.month,
-                context.day,context.hour,context.min,context.sec);
+        if (context.weekOfWeekYear != 0)
+        {
+            var MSECS_PER_DAY = 24*60*60*1000;
+
+            var date = new Date(
+                context.weekYear, 0, 1,
+                context.hour,context.min,context.sec);
+
+            // Nudge date to the nearest Monday from the start of the year;
+            // weeks always start on a monday. Note that it might be in the
+            // previous year. The actual date for the start of the first week
+            // in the year is guaranteed to be in the range (29dec-4jan)
+            var dow = date.getDay();
+            var daysOffset;
+            if (dow == 1)
+            {
+              // first day of year is monday, so no offset needed.
+              daysOffset = 0;
+            }
+            else if (dow <= 4)
+            {
+              // first day-of-year is tue, wed, thurs so the nearest monday is
+              // earlier (in the previous year). The offset will be negative.
+              daysOffset = 1 - dow;
+            }
+            else
+            {
+              // first day-of-year is fri,sat,sun so the nearest monday
+              // is later..
+              daysOffset = 8 - dow;
+            }
+           
+            // now add week*7 days
+            daysOffset += (context.weekOfWeekYear - 1) * 7;
+
+            // do arithmetic
+            var msecsBase = date.getTime();
+            var msecsOffset = daysOffset * MSECS_PER_DAY;
+
+            var finalDate = new Date();
+            finalDate.setTime(msecsBase + msecsOffset);
+            return finalDate;
+        }
+        else
+        {
+            return new Date(
+                context.year, context.month, context.day,
+                context.hour,context.min,context.sec);
+        }
     };
+
+//----------------------------------------------------------------------------
+// Accept a normal javascript Date object, and return a String.
+//----------------------------------------------------------------------------
 org_apache_myfaces_SimpleDateFormat.prototype.format = function(date)
     {
         var context = this._handle(null, date, false);
@@ -187,6 +408,10 @@
         return context.dateStr;
     };
 
+//----------------------------------------------------------------------------
+// dateStr is the full string currently being parsed.
+// dateIndex is the offset within dateStr to parse from.
+//----------------------------------------------------------------------------
 org_apache_myfaces_SimpleDateFormat.prototype._parseString = function(context, dateStr, dateIndex, strings)
     {
         var fragment = dateStr.substr(dateIndex);
@@ -202,8 +427,21 @@
         return context;
     };
 
+//----------------------------------------------------------------------------
+// Convert at most the next posCount characters to numeric (or stop at
+// end-of-string), starting from offset dateIndex within dateStr.
+//
+// dateStr is the full string currently being parsed.
+// dateIndex is the offset within dateStr to parse from.
+//
+// Stores the result in context.retValue
+// Updates context.newIndex to contain the next unparsed char within dateStr
+//----------------------------------------------------------------------------
 org_apache_myfaces_SimpleDateFormat.prototype._parseNum = function(context, dateStr, posCount, dateIndex)
     {
+        // Try to convert the most possible characters (posCount). If that fails,
+        // then try again without the last character. Repeat until successful
+        // numeric conversion occurs.
         for(var i=Math.min(posCount,dateStr.length-dateIndex);i>0;i--)
         {
             var numStr = dateStr.substring(dateIndex,dateIndex+i);
@@ -222,6 +460,12 @@
         return context;
     };
 
+//----------------------------------------------------------------------------
+// Handles a "segment" of a pattern, for either parsing or formatting.
+//
+// patternSub contains a sequence of identical chars, eg "yyyy" or "HH".
+// returns VOID
+//----------------------------------------------------------------------------
 org_apache_myfaces_SimpleDateFormat.prototype._handlePatternSub = function(context, patternSub, dateStr, dateIndex, parse)
     {
         if(patternSub==null || patternSub.length==0)
@@ -231,25 +475,6 @@
         {
             if(parse)
             {
-                /* XXX @Arvid: whatever we do, we need to try to parse
-                    the full year format - length means nothing for
-                    parsing, only for formatting, so says SimpleDateFormat javadoc.
-                    only if we run into problems as there are no separator chars, we
-                    should use exact length parsing - how are we going to handle this?
-
-                    Additionally, the threshold was not quite correct - it needs to
-                    be set to current date - 80years...
-
-                    this is done after parsing now!
-
-                if (patternSub.length <= 3) {
-                  this._parseNum(context, dateStr,2,dateIndex);
-                  context.year = (context.retValue < 26)
-                      ? 2000 + context.retValue : 1900 + context.retValue;
-                } else {
-                  this._parseNum(context, dateStr,4,dateIndex);
-                  context.year = context.retValue;
-                }*/
                 this._parseNum(context, dateStr,4,dateIndex);
 
                 if((context.newIndex-dateIndex)<4)
@@ -260,7 +485,6 @@
                 else
                 {
                     context.year = context.retValue;
-
                 }
             }
             else
@@ -268,6 +492,27 @@
                 this._formatNum(context,context.year,patternSub.length <= 3 ? 2 : 4,true);
             }
         }
+        else if(patternSub.charAt(0)=='x')
+        {
+            if(parse)
+            {
+                this._parseNum(context, dateStr,4,dateIndex);
+
+                if((context.newIndex-dateIndex)<4)
+                {
+                    context.weekYear = context.retValue+1900;
+                    context.ambigousYear = true;
+                }
+                else
+                {
+                    context.weekYear = context.retValue;
+                }
+            }
+            else
+            {
+                this._formatNum(context,context.weekYear,patternSub.length <= 3 ? 2 : 4,true);
+            }
+        }
         else if(patternSub.charAt(0)=='M')
         {
             if(parse)
@@ -393,6 +638,18 @@
                 context.dateStr += this.dateFormatSymbols.ampms[context.ampm];
             }
         }
+        else if(patternSub.charAt(0)=='w')
+        {
+            if(parse)
+            {
+                this._parseNum(context, dateStr,2,dateIndex);
+                context.weekOfWeekYear = context.retValue;
+            }
+            else
+            {
+                this._formatNum(context,context.weekOfWeekYear,patternSub.length);
+            }
+        }
         else
         {
             if(parse)
@@ -407,6 +664,12 @@
         }
     };
 
+//----------------------------------------------------------------------------
+// Write out an integer padded with leading zeros to a specified width
+// If ensureLength is set, and the number is longer than length, then display only the rightmost length digits.
+//
+// modifies member variable context.dateStr, returns VOID
+//----------------------------------------------------------------------------
 org_apache_myfaces_SimpleDateFormat.prototype._formatNum = function (context, num, length, ensureLength)
     {
         var str = num+"";
@@ -473,6 +736,7 @@
 
         return year;
     };
+
 org_apache_myfaces_SimpleDateFormat.prototype._adjustTwoDigitYear = function(context)
     {
 
@@ -486,3 +750,7 @@
         }
     };
 
+//----------------------------------------------------------------------------
+// Make static method callable via short name
+//----------------------------------------------------------------------------
+org_apache_myfaces_SimpleDateFormat.prototype._weekNbr=org_apache_myfaces_SimpleDateFormat_weekNbr;