Bug 1151782 - Inputting 29th Feb as a birthday in the addressbook contact replaces it with 1st Mar. r=mkmelin,iann a=SM always CLOSED TREE
authorChristian Hoffmann <christian>
Mon, 20 Apr 2015 23:48:00 +0200
changeset 22541 6d44313309e36962afd447b9f3a47b6b1b02424b
parent 22540 c0c837751ccdd85bbe67e7220186da3f25e950f6
child 22542 c18d41881e40c7917814078562bf7d828c4a4d93
push id1420
push usermbanner@mozilla.com
push dateMon, 29 Jun 2015 20:47:24 +0000
treeherdercomm-beta@b3db00bb24e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin, iann, SM
bugs1151782
Bug 1151782 - Inputting 29th Feb as a birthday in the addressbook contact replaces it with 1st Mar. r=mkmelin,iann a=SM always CLOSED TREE
mail/components/addrbook/content/abCardOverlay.js
mail/components/addrbook/content/abCardViewOverlay.js
mail/components/addrbook/content/abCommon.js
suite/mailnews/addrbook/abCardOverlay.js
suite/mailnews/addrbook/abCardViewOverlay.js
suite/mailnews/addrbook/abCommon.js
--- a/mail/components/addrbook/content/abCardOverlay.js
+++ b/mail/components/addrbook/content/abCardOverlay.js
@@ -63,17 +63,16 @@ const kVcardFields =
          ["Skype", "_Skype"],
          ["QQ", "_QQ"],
          ["MSN", "_MSN"],
          ["ICQ", "_ICQ"],
          ["XMPP", "_JabberId"],
          ["IRC", "_IRC"]
         ];
 
-const kDefaultYear = 2000;
 var gEditCard;
 var gOnSaveListeners = new Array();
 var gOnLoadListeners = new Array();
 var gOkCallback = null;
 var gHideABPicker = false;
 var gPhotoHandlers = {};
 
 function OnLoadNewCard()
@@ -454,38 +453,38 @@ function GetCardValues(cardproperty, doc
   for (var i = kVcardFields.length; i-- > 0; ) {
     doc.getElementById(kVcardFields[i][0]).value =
       cardproperty.getProperty(kVcardFields[i][1], "");
   }
 
   var birthday = doc.getElementById("Birthday");
   modifyDatepicker(birthday);
 
+  // Get the year first, so that the following month/day
+  // calculations can take leap years into account.
+  var year = cardproperty.getProperty("BirthYear", null);
+  var birthYear = doc.getElementById("BirthYear");
+  // set the year in the datepicker to the stored year
+  birthday.year = saneBirthYear(year);
+  birthYear.value = year;
+
   // get the month of the year (1 - 12)
   var month = cardproperty.getProperty("BirthMonth", null);
   if (month > 0 && month < 13)
     birthday.month = month - 1;
   else
     birthday.monthField.value = null;
 
   // get the date of the month (1 - 31)
   var date = cardproperty.getProperty("BirthDay", null);
   if (date > 0 && date < 32)
     birthday.date = date;
   else
     birthday.dateField.value = null;
 
-  // get the year
-  var year = cardproperty.getProperty("BirthYear", null);
-  var birthYear = doc.getElementById("BirthYear");
-  // set the year in the datepicker to the stored year
-  // if the year isn't present, default to 2000 (a leap year)
-  birthday.year = year && year < 10000 && year > 0 ? year : kDefaultYear;
-  birthYear.value = year;
-
   // get the current age
   calculateAge(null, birthYear);
 
   // when the birth year changes, update the datepicker's year to the new value
   // or to kDefaultYear if the value is null
   birthYear.onchange = calculateAge;
   birthday.onchange = calculateAge;
   var age = doc.getElementById("Age");
--- a/mail/components/addrbook/content/abCardViewOverlay.js
+++ b/mail/components/addrbook/content/abCardViewOverlay.js
@@ -286,27 +286,28 @@ function DisplayCardViewPane(realCard)
   else {
     // Other section
     // setup the birthday information
     var day = card.getProperty("BirthDay", null);
     var month = card.getProperty("BirthMonth", null);
     var year = card.getProperty("BirthYear", null);
     var dateStr;
     if (day > 0 && day < 32 && month > 0 && month < 13) {
-      var date = new Date(year, month - 1, day);
+      var date;
       // if the year exists, just use Date.toLocaleString
       if (year) {
         // use UTC-based calculations to avoid off-by-one day
         // due to time zone/dst discontinuity
         date = new Date(Date.UTC(year, month - 1, day));
         date.setUTCFullYear(year); // to handle two-digit years properly
         dateStr = date.toLocaleDateString([], {timeZone: "UTC"});
       }
       // if the year doesn't exist, display Month DD (ex. January 01)
       else {
+        date = new Date(saneBirthYear(year), month - 1, day);
         // toLocaleFormat() seems to have a different implementation than
         // toLocaleDateString() and returns correct results even when not
         // passing UTC times; besides, it would not support an options
         // parameter for {timeZone: "UTC"} anyway.
         dateStr = date.toLocaleFormat(
           gAddressBookBundle.getString("dateFormatMonthDay")
         );
       }
--- a/mail/components/addrbook/content/abCommon.js
+++ b/mail/components/addrbook/content/abCommon.js
@@ -15,16 +15,19 @@ var gAbView = null;
 var gAddressBookBundle;
 // A boolean variable determining whether AB column should be shown in AB
 // sidebar in compose window.
 var gShowAbColumnInComposeSidebar = false;
 
 const kDefaultSortColumn = "GeneratedName";
 const kDefaultAscending = "ascending";
 const kDefaultDescending = "descending";
+// kDefaultYear will be used in birthday calculations when no year is given;
+// this is a leap year so that Feb 29th works.
+const kDefaultYear = 2000;
 const kAllDirectoryRoot = "moz-abdirectory://";
 const kLdapUrlPrefix = "moz-abldapdirectory://";
 const kPersonalAddressbookURI = "moz-abmdbdirectory://abook.mab";
 const kCollectedAddressbookURI = "moz-abmdbdirectory://history.mab";
 // The default, generic contact image is displayed via CSS when the photoURI is
 // blank.
 let defaultPhotoURI = "";
 
@@ -857,8 +860,16 @@ function makePhotoFile(aDir, aExtension)
  * The '(' and ')' characters are special for the addressbook
  * search query language, but are not escaped in encodeURIComponent()
  * so must be done manually on top of it.
  */
 function encodeABTermValue(aString) {
   return encodeURIComponent(aString).replace(/\(/g, "%28").replace(/\)/g, "%29");
 }
 
+/**
+ * Validates the given year and returns it, if it looks sane.
+ * Returns kDefaultYear (a leap year), if no valid date is given.
+ * This ensures that month/day calculations still work.
+ */
+function saneBirthYear(aYear) {
+  return aYear && aYear < 10000 && aYear > 0 ? aYear : kDefaultYear;
+}
--- a/suite/mailnews/addrbook/abCardOverlay.js
+++ b/suite/mailnews/addrbook/abCardOverlay.js
@@ -50,17 +50,16 @@ const kVcardFields =
           // Other > (custom)
          ["Custom1", "Custom1"],
          ["Custom2", "Custom2"],
          ["Custom3", "Custom3"],
          ["Custom4", "Custom4"],
           // Other > Notes
          ["Notes", "Notes"]];
 
-const kDefaultYear = 2000;
 var gEditCard;
 var gOnSaveListeners = [];
 var gOnLoadListeners = [];
 var gOkCallback = null;
 var gHideABPicker = false;
 var gPhotoHandlers = {};
 
 function OnLoadNewCard()
@@ -444,38 +443,39 @@ function GetCardValues(cardproperty, doc
   for (var i = kVcardFields.length; i-- > 0; ) {
     doc.getElementById(kVcardFields[i][0]).value =
       cardproperty.getProperty(kVcardFields[i][1], "");
   }
 
   var birthday = doc.getElementById("Birthday");
   modifyDatepicker(birthday);
 
+  // Get the year first, so that the following month/day
+  // calculations can take leap years into account.
+  var year = cardproperty.getProperty("BirthYear", null);
+  var birthYear = doc.getElementById("BirthYear");
+  // set the year in the datepicker to the stored year
+  // if the year isn't present, default to 2000 (a leap year)
+  birthday.year = saneBirthYear(year);
+  birthYear.value = year;
+
   // get the month of the year (1 - 12)
   var month = cardproperty.getProperty("BirthMonth", null);
   if (month > 0 && month < 13)
     birthday.month = month - 1;
   else
     birthday.monthField.value = null;
 
   // get the date of the month (1 - 31)
   var date = cardproperty.getProperty("BirthDay", null);
   if (date > 0 && date < 32)
     birthday.date = date;
   else
     birthday.dateField.value = null;
 
-  // get the year
-  var year = cardproperty.getProperty("BirthYear", null);
-  var birthYear = doc.getElementById("BirthYear");
-  // set the year in the datepicker to the stored year
-  // if the year isn't present, default to 2000 (a leap year)
-  birthday.year = year && year < 10000 && year > 0 ? year : kDefaultYear;
-  birthYear.value = year;
-
   // get the current age
   calculateAge(null, birthYear);
   // when the birth year changes, update the datepicker's year to the new value
   // or to kDefaultYear if the value is null
   birthYear.onchange = calculateAge;
   birthday.onchange = calculateAge;
   var age = doc.getElementById("Age");
   age.onchange = calculateYear;
--- a/suite/mailnews/addrbook/abCardViewOverlay.js
+++ b/suite/mailnews/addrbook/abCardViewOverlay.js
@@ -246,27 +246,28 @@ function DisplayCardViewPane(realCard)
   else {
 	  // Other section
     /// setup the birthday information
     var day = card.getProperty("BirthDay", null);
     var month = card.getProperty("BirthMonth", null);
     var year = card.getProperty("BirthYear", null);
     var dateStr;
     if (day > 0 && day < 32 && month > 0 && month < 13) {
-      var date = new Date(year, month - 1, day);
+      var date;
       // if the year exists, just use Date.toLocaleString
       if (year) {
         // use UTC-based calculations to avoid off-by-one day
         // due to time zone/dst discontinuity
         date = new Date(Date.UTC(year, month - 1, day));
         date.setUTCFullYear(year); // to handle two-digit years properly
         dateStr = date.toLocaleDateString([], {timeZone: "UTC"});
       }
       // if the year doesn't exist, display Month DD (ex. January 1)
       else {
+        date = new Date(saneBirthYear(year), month - 1, day);
         // toLocaleFormat() seems to have a different implementation than
         // toLocaleDateString() and returns correct results even when not
         // passing UTC times; besides, it would not support an options
         // parameter for {timeZone: "UTC"} anyway.
         dateStr = date.toLocaleFormat(gAddressBookBundle.getString("dateFormatMonthDay"));
       }
     }
     else if (year)
--- a/suite/mailnews/addrbook/abCommon.js
+++ b/suite/mailnews/addrbook/abCommon.js
@@ -10,16 +10,19 @@ var gDirTree = 0;
 var abList = null;
 var gAbResultsTree = null;
 var gAbView = null;
 var gAddressBookBundle;
 
 const kDefaultSortColumn = "GeneratedName";
 const kDefaultAscending = "ascending";
 const kDefaultDescending = "descending";
+// kDefaultYear will be used in birthday calculations when no year is given;
+// this is a leap year so that Feb 29th works.
+const kDefaultYear = 2000;
 const kLdapUrlPrefix = "moz-abldapdirectory://";
 const kPersonalAddressbookURI = "moz-abmdbdirectory://abook.mab";
 const kCollectedAddressbookURI = "moz-abmdbdirectory://history.mab";
 // The default image for contacts
 var defaultPhotoURI = "chrome://messenger/skin/addressbook/icons/contact-generic.png";
 
 // Controller object for Dir Pane
 var DirPaneController =
@@ -736,8 +739,17 @@ function makePhotoFile(aDir, aExtension)
  * Encode the string passed as value into an addressbook search term.
  * The '(' and ')' characters are special for the addressbook
  * search query language, but are not escaped in encodeURIComponent()
  * so must be done manually on top of it.
  */
 function encodeABTermValue(aString) {
   return encodeURIComponent(aString).replace(/\(/g, "%28").replace(/\)/g, "%29");
 }
+
+/**
+ * Validates the given year and returns it, if it looks sane.
+ * Returns kDefaultYear (a leap year), if no valid date is given.
+ * This ensures that month/day calculations still work.
+ */
+function saneBirthYear(aYear) {
+  return aYear && aYear < 10000 && aYear > 0 ? aYear : kDefaultYear;
+}