Bug 1634912 - de-xpcom calendar datetime formatter. r=pmorris
authorPhilipp Kewisch <mozilla@kewis.ch>
Sat, 16 May 2020 14:17:45 +0300
changeset 38270 39a884a7b19acd946dedf75a441f0d14631053eb
parent 38269 7ba221cc0eb4896093aae8b19ab560f92e92e67d
child 38271 94c8faf148df4c428cb3d25fb37fe9d24d63b9dc
push id2607
push userclokep@gmail.com
push dateMon, 01 Jun 2020 20:50:20 +0000
treeherdercomm-beta@9d45cd34927b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspmorris
bugs1634912
Bug 1634912 - de-xpcom calendar datetime formatter. r=pmorris Differential Revision: https://phabricator.services.mozilla.com/D73560
calendar/base/content/agenda-listbox.js
calendar/base/content/calendar-base-view.js
calendar/base/content/calendar-day-label.js
calendar/base/content/calendar-event-column.js
calendar/base/content/calendar-item-bindings.js
calendar/base/content/calendar-month-view.js
calendar/base/content/calendar-multiday-base-view.js
calendar/base/content/calendar-task-tree-view.js
calendar/base/content/calendar-task-view.js
calendar/base/content/calendar-unifinder.js
calendar/base/content/calendar-views-utils.js
calendar/base/content/dialogs/calendar-event-dialog-attendees-custom-elements.js
calendar/base/content/dialogs/calendar-invitations-dialog.js
calendar/base/content/preferences/general.js
calendar/base/content/preferences/views.js
calendar/base/content/today-pane.js
calendar/base/content/widgets/calendar-alarm-widget.js
calendar/base/modules/calRecurrenceUtils.jsm
calendar/base/modules/calUtils.jsm
calendar/base/modules/moz.build
calendar/base/modules/utils/calDateTimeFormatter.jsm
calendar/base/modules/utils/calDateTimeUtils.jsm
calendar/base/modules/utils/calPrintUtils.jsm
calendar/base/public/calIDateTimeFormatter.idl
calendar/base/public/moz.build
calendar/base/src/CalAlarm.jsm
calendar/base/src/CalDateTimeFormatter.jsm
calendar/base/src/CalIcsParser.jsm
calendar/base/src/components.conf
calendar/base/src/moz.build
calendar/import-export/CalHtmlExport.jsm
calendar/import-export/CalWeekPrinter.jsm
calendar/lightning/content/lightning-item-iframe.js
calendar/lightning/modules/ltnInvitationUtils.jsm
calendar/resources/content/datetimepickers/datetimepickers.js
calendar/resources/content/mouseoverPreviews.js
calendar/test/browser/browser_basicFunctionality.js
calendar/test/browser/browser_eventDisplay.js
calendar/test/browser/browser_todayPane.js
calendar/test/browser/eventDialog/browser_eventDialog.js
calendar/test/browser/recurrence/browser_weeklyUntil.js
calendar/test/browser/views/browser_dayView.js
calendar/test/browser/views/browser_monthView.js
calendar/test/browser/views/browser_multiweekView.js
calendar/test/browser/views/browser_weekView.js
calendar/test/modules/ItemEditingHelpers.jsm
calendar/test/unit/test_datetimeformatter.js
calendar/test/unit/test_ltninvitationutils.js
calendar/test/unit/test_recur.js
--- a/calendar/base/content/agenda-listbox.js
+++ b/calendar/base/content/agenda-listbox.js
@@ -43,17 +43,17 @@
         if (this.id == "nextweek-header") {
           if (duration > 7) {
             this.kCheckbox.label += " (" + unitPluralForm(duration / 7, "weeks") + ")";
           } else {
             this.kCheckbox.label += " (" + unitPluralForm(duration, "days") + ")";
           }
         }
       } else if (synthetic.duration == 1) {
-        this.kCheckbox.label = cal.getDateFormatter().formatDate(synthetic.start);
+        this.kCheckbox.label = cal.dtz.formatter.formatDate(synthetic.start);
       } else {
         this.kCheckbox.label = cal
           .getDateFormatter()
           .formatInterval(synthetic.start, synthetic.end);
       }
     }
     getCheckbox() {
       return this.kCheckbox;
@@ -135,17 +135,17 @@
       this.mOccurrence = null;
       this.initializeAttributeInheritance();
     }
 
     setOccurrence(aOccurrence, aPeriod) {
       this.mOccurrence = aOccurrence;
       this.mAllDayItem = this.querySelector("calendar-month-day-box-item");
       this.mAllDayItem.occurrence = aOccurrence;
-      let dateFormatter = cal.getDateFormatter();
+      let dateFormatter = cal.dtz.formatter;
       let periodStartDate = aPeriod.start.clone();
       periodStartDate.isDate = true;
       let periodEndDate = aPeriod.end;
       let startDate = this.mOccurrence[cal.dtz.startDateProp(this.mOccurrence)].getInTimezone(
         cal.dtz.defaultTimezone
       );
       let endDate = this.mOccurrence[cal.dtz.endDateProp(this.mOccurrence)].getInTimezone(
         cal.dtz.defaultTimezone
@@ -266,19 +266,17 @@
         `)
       );
       this.mOccurrence = null;
       this.initializeAttributeInheritance();
     }
     setOccurrence(aItem, aPeriod) {
       this.mOccurrence = aItem;
       this.setAttribute("status", aItem.status);
-      let dateFormatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-        Ci.calIDateTimeFormatter
-      );
+      let dateFormatter = cal.dtz.formatter;
       let periodStartDate = aPeriod.start.clone();
       periodStartDate.isDate = true;
       let periodEndDate = aPeriod.end.clone();
       periodEndDate.day--;
       let start = this.mOccurrence[cal.dtz.startDateProp(this.mOccurrence)].getInTimezone(
         cal.dtz.defaultTimezone
       );
       let end = this.mOccurrence[cal.dtz.endDateProp(this.mOccurrence)].getInTimezone(
--- a/calendar/base/content/calendar-base-view.js
+++ b/calendar/base/content/calendar-base-view.js
@@ -910,17 +910,17 @@
 
     // calICalendarView Methods
 
     goToDay(date) {
       this.showDate(date);
     }
 
     getRangeDescription() {
-      return cal.getDateFormatter().formatInterval(this.rangeStartDate, this.rangeEndDate);
+      return cal.dtz.formatter.formatInterval(this.rangeStartDate, this.rangeEndDate);
     }
 
     removeDropShadows() {
       this.querySelectorAll("[dropbox='true']").forEach(dbox => {
         dbox.setAttribute("dropbox", "false");
       });
     }
 
--- a/calendar/base/content/calendar-day-label.js
+++ b/calendar/base/content/calendar-day-label.js
@@ -63,28 +63,28 @@
       } else {
         this.longWeekdayName.removeAttribute("relation");
         this.shortWeekdayName.removeAttribute("relation");
       }
     }
 
     set weekDay(val) {
       this.mWeekday = val % 7;
-      this.longWeekdayName.value = cal.getDateFormatter().dayName(val);
-      this.shortWeekdayName.value = cal.getDateFormatter().shortDayName(val);
+      this.longWeekdayName.value = cal.dtz.formatter.dayName(val);
+      this.shortWeekdayName.value = cal.dtz.formatter.shortDayName(val);
       return this.mWeekday;
     }
 
     get weekDay() {
       return this.mWeekday;
     }
 
     set date(val) {
       this.mDate = val;
-      let dateFormatter = cal.getDateFormatter();
+      let dateFormatter = cal.dtz.formatter;
       let label = cal.l10n.getCalString("dayHeaderLabel", [
         dateFormatter.shortDayName(val.weekday),
         dateFormatter.formatDateWithoutYear(val),
       ]);
       this.shortWeekdayName.setAttribute("value", label);
       label = cal.l10n.getCalString("dayHeaderLabel", [
         dateFormatter.dayName(val.weekday),
         dateFormatter.formatDateWithoutYear(val),
--- a/calendar/base/content/calendar-event-column.js
+++ b/calendar/base/content/calendar-event-column.js
@@ -1821,23 +1821,23 @@
       let realstartmin = this.mDragState.startMin + this.mStartMin;
       let realendmin = this.mDragState.endMin + this.mStartMin;
       let starthr = Math.floor(realstartmin / 60);
       let startmin = realstartmin % 60;
 
       let endhr = Math.floor(realendmin / 60);
       let endmin = realendmin % 60;
 
-      let timeFormatter = cal.getDateFormatter();
+      let formatter = cal.dtz.formatter;
 
       let jsTime = new Date();
       jsTime.setHours(starthr, startmin);
-      let startstr = timeFormatter.formatTime(cal.dtz.jsDateToDateTime(jsTime, cal.dtz.floating));
+      let startstr = formatter.formatTime(cal.dtz.jsDateToDateTime(jsTime, cal.dtz.floating));
       jsTime.setHours(endhr, endmin);
-      let endstr = timeFormatter.formatTime(cal.dtz.jsDateToDateTime(jsTime, cal.dtz.floating));
+      let endstr = formatter.formatTime(cal.dtz.jsDateToDateTime(jsTime, cal.dtz.floating));
 
       // Tasks without Entry or Due date have a string as first label
       // instead of the time.
       if (cal.item.isToDo(this.mDragState.dragOccurrence)) {
         if (!this.mDragState.dragOccurrence.dueDate) {
           startstr = cal.l10n.getCalString("dragLabelTasksWithOnlyEntryDate");
         } else if (!this.mDragState.dragOccurrence.entryDate) {
           startstr = cal.l10n.getCalString("dragLabelTasksWithOnlyDueDate");
--- a/calendar/base/content/calendar-item-bindings.js
+++ b/calendar/base/content/calendar-item-bindings.js
@@ -46,22 +46,21 @@
         date = this.mItem[cal.dtz.endDateProp(this.mItem)];
       }
       let hideTextbox = date == null;
       if (hideTextbox) {
         this.style.visibility = "collapse";
       } else {
         const kDefaultTimezone = cal.dtz.defaultTimezone;
         let localTime = date.getInTimezone(kDefaultTimezone);
-        let formatter = cal.getDateFormatter();
-        this.value = formatter.formatDateTime(localTime);
+        this.value = cal.dtz.formatter.formatDateTime(localTime);
         if (!date.timezone.isFloating && date.timezone.tzid != kDefaultTimezone.tzid) {
           // we additionally display the original datetime with timezone
           let orgTime = cal.l10n.getCalString("datetimeWithTimezone", [
-            formatter.formatDateTime(date),
+            cal.dtz.formatter.formatDateTime(date),
             date.timezone.tzid,
           ]);
           this.value += " (" + orgTime + ")";
         }
         this.style.visibility = "visible";
       }
     }
 
--- a/calendar/base/content/calendar-month-view.js
+++ b/calendar/base/content/calendar-month-view.js
@@ -118,17 +118,17 @@
         return val;
       }
       this.mShowMonthLabel = val;
 
       if (!this.mDate) {
         return val;
       }
       if (val) {
-        this.setAttribute("value", cal.getDateFormatter().formatDateWithoutYear(this.mDate));
+        this.setAttribute("value", cal.dtz.formatter.formatDateWithoutYear(this.mDate));
       } else {
         this.setAttribute("value", this.mDate.day);
       }
       return val;
     }
 
     setDate(aDate) {
       if (!aDate) {
@@ -353,19 +353,17 @@
     }
 
     set occurrence(val) {
       cal.ASSERT(!this.mOccurrence, "Code changes needed to set the occurrence twice", true);
       this.mOccurrence = val;
       if (cal.item.isEvent(val) && !val.startDate.isDate) {
         let icon = this.querySelector(".calendar-item-image");
         let label = this.querySelector(".calendar-month-day-box-item-label");
-        let formatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-          Ci.calIDateTimeFormatter
-        );
+        let formatter = cal.dtz.formatter;
         let timezone = this.calendarView ? this.calendarView.mTimezone : cal.dtz.defaultTimezone;
         let parentDate = this.parentBox.date;
         let parentTime = cal.createDateTime();
         parentTime.resetTo(parentDate.year, parentDate.month, parentDate.day, 0, 0, 0, timezone);
         let startTime = val.startDate.getInTimezone(timezone);
         let endTime = val.endDate.getInTimezone(timezone);
         let nextDay = parentTime.clone();
         nextDay.day++;
--- a/calendar/base/content/calendar-multiday-base-view.js
+++ b/calendar/base/content/calendar-multiday-base-view.js
@@ -134,17 +134,17 @@
     relayout() {
       const topbox = this.querySelector(".topbox");
 
       while (topbox.hasChildNodes()) {
         topbox.lastChild.remove();
       }
 
       const orient = topbox.getAttribute("orient");
-      const timeFormatter = cal.getDateFormatter();
+      const formatter = cal.dtz.formatter;
       const jsTime = new Date();
 
       this.getSections().forEach(([startMinute, duration]) => {
         const box = document.createXULElement("box");
         box.setAttribute("orient", orient);
 
         // Calculate duration pixel as the difference between
         // start pixel and end pixel to avoid rounding errors.
@@ -157,17 +157,17 @@
         const hour = Math.floor(startMinute / 60);
         let timeString = "";
 
         if (duration == 60) {
           jsTime.setHours(hour, 0, 0);
 
           const dateTime = cal.dtz.jsDateToDateTime(jsTime, cal.dtz.floating);
 
-          timeString = timeFormatter.formatTime(dateTime);
+          timeString = formatter.formatTime(dateTime);
         }
 
         const label = document.createXULElement("label");
         label.setAttribute("value", timeString);
         label.setAttribute("class", "calendar-time-bar-label");
         label.setAttribute("align", "center");
         box.appendChild(label);
 
--- a/calendar/base/content/calendar-task-tree-view.js
+++ b/calendar/base/content/calendar-task-tree-view.js
@@ -483,14 +483,12 @@ class CalendarTaskTreeView {
   /**
    * Format a datetime object for display.
    *
    * @param {Object} dateTime    From a todo object, not a JavaScript date.
    * @return {string}            Formatted string version of the datetime ("" if invalid).
    */
   _formatDateTime(dateTime) {
     return dateTime && dateTime.isValid
-      ? Cc["@mozilla.org/calendar/datetime-formatter;1"]
-          .getService(Ci.calIDateTimeFormatter)
-          .formatDateTime(dateTime.getInTimezone(cal.dtz.defaultTimezone))
+      ? cal.dtz.formatter.formatDateTime(dateTime.getInTimezone(cal.dtz.defaultTimezone))
       : "";
   }
 }
--- a/calendar/base/content/calendar-task-view.js
+++ b/calendar/base/content/calendar-task-view.js
@@ -23,19 +23,17 @@ var taskDetailsView = {
    * its the only function in taskDetailsView.
    */
   onSelect(event) {
     function displayElement(id, flag) {
       setBooleanAttribute(id, "hidden", !flag);
       return flag;
     }
 
-    let dateFormatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-      Ci.calIDateTimeFormatter
-    );
+    let dateFormatter = cal.dtz.formatter;
 
     let item = document.getElementById("calendar-task-tree").currentTask;
     if (
       displayElement("calendar-task-details-container", item != null) &&
       displayElement("calendar-task-view-splitter", item != null)
     ) {
       document.getElementById("calendar-task-details-title-row").toggleAttribute("hidden", false);
       document.getElementById("calendar-task-details-title").textContent = item.title
--- a/calendar/base/content/calendar-unifinder.js
+++ b/calendar/base/content/calendar-unifinder.js
@@ -285,17 +285,17 @@ function unifinderItemSelect(aEvent) {
 
 /**
  * Helper function to display event datetimes in the unifinder.
  *
  * @param aDatetime     A calIDateTime object to format.
  * @return              The passed date's formatted in the default timezone.
  */
 function formatUnifinderEventDateTime(aDatetime) {
-  return cal.getDateFormatter().formatDateTime(aDatetime.getInTimezone(cal.dtz.defaultTimezone));
+  return cal.dtz.formatter.formatDateTime(aDatetime.getInTimezone(cal.dtz.defaultTimezone));
 }
 
 /**
  * Handler function for double clicking the unifinder.
  *
  * @param event         The DOM doubleclick event.
  */
 function unifinderDoubleClick(event) {
--- a/calendar/base/content/calendar-views-utils.js
+++ b/calendar/base/content/calendar-views-utils.js
@@ -608,17 +608,17 @@ var calendarNavigationBar = {
   setDateRange(startDate, endDate) {
     let docTitle = "";
     if (startDate) {
       let intervalLabel = document.getElementById("intervalDescription");
       let firstWeekNo = cal.getWeekInfoService().getWeekTitle(startDate);
       let secondWeekNo = firstWeekNo;
       let weekLabel = document.getElementById("calendarWeek");
       if (startDate.nativeTime == endDate.nativeTime) {
-        intervalLabel.value = cal.getDateFormatter().formatDate(startDate);
+        intervalLabel.value = cal.dtz.formatter.formatDate(startDate);
       } else {
         intervalLabel.value = currentView().getRangeDescription();
         secondWeekNo = cal.getWeekInfoService().getWeekTitle(endDate);
       }
       if (secondWeekNo == firstWeekNo) {
         weekLabel.value = cal.l10n.getCalString("singleShortCalendarWeek", [firstWeekNo]);
         weekLabel.tooltipText = cal.l10n.getCalString("singleLongCalendarWeek", [firstWeekNo]);
       } else {
--- a/calendar/base/content/dialogs/calendar-event-dialog-attendees-custom-elements.js
+++ b/calendar/base/content/dialogs/calendar-event-dialog-attendees-custom-elements.js
@@ -1519,19 +1519,17 @@
      */
     onLoad() {
       let numHours = this.endHour - this.startHour;
       this.state = new Array(this.range * numHours);
       for (let i = 0; i < this.state.length; i++) {
         this.state[i] = Ci.calIFreeBusyInterval.UNKNOWN;
       }
       let step_in_minutes = Math.floor((60 * this.zoomFactor) / 100);
-      let formatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-        Ci.calIDateTimeFormatter
-      );
+      let formatter = cal.dtz.formatter;
       let date = cal.dtz.jsDateToDateTime(new Date());
       date.hour = this.startHour;
       date.minute = 0;
       if (this.hoursNode.children.length <= 0) {
         let template = document.createXULElement("label");
         template.className = "freebusy-grid";
         // TODO: hardcoded value
         let num_days = Math.max(2, (4 * this.zoomFactor) / 100);
@@ -1898,47 +1896,40 @@
      * @param {calIDateTime} val        Date object to be modified
      * @returns {calIDateTime}          Modified date object
      */
     set date(val) {
       if (val == null) {
         return null;
       }
 
+      let formatter = cal.dtz.formatter;
       let date = val.clone();
       date.hour = 0;
       date.minute = 0;
       date.isDate = false;
 
-      if (!this.dateFormatter) {
-        this.dateFormatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-          Ci.calIDateTimeFormatter
-        );
-      }
-
       // First set the formatted date string as title
       let dateValue =
-        this.zoomFactor > 100
-          ? this.dateFormatter.formatDateShort(date)
-          : this.dateFormatter.formatDateLong(date);
+        this.zoomFactor > 100 ? formatter.formatDateShort(date) : formatter.formatDateLong(date);
       this.text.setAttribute("value", dateValue);
 
       // Now create as many 'hour' elements as needed
       let step_in_minutes = Math.floor((60 * this.zoomFactor) / 100);
       let hours = this.box;
       date.hour = this.startHour;
       if (hours.children.length <= 0) {
         let template = document.createXULElement("label");
         template.className = "freebusy-timebar-hour";
         let count = Math.ceil(((this.endHour - this.startHour) * 60) / step_in_minutes);
         let remain = count;
         let first = true;
         while (remain--) {
           let newNode = template.cloneNode(false);
-          let value = this.dateFormatter.formatTime(date);
+          let value = formatter.formatTime(date);
           if (first) {
             newNode.classList.add("first-in-day");
             first = false;
           }
           newNode.setAttribute("value", value);
           hours.appendChild(newNode);
           date.minute += step_in_minutes;
 
--- a/calendar/base/content/dialogs/calendar-invitations-dialog.js
+++ b/calendar/base/content/dialogs/calendar-invitations-dialog.js
@@ -11,21 +11,19 @@ var { cal } = ChromeUtils.import("resour
 // Wrap in a block to prevent leaking to window scope.
 {
   const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
   class MozCalendarInvitationsRichlistitem extends MozElements.MozRichlistitem {
     constructor() {
       super();
 
-      this.mDateFormatter = null;
       this.mCalendarItem = null;
       this.mInitialParticipationStatus = null;
       this.mParticipationStatus = null;
-      this.mDateFormatter = cal.getDateFormatter();
       this.calInvitationsProps = Services.strings.createBundle(
         "chrome://calendar/locale/calendar-invitations-dialog.properties"
       );
     }
 
     getString(propName) {
       return this.calInvitationsProps.GetStringFromName(propName);
     }
@@ -106,17 +104,17 @@ var { cal } = ChromeUtils.import("resour
       this.mCalendarItem = item;
       this.mInitialParticipationStatus = this.getCalendarItemParticipationStatus(item);
       this.participationStatus = this.mInitialParticipationStatus;
 
       let titleLabel = this.querySelector(".calendar-invitations-richlistitem-title");
       titleLabel.setAttribute("value", item.title);
 
       let dateLabel = this.querySelector(".calendar-invitations-richlistitem-date");
-      let dateString = this.mDateFormatter.formatItemInterval(item);
+      let dateString = cal.dtz.formatter.formatItemInterval(item);
       if (item.startDate.isDate) {
         dateString += ", " + this.getString("alldayEvent");
       }
       dateLabel.setAttribute("value", dateString);
 
       let recurrenceLabel = this.querySelector(".calendar-invitations-richlistitem-recurrence");
       if (item.recurrenceInfo) {
         recurrenceLabel.setAttribute("value", this.getString("recurrentEvent"));
--- a/calendar/base/content/preferences/general.js
+++ b/calendar/base/content/preferences/general.js
@@ -28,20 +28,17 @@ Preferences.addAll([
  * Global Object to hold methods for the general pref pane
  */
 var gCalendarGeneralPane = {
   /**
    * Initialize the general pref pane. Sets up dialog controls to match the
    * values set in prefs.
    */
   init() {
-    let formatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-      Ci.calIDateTimeFormatter
-    );
-
+    let formatter = cal.dtz.formatter;
     let dateFormattedLong = formatter.formatDateLong(cal.dtz.now());
     let dateFormattedShort = formatter.formatDateShort(cal.dtz.now());
 
     // menu items include examples of current date formats.
     document
       .getElementById("dateformat-long-menuitem")
       .setAttribute("label", labelLong + ": " + dateFormattedLong);
     document
--- a/calendar/base/content/preferences/views.js
+++ b/calendar/base/content/preferences/views.js
@@ -41,25 +41,24 @@ var gViewsPane = {
 
   /**
    * Initialize the strings for the  "day starts at" and "day ends at"
    * menulists. This is needed to respect locales that use AM/PM.
    */
   initializeViewStartEndMenus() {
     const { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
 
+    let formatter = cal.dtz.formatter;
     let calTime = cal.createDateTime();
     calTime.minute = 0;
 
-    let timeFormatter = cal.getDateFormatter();
-
     // 1 to 23 instead of 0 to 24 to keep midnight & noon as the localized strings
     for (let theHour = 1; theHour <= 23; theHour++) {
       calTime.hour = theHour;
-      let time = timeFormatter.formatTime(calTime);
+      let time = formatter.formatTime(calTime);
 
       let labelIdStart = "timeStart" + theHour;
       let labelIdEnd = "timeEnd" + theHour;
       // This if block to keep Noon as the localized string, instead of as a number.
       if (theHour != 12) {
         document.getElementById(labelIdStart).setAttribute("label", time);
         document.getElementById(labelIdEnd).setAttribute("label", time);
       }
--- a/calendar/base/content/today-pane.js
+++ b/calendar/base/content/today-pane.js
@@ -406,17 +406,17 @@ var TodayPane = {
     // to avoid its selectedIndex being reset to the wrong value.
     setTimeout(() => {
       let weekdaylabel = document.getElementById("weekdayNameContainer");
       weekdaylabel.selectedIndex = this.start.weekday;
     }, 0);
 
     let monthnamelabel = document.getElementById("monthNameContainer");
     monthnamelabel.value =
-      cal.getDateFormatter().shortMonthName(this.start.month) + " " + this.start.year;
+      cal.dtz.formatter.shortMonthName(this.start.month) + " " + this.start.year;
 
     let currentweeklabel = document.getElementById("currentWeek-label");
     currentweeklabel.value =
       cal.l10n.getCalString("shortcalendarweek") +
       " " +
       cal.getWeekInfoService().getWeekTitle(this.start);
 
     if (!aDontUpdateMinimonth) {
--- a/calendar/base/content/widgets/calendar-alarm-widget.js
+++ b/calendar/base/content/widgets/calendar-alarm-widget.js
@@ -88,17 +88,17 @@
     /**
      * Refresh UI text (dates, titles, locations) when the data has changed.
      */
     updateLabels() {
       if (!this.mItem || !this.mAlarm) {
         // Setup not complete, do nothing for now.
         return;
       }
-      const formatter = cal.getDateFormatter();
+      const formatter = cal.dtz.formatter;
       let titleLabel = this.querySelector(".alarm-title-label");
       let locationDescription = this.querySelector(".alarm-location-description");
       let dateLabel = this.querySelector(".alarm-date-label");
 
       // Dates
       if (cal.item.isEvent(this.mItem)) {
         dateLabel.value = formatter.formatItemInterval(this.mItem);
       } else if (cal.item.isToDo(this.mItem)) {
@@ -141,17 +141,17 @@
         snoozeButton.removeAttribute("tooltiptext");
       }
     }
 
     /**
      * Refresh UI text for relative date when the data has changed.
      */
     updateRelativeDateLabel() {
-      const formatter = cal.getDateFormatter();
+      const formatter = cal.dtz.formatter;
       const item = this.mItem;
       let relativeDateLabel = this.querySelector(".alarm-relative-date-label");
       let relativeDateString;
       let startDate = item[cal.dtz.startDateProp(item)] || item[cal.dtz.endDateProp(item)];
 
       if (startDate) {
         startDate = startDate.getInTimezone(cal.dtz.defaultTimezone);
         let currentDate = cal.dtz.now();
--- a/calendar/base/modules/calRecurrenceUtils.jsm
+++ b/calendar/base/modules/calRecurrenceUtils.jsm
@@ -67,17 +67,17 @@ function recurrenceRule2String(recurrenc
       /* "BYMONTHDAY", */
       "BYYEARDAY",
       "BYWEEKNO",
       /* "BYMONTH", */
       "BYSETPOS",
     ];
 
     if (rule && !checkRecurrenceRule(rule, byparts)) {
-      let dateFormatter = cal.getDateFormatter();
+      let dateFormatter = cal.dtz.formatter;
       let ruleString;
       if (rule.type == "DAILY") {
         if (checkRecurrenceRule(rule, ["BYDAY"])) {
           let days = rule.getComponent("BYDAY");
           let weekdays = [2, 3, 4, 5, 6];
           if (weekdays.length == days.length) {
             let i;
             for (i = 0; i < weekdays.length; i++) {
--- a/calendar/base/modules/calUtils.jsm
+++ b/calendar/base/modules/calUtils.jsm
@@ -59,17 +59,16 @@ var cal = {
   getIcsService: _service("@mozilla.org/calendar/ics-service;1", "calIICSService"),
   getTimezoneService: _service("@mozilla.org/calendar/timezone-service;1", "calITimezoneService"),
   getCalendarSearchService: _service(
     "@mozilla.org/calendar/calendarsearch-service;1",
     "calICalendarSearchProvider"
   ),
   getFreeBusyService: _service("@mozilla.org/calendar/freebusy-service;1", "calIFreeBusyService"),
   getWeekInfoService: _service("@mozilla.org/calendar/weekinfo-service;1", "calIWeekInfoService"),
-  getDateFormatter: _service("@mozilla.org/calendar/datetime-formatter;1", "calIDateTimeFormatter"),
   getDragService: _service("@mozilla.org/widget/dragservice;1", "nsIDragService"),
 
   /**
    * The calendar console instance
    */
   console: gCalendarConsole,
 
   /**
--- a/calendar/base/modules/moz.build
+++ b/calendar/base/modules/moz.build
@@ -5,16 +5,17 @@
 
 EXTRA_JS_MODULES.calendar.utils += [
     'utils/calACLUtils.jsm',
     'utils/calAlarmUtils.jsm',
     'utils/calAsyncUtils.jsm',
     'utils/calAuthUtils.jsm',
     'utils/calCategoryUtils.jsm',
     'utils/calDataUtils.jsm',
+    'utils/calDateTimeFormatter.jsm',
     'utils/calDateTimeUtils.jsm',
     'utils/calEmailUtils.jsm',
     'utils/calItemUtils.jsm',
     'utils/calIteratorUtils.jsm',
     'utils/calItipUtils.jsm',
     'utils/calL10NUtils.jsm',
     'utils/calPrintUtils.jsm',
     'utils/calProviderUtils.jsm',
rename from calendar/base/src/CalDateTimeFormatter.jsm
rename to calendar/base/modules/utils/calDateTimeFormatter.jsm
--- a/calendar/base/src/CalDateTimeFormatter.jsm
+++ b/calendar/base/modules/utils/calDateTimeFormatter.jsm
@@ -1,90 +1,114 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-var EXPORTED_SYMBOLS = ["CalDateTimeFormatter"];
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ChromeUtils.defineModuleGetter(this, "cal", "resource:///modules/calendar/calUtils.jsm");
 
-var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+XPCOMUtils.defineLazyGetter(this, "gDateStringBundle", () =>
+  Services.strings.createBundle("chrome://calendar/locale/dateFormat.properties")
+);
+
+/*
+ * Date time formatting functions for display.
+ */
 
-function CalDateTimeFormatter() {
-  this.wrappedJSObject = this;
-  this.mDateStringBundle = Services.strings.createBundle(
-    "chrome://calendar/locale/dateFormat.properties"
-  );
-}
-CalDateTimeFormatter.prototype = {
-  QueryInterface: ChromeUtils.generateQI([Ci.calIDateTimeFormatter]),
-  classID: Components.ID("{4123da9a-f047-42da-a7d0-cc4175b9f36a}"),
+// NOTE: This module should not be loaded directly, it is available when
+// including calUtils.jsm under the cal.dtz.formatter namespace.
+
+const EXPORTED_SYMBOLS = ["formatter"]; /* exported formatter */
 
+var formatter = {
+  /**
+   * Format a date in either short or long format, depending on the users preference.
+   *
+   * @param {calIDateTime} aDate    The datetime to format.
+   * @return {string}               A string representing the date part of the datetime.
+   */
   formatDate(aDate) {
     // Format the date using user's format preference (long or short)
     let format = Services.prefs.getIntPref("calendar.date.format", 0);
     return format == 0 ? this.formatDateLong(aDate) : this.formatDateShort(aDate);
   },
 
+  /**
+   * Format a date into a short format, for example "12/17/2005".
+   *
+   * @param {calIDateTime} aDate    The datetime to format.
+   * @return {string}               A string representing the date part of the datetime.
+   */
   formatDateShort(aDate) {
-    return this._inTimezone(aDate, { dateStyle: "short" });
+    return inTimezone(aDate, { dateStyle: "short" });
   },
 
+  /**
+   * Format a date into a long format, for example "Sat Dec 17 2005".
+   *
+   * @param {calIDateTime} aDate    The datetime to format.
+   * @return {string}               A string representing the date part of the datetime.
+   */
   formatDateLong(aDate) {
-    return this._inTimezone(aDate, { dateStyle: "full" });
+    return inTimezone(aDate, { dateStyle: "full" });
   },
 
+  /**
+   * Format a date into a short format without mentioning the year, for example "Dec 17"
+   *
+   * @param {calIDateTime} aDate    The datetime to format.
+   * @return {string}               A string representing the date part of the datetime.
+   */
   formatDateWithoutYear(aDate) {
     let dtOptions = { month: "short", day: "numeric" };
-    return this._inTimezone(aDate, dtOptions);
+    return inTimezone(aDate, dtOptions);
   },
 
+  /**
+   * Format a time into the format specified by the OS settings. Will omit the seconds from the
+   * output.
+   *
+   * @param {calIDateTime} aDate    The datetime to format.
+   * @return {string}               A string representing the time part of the datetime.
+   */
   formatTime(aDate) {
     if (aDate.isDate) {
-      return this.mDateStringBundle.GetStringFromName("AllDay");
+      return gDateStringBundle.GetStringFromName("AllDay");
     }
 
-    return this._inTimezone(aDate, { timeStyle: "short" });
+    return inTimezone(aDate, { timeStyle: "short" });
   },
 
+  /**
+   * Format a datetime into the format specified by the OS settings. Will omit the seconds from the
+   * output.
+   *
+   * @param {calIDateTime} aDate    The datetime to format.
+   * @return {string}               A string representing the datetime.
+   */
   formatDateTime(aDate) {
     let formattedDate = this.formatDate(aDate);
     let formattedTime = this.formatTime(aDate);
 
     let timeBeforeDate = Services.prefs.getBoolPref("calendar.date.formatTimeBeforeDate", false);
     if (timeBeforeDate) {
       return formattedTime + " " + formattedDate;
     }
     return formattedDate + " " + formattedTime;
   },
 
   /**
-   * _inTimezone returns a string with date formatted
+   * Format a time interval like formatInterval, but show only the time.
    *
-   * @param  {calIDateTime} aDate    The date object holding the tz information
-   * @param  {JsObject}     aOptions The options object for formatting.
-   * @return {String}                The date as a string.
+   * @param {calIDateTime} aStartDate   The start of the interval.
+   * @param {calIDateTime} aEndDate     The end of the interval.
+   * @return {string}                   The formatted time interval.
    */
-  _inTimezone(aDate, aOptions) {
-    let formatter = new Services.intl.DateTimeFormat(undefined, aOptions);
-
-    let timezone = aDate.timezone;
-    // We set the tz only if we have a valid tz - otherwise localtime will be used on formatting.
-    if (timezone && (timezone.isUTC || timezone.icalComponent)) {
-      aOptions.timeZone = timezone.tzid;
-      try {
-        formatter = new Services.intl.DateTimeFormat(undefined, aOptions);
-      } catch (ex) {
-        // Non-IANA timezones throw a RangeError.
-        cal.WARN(ex);
-      }
-    }
-
-    return formatter.format(cal.dtz.dateTimeToJsDate(aDate));
-  },
-
   formatTimeInterval(aStartDate, aEndDate) {
     if (!aStartDate && aEndDate) {
       return this.formatTime(aEndDate);
     }
     if (!aEndDate && aStartDate) {
       return this.formatTime(aStartDate);
     }
     if (!aStartDate && !aEndDate) {
@@ -92,16 +116,24 @@ CalDateTimeFormatter.prototype = {
     }
 
     // TODO do we need l10n for this?
     // TODO should we check for the same day? The caller should know what
     // he is doing...
     return this.formatTime(aStartDate) + "\u2013" + this.formatTime(aEndDate);
   },
 
+  /**
+   * Format a date/time interval. The returned string may assume that the dates are so close to each
+   * other, that it can leave out some parts of the part string denoting the end date.
+   *
+   * @param {calIDateTime} aStartDate        The start of the interval.
+   * @param {calIDateTime} aEndDate          The end of the interval.
+   * @return {string}                        A string describing the interval in a legible form.
+   */
   formatInterval(aStartDate, aEndDate) {
     // Check for tasks without start and/or due date
     if (aEndDate == null && aStartDate == null) {
       return cal.l10n.getCalString("datetimeIntervalTaskWithoutDate");
     } else if (aEndDate == null) {
       let startDateString = this.formatDate(aStartDate);
       let startTime = this.formatTime(aStartDate);
       return cal.l10n.getCalString("datetimeIntervalTaskWithoutDueDate", [
@@ -210,62 +242,137 @@ CalDateTimeFormatter.prototype = {
     return cal.l10n.getCalString("datetimeIntervalOnSeveralDays", [
       startDateString,
       startTime,
       endDateString,
       endTime,
     ]);
   },
 
+  /**
+   * Get the monthday followed by its ordinal symbol in the current locale.
+   * e.g.  monthday 1 -> 1st
+   *       monthday 2 -> 2nd etc.
+   *
+   * @param {number} aDay    A number from 1 to 31.
+   * @return {string}        The monthday number in ordinal format in the current locale.
+   */
   formatDayWithOrdinal(aDay) {
-    let ordinalSymbols = this.mDateStringBundle.GetStringFromName("dayOrdinalSymbol").split(",");
+    let ordinalSymbols = gDateStringBundle.GetStringFromName("dayOrdinalSymbol").split(",");
     let dayOrdinalSymbol = ordinalSymbols[aDay - 1] || ordinalSymbols[0];
     return aDay + dayOrdinalSymbol;
   },
 
-  _getItemDates(aItem) {
-    let start = aItem[cal.dtz.startDateProp(aItem)];
-    let end = aItem[cal.dtz.endDateProp(aItem)];
-    let kDefaultTimezone = cal.dtz.defaultTimezone;
-    // Check for tasks without start and/or due date
-    if (start) {
-      start = start.getInTimezone(kDefaultTimezone);
-    }
-    if (end) {
-      end = end.getInTimezone(kDefaultTimezone);
-    }
-    // EndDate is exclusive. For all-day events, we need to subtract one day,
-    // to get into a format that's understandable.
-    if (start && start.isDate && end) {
-      end.day -= 1;
-    }
+  /**
+   * Format an interval that is defined by an item with the default timezone.
+   *
+   * @param {calIItemBase} aItem      The item describing the interval.
+   * @return {string}                 The formatted item interval.
+   */
+  formatItemInterval(aItem) {
+    return this.formatInterval(...getItemDates(aItem));
+  },
 
-    return [start, end];
+  /**
+   * Format a time interval like formatItemInterval, but only show times.
+   *
+   * @param {calIItemBase} aItem      The item describing the interval.
+   * @return {string}                 The formatted item interval.
+   */
+  formatItemTimeInterval(aItem) {
+    return this.formatTimeInterval(...getItemDates(aItem));
   },
 
-  formatItemInterval(aItem) {
-    return this.formatInterval(...this._getItemDates(aItem));
+  /**
+   * Get the month name.
+   *
+   * @param {number} aMonthIndex      Zero-based month number (0 is january, 11 is december).
+   * @return {string}                 The month name in the current locale.
+   */
+  monthName(aMonthIndex) {
+    let oneBasedMonthIndex = aMonthIndex + 1;
+    return gDateStringBundle.GetStringFromName("month." + oneBasedMonthIndex + ".name");
+  },
+
+  /**
+   * Get the abbreviation of the month name.
+   *
+   * @param {number} aMonthIndex      Zero-based month number (0 is january, 11 is december).
+   * @return {string}                 The abbreviated month name in the current locale.
+   */
+  shortMonthName(aMonthIndex) {
+    let oneBasedMonthIndex = aMonthIndex + 1;
+    return gDateStringBundle.GetStringFromName("month." + oneBasedMonthIndex + ".Mmm");
+  },
+
+  /**
+   * Get the day name.
+   *
+   * @param {number} aMonthIndex      Zero-based day number (0 is sunday, 6 is saturday).
+   * @return {string}                 The day name in the current locale.
+   */
+  dayName(aDayIndex) {
+    let oneBasedDayIndex = aDayIndex + 1;
+    return gDateStringBundle.GetStringFromName("day." + oneBasedDayIndex + ".name");
   },
 
-  formatItemTimeInterval(aItem) {
-    return this.formatTimeInterval(...this._getItemDates(aItem));
-  },
-
-  monthName(aMonthIndex) {
-    let oneBasedMonthIndex = aMonthIndex + 1;
-    return this.mDateStringBundle.GetStringFromName("month." + oneBasedMonthIndex + ".name");
-  },
-
-  shortMonthName(aMonthIndex) {
-    let oneBasedMonthIndex = aMonthIndex + 1;
-    return this.mDateStringBundle.GetStringFromName("month." + oneBasedMonthIndex + ".Mmm");
-  },
-
-  dayName(aDayIndex) {
-    let oneBasedDayIndex = aDayIndex + 1;
-    return this.mDateStringBundle.GetStringFromName("day." + oneBasedDayIndex + ".name");
-  },
-
+  /**
+   * Get the abbreviation of the day name.
+   *
+   * @param {number} aMonthIndex      Zero-based day number (0 is sunday, 6 is saturday).
+   * @return {string}                 The abbrevidated day name in the current locale.
+   */
   shortDayName(aDayIndex) {
     let oneBasedDayIndex = aDayIndex + 1;
-    return this.mDateStringBundle.GetStringFromName("day." + oneBasedDayIndex + ".Mmm");
+    return gDateStringBundle.GetStringFromName("day." + oneBasedDayIndex + ".Mmm");
   },
 };
+
+/**
+ * inTimezone returns a string with date formatted.
+ *
+ * @param  {calIDateTime} aDate    The date object holding the tz information.
+ * @param  {Object} aOptions       The Intl.DateTimeFormatter options object.
+ * @return {string}                The date as a string.
+ */
+function inTimezone(aDate, aOptions) {
+  let formatter = new Services.intl.DateTimeFormat(undefined, aOptions);
+
+  let timezone = aDate.timezone;
+  // We set the tz only if we have a valid tz - otherwise localtime will be used on formatting.
+  if (timezone && (timezone.isUTC || timezone.icalComponent)) {
+    aOptions.timeZone = timezone.tzid;
+    try {
+      formatter = new Services.intl.DateTimeFormat(undefined, aOptions);
+    } catch (ex) {
+      // Non-IANA timezones throw a RangeError.
+      cal.WARN(ex);
+    }
+  }
+
+  return formatter.format(cal.dtz.dateTimeToJsDate(aDate));
+}
+
+/**
+ * Helper to get the start/end dates for a given item.
+ *
+ * @param {calIItemBase} aItem              The item to get the dates for.
+ * @return {[calIDateTime, calIDateTime]}   An array with start and end date.
+ */
+function getItemDates(aItem) {
+  let start = aItem[cal.dtz.startDateProp(aItem)];
+  let end = aItem[cal.dtz.endDateProp(aItem)];
+  let kDefaultTimezone = cal.dtz.defaultTimezone;
+  // Check for tasks without start and/or due date
+  if (start) {
+    start = start.getInTimezone(kDefaultTimezone);
+  }
+  if (end) {
+    end = end.getInTimezone(kDefaultTimezone);
+  }
+  // EndDate is exclusive. For all-day events, we need to subtract one day,
+  // to get into a format that's understandable.
+  if (start && start.isDate && end) {
+    end.day -= 1;
+  }
+
+  return [start, end];
+}
--- a/calendar/base/modules/utils/calDateTimeUtils.jsm
+++ b/calendar/base/modules/utils/calDateTimeUtils.jsm
@@ -378,8 +378,14 @@ var caldtz = {
           "calendar.timezone.recent",
           JSON.stringify(recentTimezones.map(zone => zone.tzid))
         );
       }
     }
     return recentTimezones;
   },
 };
+
+ChromeUtils.defineModuleGetter(
+  caldtz,
+  "formatter",
+  "resource:///modules/calendar/utils/calDateTimeFormatter.jsm"
+);
--- a/calendar/base/modules/utils/calPrintUtils.jsm
+++ b/calendar/base/modules/utils/calPrintUtils.jsm
@@ -118,44 +118,44 @@ var calprint = {
   /**
    * Get time interval string for the given item. Returns an empty string for all-day items.
    *
    * @param aItem     The item providing the interval
    * @return          The string describing the interval
    */
   getItemIntervalString(aItem, aBoxDate) {
     // omit time label for all-day items
+    let formatter = cal.dtz.formatter;
     let startDate = aItem[cal.dtz.startDateProp(aItem)];
     let endDate = aItem[cal.dtz.endDateProp(aItem)];
     if ((startDate && startDate.isDate) || (endDate && endDate.isDate)) {
       return "";
     }
 
     // check for tasks without start and/or due date
     if (!startDate || !endDate) {
-      return cal.getDateFormatter().formatItemTimeInterval(aItem);
+      return formatter.formatItemTimeInterval(aItem);
     }
 
-    let dateFormatter = cal.getDateFormatter();
     let defaultTimezone = cal.dtz.defaultTimezone;
     startDate = startDate.getInTimezone(defaultTimezone);
     endDate = endDate.getInTimezone(defaultTimezone);
     let start = startDate.clone();
     let end = endDate.clone();
     start.isDate = true;
     end.isDate = true;
     if (start.compare(end) == 0) {
       // Events that start and end in the same day.
-      return dateFormatter.formatTimeInterval(startDate, endDate);
+      return formatter.formatTimeInterval(startDate, endDate);
     }
     // Events that span two or more days.
     let compareStart = aBoxDate.compare(start);
     let compareEnd = aBoxDate.compare(end);
     if (compareStart == 0) {
-      return "\u21e4 " + dateFormatter.formatTime(startDate); // unicode '⇤'
+      return "\u21e4 " + formatter.formatTime(startDate); // unicode '⇤'
     } else if (compareStart > 0 && compareEnd < 0) {
       return "\u21ff"; // unicode '↔'
     } else if (compareEnd == 0) {
-      return "\u21e5 " + dateFormatter.formatTime(endDate); // unicode '⇥'
+      return "\u21e5 " + formatter.formatTime(endDate); // unicode '⇥'
     }
     return "";
   },
 };
deleted file mode 100644
--- a/calendar/base/public/calIDateTimeFormatter.idl
+++ /dev/null
@@ -1,162 +0,0 @@
-/* -*- Mode: idl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-interface calIDateTime;
-interface calIItemBase;
-
-[scriptable, uuid(69741510-5f5d-11e4-9803-0800200c9a66)]
-interface calIDateTimeFormatter : nsISupports
-{
-    /**
-     * Format a date in either short or long format, depending on the
-     * users preference
-     *
-     * @see
-     *     formatDateShort
-     *     formatDateLong
-     */
-    AString formatDate(in calIDateTime aDate);
-
-    /**
-     * Format a date into a short format, for example
-     * "12/17/2005"
-     *
-     * @param aDate
-     *      the datetime to format
-     * @returns
-     *      a string representing the date part of the datetime
-     */
-    AString formatDateShort(in calIDateTime aDate);
-
-    /**
-     * Format a date into a long format, for example
-     * "Sat Dec 17 2005"
-     *
-     * @param aDate
-     *      the datetime to format
-     * @returns
-     *      a string representing the date part of the datetime
-     */
-    AString formatDateLong(in calIDateTime aDate);
-
-    /**
-     * Format a date into a short format without mentioning the year, for
-     * example "Dec 17"
-     *
-     * @param aDate
-     *      the datetime to format
-     * @returns
-     *      a string representing the date part of the datetime
-     */
-    AString formatDateWithoutYear(in calIDateTime aDate);
-
-    /**
-     * Format a time into the format specified by the OS settings.
-     * Will omit the seconds from the output.
-     *
-     * @param aDate
-     *      the datetime to format
-     * @returns
-     *      a string representing the time part of the datetime
-     */
-    AString formatTime(in calIDateTime aDate);
-
-    /**
-     * Format a datetime into the format specified by the OS settings.
-     * Will omit the seconds from the output.
-     *
-     * @param aDateTime
-     *      the datetime to format
-     * @returns
-     *      a string representing the datetime
-     */
-    AString formatDateTime(in calIDateTime aDate);
-
-    /**
-     * Format a time interval that is defined by an item with the default
-     * timezone Internally it calls "formatInterval" after retrieving
-     * the start/entry and end/due date of the item.
-     *
-     * @param aItem
-     *      The item describing the interval
-     */
-    AUTF8String formatItemInterval(in calIItemBase aItem);
-
-    /**
-     * Format a time interval like formatItemInterval, but only show times.
-     *
-     * @param aItem     The item providing the interval
-     * @return          The string describing the interval
-     */
-     AUTF8String formatItemTimeInterval(in calIItemBase aItem);
-
-    /**
-     * Format a date/time interval. The returned string may assume that the
-     * dates are so close to each other, that it can leave out some parts of the
-     * part string denoting the end date.
-     *
-     * @param aStartDate        The start of the interval
-     * @param aEndDate          The end of the interval
-     * @return                  A String describing the interval in a legible form
-     */
-    AUTF8String formatInterval(in calIDateTime aStartDate,
-                               in calIDateTime aEndDate);
-
-    /**
-     * Format a time interval like formatInterval, but show only the time.
-     *
-     * @param aStartDate        The start of the interval.
-     * @param aEndDate          The end of the interval.
-     * @return                  The formatted time interval.
-     */
-    AUTF8String formatTimeInterval(in calIDateTime aStartTime,
-                                   in calIDateTime aEndTime);
-
-    /**
-     * Get the monthday followed by its ordinal symbol in the current locale.
-     * e.g.  monthday 1 -> 1st
-     *       monthday 2 -> 2nd etc.
-     *
-     * @param aMonthdayIndex
-     *      a number from 1 to 31
-     * @returns
-     *      the monthday number in ordinal format in the current locale
-     */
-    AUTF8String formatDayWithOrdinal(in unsigned long aMonthdayIndex);
-
-    /**
-     * Get the month name
-     *
-     * @param aMonthIndex
-     *     zero-based month number (0 is january, 11 is december)
-     * @returns
-     *      the month name in the current locale
-     */
-    AString monthName(in unsigned long aMonthIndex);
-
-    /**
-     * Get the abbreviation of the month name
-     *
-     * @see monthName
-     */
-    AString shortMonthName(in unsigned long aMonthIndex);
-
-    /**
-     * Get the day name
-     * @param aMonthIndex
-     *     zero-based month number (0 is sunday, 6 is saturday)
-     * @returns
-     *      the day name in the current locale
-     */
-    AString dayName(in unsigned long aDayIndex);
-
-    /**
-     * Get the abbreviation of the day name
-     * @see dayName
-     */
-    AString shortDayName(in unsigned long aDayIndex);
-};
--- a/calendar/base/public/moz.build
+++ b/calendar/base/public/moz.build
@@ -12,17 +12,16 @@ XPIDL_SOURCES += [
     'calICalendarACLManager.idl',
     'calICalendarManager.idl',
     'calICalendarProvider.idl',
     'calICalendarSearchProvider.idl',
     'calICalendarView.idl',
     'calICalendarViewController.idl',
     'calIChangeLog.idl',
     'calIDateTime.idl',
-    'calIDateTimeFormatter.idl',
     'calIDeletedItems.idl',
     'calIDuration.idl',
     'calIErrors.idl',
     'calIEvent.idl',
     'calIFreeBusyProvider.idl',
     'calIIcsParser.idl',
     'calIIcsSerializer.idl',
     'calIICSService.idl',
--- a/calendar/base/src/CalAlarm.jsm
+++ b/calendar/base/src/CalAlarm.jsm
@@ -608,19 +608,18 @@ CalAlarm.prototype = {
         return aPrefix + "Task";
       }
       return aPrefix;
     }
 
     if (this.related == ALARM_RELATED_ABSOLUTE && this.mAbsoluteDate) {
       // this is an absolute alarm. Use the calendar default timezone and
       // format it.
-      let formatter = cal.getDateFormatter();
       let formatDate = this.mAbsoluteDate.getInTimezone(cal.dtz.defaultTimezone);
-      return formatter.formatDateTime(formatDate);
+      return cal.dtz.formatter.formatDateTime(formatDate);
     } else if (this.related != ALARM_RELATED_ABSOLUTE && this.mOffset) {
       // Relative alarm length
       let alarmlen = Math.abs(this.mOffset.inSeconds / 60);
       if (alarmlen == 0) {
         // No need to get the other information if the alarm is at the start
         // of the event/task.
         if (this.related == ALARM_RELATED_START) {
           return cal.l10n.getString("calendar-alarms", alarmString("reminderTitleAtStart"));
--- a/calendar/base/src/CalIcsParser.jsm
+++ b/calendar/base/src/CalIcsParser.jsm
@@ -229,17 +229,17 @@ parserState.prototype = {
       let tzid = date.timezone.tzid;
       let hid = item.hashId + "#" + tzid;
       if (!(hid in this.tzErrors)) {
         // For now, publish errors to console and alert user.
         // In future, maybe make them available through an interface method
         // so this UI code can be removed from the parser, and caller can
         // choose whether to alert, or show user the problem items and ask
         // for fixes, or something else.
-        let msgArgs = [tzid, item.title, cal.getDateFormatter().formatDateTime(date)];
+        let msgArgs = [tzid, item.title, cal.dtz.formatter.formatDateTime(date)];
         let msg = cal.l10n.getCalString("unknownTimezoneInItem", msgArgs);
 
         cal.ERROR(msg + "\n" + item.icalString);
         this.tzErrors[hid] = true;
       }
     }
   },
 
--- a/calendar/base/src/components.conf
+++ b/calendar/base/src/components.conf
@@ -62,22 +62,16 @@ Classes = [
   },
   {
     'cid': '{f5f743cd-8997-428e-bc1b-644e73f61203}',
     'contract_ids': ['@mozilla.org/calendar/calendarsearch-service;1'],
     'jsm': 'resource:///modules/CalCalendarSearchService.jsm',
     'constructor': 'CalCalendarSearchService',
   },
   {
-    'cid': '{4123da9a-f047-42da-a7d0-cc4175b9f36a}',
-    'contract_ids': ['@mozilla.org/calendar/datetime-formatter;1'],
-    'jsm': 'resource:///modules/CalDateTimeFormatter.jsm',
-    'constructor': 'CalDateTimeFormatter',
-  },
-  {
     'cid': '{8e6799af-e7e9-4e6c-9a82-a2413e86d8c3}',
     'contract_ids': ['@mozilla.org/calendar/deleted-items-manager;1'],
     'jsm': 'resource:///modules/CalDeletedItems.jsm',
     'constructor': 'CalDeletedItems',
     'categories': {'profile-after-change': 'deleted-items-manager'},
   },
   {
     'cid': '{974339d5-ab86-4491-aaaf-2b2ca177c12b}',
--- a/calendar/base/src/moz.build
+++ b/calendar/base/src/moz.build
@@ -12,17 +12,16 @@ XPIDL_MODULE = 'calbaseinternal'
 EXTRA_JS_MODULES += [
     'CalAlarm.jsm',
     'CalAlarmMonitor.jsm',
     'CalAlarmService.jsm',
     'CalAttachment.jsm',
     'CalAttendee.jsm',
     'CalCalendarManager.jsm',
     'CalCalendarSearchService.jsm',
-    'CalDateTimeFormatter.jsm',
     'CalDefaultACLManager.jsm',
     'CalDeletedItems.jsm',
     'CalEvent.jsm',
     'CalFreeBusyService.jsm',
     'CalIcsParser.jsm',
     'CalIcsSerializer.jsm',
     'CalItipItem.jsm',
     'CalProtocolHandler.jsm',
--- a/calendar/import-export/CalHtmlExport.jsm
+++ b/calendar/import-export/CalHtmlExport.jsm
@@ -72,17 +72,17 @@ CalHtmlExporter.prototype = {
       let startDate = item[cal.dtz.startDateProp(item)];
       let endDate = item[cal.dtz.endDateProp(item)];
       if (startDate || endDate) {
         // This is a task with a start or due date, format accordingly
         let prefixWhen = cal.l10n.getCalString("htmlPrefixWhen");
         itemNode.querySelector(".intervalkey").textContent = prefixWhen;
 
         let startNode = itemNode.querySelector(".dtstart");
-        let dateString = cal.getDateFormatter().formatItemInterval(item);
+        let dateString = cal.dtz.formatter.formatItemInterval(item);
         startNode.setAttribute("title", startDate ? startDate.icalString : "none");
         startNode.textContent = dateString;
       } else {
         let row = itemNode.querySelector(".intervalrow");
         row.remove();
         if (
           row.nextSibling &&
           (row.nextSibling.nodeType == row.nextSibling.TEXT_NODE ||
--- a/calendar/import-export/CalWeekPrinter.jsm
+++ b/calendar/import-export/CalWeekPrinter.jsm
@@ -113,17 +113,16 @@ CalWeekPrinter.prototype = {
 
     // Clone the template week and make sure it doesn't have an id
     let currentPage = weekTemplate.cloneNode(true);
     currentPage.removeAttribute("id");
     currentPage.item = startOfWeek.clone();
 
     // Set up the week number title
     let weekInfo = cal.getWeekInfoService();
-    let dateFormatter = cal.getDateFormatter();
     let weekno = weekInfo.getWeekTitle(startOfWeek);
     let weekTitle = cal.l10n.getCalString("WeekTitle", [weekno]);
     currentPage.querySelector(".week-number").textContent = weekTitle;
 
     // Set up the day boxes
     let endOfWeek = weekInfo.getEndOfWeek(startOfWeek);
     for (
       let currentDate = startOfWeek.clone();
@@ -133,17 +132,17 @@ CalWeekPrinter.prototype = {
       let weekday = currentDate.weekday;
       let weekdayName = weekdayMap[weekday];
       let dayOffPrefName = "calendar.week.d" + weekday + weekdayName + "soff";
       dayTable[cal.print.getDateKey(currentDate)] = currentPage.querySelector(
         "." + weekdayName + "-container"
       );
 
       let titleNode = currentPage.querySelector("." + weekdayName + "-title");
-      titleNode.textContent = dateFormatter.formatDateLong(
+      titleNode.textContent = cal.dtz.formatter.formatDateLong(
         currentDate.getInTimezone(defaultTimezone)
       );
 
       if (Services.prefs.getBoolPref(dayOffPrefName, false)) {
         let daysOffNode = currentPage.querySelector("." + weekdayName + "-box");
         daysOffNode.className += " day-off";
       }
     }
--- a/calendar/lightning/content/lightning-item-iframe.js
+++ b/calendar/lightning/content/lightning-item-iframe.js
@@ -4297,18 +4297,17 @@ function lookupCounterLabel(aProperty) {
  */
 function formatCounterValue(aProperty) {
   const dateProps = ["DTSTART", "DTEND"];
   const stringProps = ["SUMMARY", "LOCATION"];
 
   let val;
   if (dateProps.includes(aProperty.property)) {
     let localTime = aProperty.proposed.getInTimezone(cal.dtz.defaultTimezone);
-    let formatter = cal.getDateFormatter();
-    val = formatter.formatDateTime(localTime);
+    val = cal.dtz.formatter.formatDateTime(localTime);
     if (gTimezonesEnabled) {
       let tzone = localTime.timezone.displayName || localTime.timezone.tzid;
       val += " " + tzone;
     }
   } else if (stringProps.includes(aProperty.property)) {
     val = aProperty.proposed;
   } else {
     cal.LOG(
--- a/calendar/lightning/modules/ltnInvitationUtils.jsm
+++ b/calendar/lightning/modules/ltnInvitationUtils.jsm
@@ -72,17 +72,17 @@ ltn.invitation = {
    *
    * @param  {calIItemBase} aEvent     The event to parse into html.
    * @param  {calItipItem}  aItipItem  The itip item, which contains aEvent.
    * @return {DOM}                     The html representation of aEvent.
    */
   createInvitationOverlay(aEvent, aItipItem) {
     // Creates HTML using the Node strings in the properties file
     let doc = cal.xml.parseFile("chrome://lightning/content/lightning-invitation.xhtml");
-    let formatter = cal.getDateFormatter();
+    let formatter = cal.dtz.formatter;
 
     let linkConverter = Cc["@mozilla.org/txttohtmlconv;1"].getService(Ci.mozITXTToHTMLConv);
 
     let field = function(aField, aContentText, aConvert) {
       let descr = doc.getElementById("imipHtml-" + aField + "-descr");
       if (descr) {
         let labelText = cal.l10n.getLtnString("imipHtml." + aField);
         descr.textContent = labelText;
--- a/calendar/resources/content/datetimepickers/datetimepickers.js
+++ b/calendar/resources/content/datetimepickers/datetimepickers.js
@@ -1494,16 +1494,16 @@
     amRegExp = new RegExp("^(?:" + amExpr + ")$");
     pmRegExp = new RegExp("^(?:" + pmExpr + ")$");
   }
 
   function formatDate(aDate, aTimezone) {
     // Usually, floating is ok here, so no need to pass aTimezone - we just need to pass
     // it in if we need to make sure formatting happens without a timezone conversion.
     let timezone = aTimezone || cal.dtz.floating;
-    return cal.getDateFormatter().formatDateShort(cal.dtz.jsDateToDateTime(aDate, timezone));
+    return cal.dtz.formatter.formatDateShort(cal.dtz.jsDateToDateTime(aDate, timezone));
   }
 
   function formatTime(aValue) {
     let formatter = new Services.intl.DateTimeFormat(undefined, { timeStyle: "short" });
     return formatter.format(aValue);
   }
 }
--- a/calendar/resources/content/mouseoverPreviews.js
+++ b/calendar/resources/content/mouseoverPreviews.js
@@ -321,30 +321,30 @@ function boxAppendBody(box, textString, 
  * and to header table append a row containing localized Label: date.
  *
  * @param {Node}         box            The node to add the date label to
  * @param {String}       labelProperty  The label
  * @param {calIDateTime} date           The datetime object to format and add
  */
 function boxAppendLabeledDateTime(box, labelProperty, date) {
   date = date.getInTimezone(cal.dtz.defaultTimezone);
-  let formattedDateTime = cal.getDateFormatter().formatDateTime(date);
+  let formattedDateTime = cal.dtz.formatter.formatDateTime(date);
   boxAppendLabeledText(box, labelProperty, formattedDateTime);
 }
 
 /**
  * PRIVATE: Use dateFormatter to format date and time interval,
  * and to header table append a row containing localized Label: interval.
  *
  * @param box               contains header table.
  * @param labelProperty     name of property for localized field label.
  * @param item              the event or task
  */
 function boxAppendLabeledDateTimeInterval(box, labelProperty, item) {
-  let dateString = cal.getDateFormatter().formatItemInterval(item);
+  let dateString = cal.dtz.formatter.formatItemInterval(item);
   boxAppendLabeledText(box, labelProperty, dateString);
 }
 
 /**
  * PRIVATE: create empty 2-column table for header fields, and append it to box.
  *
  * @param  {Node}  box  The node to create a column table for
  */
--- a/calendar/test/browser/browser_basicFunctionality.js
+++ b/calendar/test/browser/browser_basicFunctionality.js
@@ -54,17 +54,17 @@ add_task(function testBasicFunctionality
   // There should be search field.
   controller.assertNode(eid("unifinder-search-field"));
 
   switchToView(controller, "day");
 
   // Default view is day view which should have 09:00 label and box.
   let someTime = cal.createDateTime();
   someTime.resetTo(someTime.year, someTime.month, someTime.day, 9, 0, 0, someTime.timezone);
-  let label = cal.getDateFormatter().formatTime(someTime);
+  let label = cal.dtz.formatter.formatTime(someTime);
   controller.assertNode(
     lookup(`
         ${DAY_VIEW}/{"class":"mainbox"}/{"class":"scrollbox"}/
         {"class":"timebar"}/{"class":"timebarboxstack"}/{"class":"topbox"}/[9]/
         {"class":"calendar-time-bar-label","value":"${label}"}
     `)
   );
   controller.assertNode(
--- a/calendar/test/browser/browser_eventDisplay.js
+++ b/calendar/test/browser/browser_eventDisplay.js
@@ -7,17 +7,17 @@ var { CALENDARNAME, createCalendar, dele
 );
 
 var mozmill = ChromeUtils.import("resource://testing-common/mozmill/mozmill.jsm");
 var controller = mozmill.getMail3PaneController();
 
 var calendarId = createCalendar(controller, CALENDARNAME);
 var calendar = cal.async.promisifyCalendar(cal.getCalendarManager().getCalendarById(calendarId));
 
-let formatter = cal.getDateFormatter();
+let formatter = cal.dtz.formatter;
 let startTime = formatter.formatTime(cal.createDateTime("20190403T123400"));
 let endTime = formatter.formatTime(cal.createDateTime("20190403T234500"));
 
 Services.prefs.setIntPref("calendar.week.start", 1);
 
 /**
  * Test an event that occurs within one day, in the day view.
  */
--- a/calendar/test/browser/browser_todayPane.js
+++ b/calendar/test/browser/browser_todayPane.js
@@ -101,17 +101,17 @@ add_task(async function testTodayPane() 
   let listChildren = agendaListbox.agendaListboxControl.children;
   Assert.equal(listChildren.length, 7);
   Assert.equal(listChildren[0].localName, "menupopup");
   Assert.equal(listChildren[1].id, "today-header");
   Assert.equal(listChildren[3].id, "tomorrow-header");
   Assert.equal(listChildren[5].id, "nextweek-header");
 
   // Verify events shown in today pane.
-  let dateFormatter = cal.getDateFormatter();
+  let dateFormatter = cal.dtz.formatter;
 
   let startString = dateFormatter.formatTime(todaysEvent.startDate, cal.dtz.defaultTimezone);
   Assert.equal(
     listChildren[2].querySelector(".agenda-event-start").textContent,
     `${startString} Today's Event`
   );
 
   startString = dateFormatter.formatTime(tomorrowsEvent.startDate, cal.dtz.defaultTimezone);
--- a/calendar/test/browser/eventDialog/browser_eventDialog.js
+++ b/calendar/test/browser/eventDialog/browser_eventDialog.js
@@ -36,17 +36,16 @@ var { eid, lookup, lookupEventBox } = he
 const EVENTTITLE = "Event";
 const EVENTLOCATION = "Location";
 const EVENTDESCRIPTION = "Event Description";
 const EVENTATTENDEE = "foo@bar.com";
 const EVENTURL = "http://mozilla.org/";
 var firstDay;
 
 add_task(async function testEventDialog() {
-  let dateFormatter = cal.getDateFormatter();
   let now = new Date();
 
   createCalendar(controller, CALENDARNAME);
   // Since from other tests we may be elsewhere, make sure we start today.
   switchToView(controller, "day");
   goToDate(controller, now.getFullYear(), now.getMonth() + 1, now.getDate());
   viewBack(controller, 1);
 
@@ -57,27 +56,27 @@ add_task(async function testEventDialog(
 
   // Setup start- & endTime.
   // Next full hour except last hour of the day.
   let hour = now.getHours();
   let startHour = hour == 23 ? hour : (hour + 1) % 24;
 
   let nextHour = cal.dtz.now();
   nextHour.resetTo(firstDay.year, firstDay.month, firstDay.day, startHour, 0, 0, cal.dtz.floating);
-  let startTime = dateFormatter.formatTime(nextHour);
+  let startTime = cal.dtz.formatter.formatTime(nextHour);
   nextHour.resetTo(
     firstDay.year,
     firstDay.month,
     firstDay.day,
     (startHour + 1) % 24,
     0,
     0,
     cal.dtz.floating
   );
-  let endTime = dateFormatter.formatTime(nextHour);
+  let endTime = cal.dtz.formatter.formatTime(nextHour);
 
   // Create new event on first day in view.
   controller.click(lookupEventBox("month", CANVAS_BOX, 1, 1, null));
   controller.mainMenu.click("#calendar-new-event-menuitem");
 
   await invokeEventDialog(controller, null, async (event, iframe) => {
     let { eid: eventid } = helpersForController(event);
     let { eid: iframeId } = helpersForController(iframe);
@@ -222,17 +221,17 @@ function checkTooltip(row, col, startTim
   let eventName = lookup(`${toolTipTable}/[0]/[1]`);
   controller.assert(() => eventName.getNode().textContent == EVENTTITLE);
 
   // Check date and time.
   let dateTime = lookup(`${toolTipTable}/[2]/[1]`);
 
   let currDate = firstDay.clone();
   currDate.addDuration(cal.createDuration(`P${7 * (row - 1) + (col - 1)}D`));
-  let startDate = cal.getDateFormatter().formatDate(currDate);
+  let startDate = cal.dtz.formatter.formatDate(currDate);
 
   controller.assert(() => {
     let text = dateTime.getNode().textContent;
     dump(`${text} / ${startDate} ${startTime} -\n`);
     return text.includes(`${startDate} ${startTime} – `);
   });
 
   // This could be on the next day if it is 00:00.
--- a/calendar/test/browser/recurrence/browser_weeklyUntil.js
+++ b/calendar/test/browser/recurrence/browser_weeklyUntil.js
@@ -144,19 +144,17 @@ function setRecurrence(recurrence) {
   // Set until date.
   recurrence.radio(recid("recurrence-range-until"));
 
   // Delete previous date.
   let untilInput = reclookup(REC_DLG_UNTIL_INPUT);
   recurrence.keypress(untilInput, "a", { accelKey: true });
   recurrence.keypress(untilInput, "VK_DELETE", {});
 
-  let dateFormatter = cal.getDateFormatter();
-
-  let endDateString = dateFormatter.formatDateShort(
+  let endDateString = cal.dtz.formatter.formatDateShort(
     cal.dtz.jsDateToDateTime(ENDDATE, cal.dtz.floating)
   );
   recsleep(SHORT_SLEEP);
   recurrence.type(untilInput, endDateString);
 
   recsleep(SHORT_SLEEP);
   // Move focus to ensure the date is selected.
   recurrence.keypress(untilInput, "VK_TAB", {});
--- a/calendar/test/browser/views/browser_dayView.js
+++ b/calendar/test/browser/views/browser_dayView.js
@@ -29,18 +29,16 @@ var { cal } = ChromeUtils.import("resour
 var controller = mozmill.getMail3PaneController();
 var { lookup, lookupEventBox } = helpersForController(controller);
 
 const TITLE1 = "Day View Event";
 const TITLE2 = "Day View Event Changed";
 const DESC = "Day View Event Description";
 
 add_task(async function testDayView() {
-  let dateFormatter = cal.getDateFormatter();
-
   createCalendar(controller, CALENDARNAME);
   switchToView(controller, "day");
   goToDate(controller, 2009, 1, 1);
 
   // Verify date in view.
   let day = lookup(`${DAY_VIEW}/${LABELDAYBOX}/{"flex":"1"}`);
   controller.waitFor(() => day.getNode().mDate.icalString == "20090101");
 
@@ -52,18 +50,18 @@ add_task(async function testDayView() {
 
     let startTimeInput = getDateTimePicker("STARTTIME");
     let startDateInput = getDateTimePicker("STARTDATE");
 
     // Check that the start time is correct.
     let someDate = cal.createDateTime();
     someDate.resetTo(2009, 0, 1, 8, 0, 0, cal.dtz.floating);
     event.waitForElement(startTimeInput);
-    event.assertValue(startTimeInput, dateFormatter.formatTime(someDate));
-    event.assertValue(startDateInput, dateFormatter.formatDateShort(someDate));
+    event.assertValue(startTimeInput, cal.dtz.formatter.formatTime(someDate));
+    event.assertValue(startDateInput, cal.dtz.formatter.formatDateShort(someDate));
 
     // Fill in title, description and calendar.
     await setData(event, iframe, {
       title: TITLE1,
       description: DESC,
       calendar: CALENDARNAME,
     });
 
--- a/calendar/test/browser/views/browser_monthView.js
+++ b/calendar/test/browser/views/browser_monthView.js
@@ -27,18 +27,16 @@ var { cal } = ChromeUtils.import("resour
 var controller = mozmill.getMail3PaneController();
 var { lookup, lookupEventBox } = helpersForController(controller);
 
 const TITLE1 = "Month View Event";
 const TITLE2 = "Month View Event Changed";
 const DESC = "Month View Event Description";
 
 add_task(async function testMonthView() {
-  let dateFormatter = cal.getDateFormatter();
-
   createCalendar(controller, CALENDARNAME);
   switchToView(controller, "month");
   goToDate(controller, 2009, 1, 1);
 
   // Verify date.
   let day = lookup(`
         ${MONTH_VIEW}/{"class":"mainbox"}/{"class":"monthgrid"}/[0]/{"selected":"true"}/[0]
     `);
@@ -56,18 +54,18 @@ add_task(async function testMonthView() 
     let startDateInput = getDateTimePicker("STARTDATE");
 
     // Check that the start time is correct.
     // Next full hour except last hour hour of the day.
     let nextHour = hour == 23 ? hour : (hour + 1) % 24;
     let someDate = cal.dtz.now();
     someDate.resetTo(2009, 0, 1, nextHour, 0, 0, cal.dtz.floating);
     event.waitForElement(startTimeInput);
-    event.assertValue(startTimeInput, dateFormatter.formatTime(someDate));
-    event.assertValue(startDateInput, dateFormatter.formatDateShort(someDate));
+    event.assertValue(startTimeInput, cal.dtz.formatter.formatTime(someDate));
+    event.assertValue(startDateInput, cal.dtz.formatter.formatDateShort(someDate));
 
     // Fill in title, description and calendar.
     await setData(event, iframe, {
       title: TITLE1,
       description: DESC,
       calendar: CALENDARNAME,
     });
 
--- a/calendar/test/browser/views/browser_multiweekView.js
+++ b/calendar/test/browser/views/browser_multiweekView.js
@@ -27,18 +27,16 @@ var { cal } = ChromeUtils.import("resour
 var controller = mozmill.getMail3PaneController();
 var { lookup, lookupEventBox } = helpersForController(controller);
 
 const TITLE1 = "Multiweek View Event";
 const TITLE2 = "Multiweek View Event Changed";
 const DESC = "Multiweek View Event Description";
 
 add_task(async function setupModule(module) {
-  let dateFormatter = cal.getDateFormatter();
-
   createCalendar(controller, CALENDARNAME);
   switchToView(controller, "multiweek");
   goToDate(controller, 2009, 1, 1);
 
   // Verify date.
   let day = lookup(`
         ${MULTIWEEK_VIEW}/{"class":"mainbox"}/{"class":"monthgrid"}/[0]/{"selected":"true"}/[0]
     `);
@@ -56,18 +54,18 @@ add_task(async function setupModule(modu
     let startDateInput = getDateTimePicker("STARTDATE");
 
     // Check that the start time is correct.
     // Next full hour except last hour hour of the day.
     let nextHour = hour == 23 ? hour : (hour + 1) % 24;
     let someDate = cal.dtz.now();
     someDate.resetTo(2009, 0, 1, nextHour, 0, 0, cal.dtz.floating);
     event.waitForElement(startTimeInput);
-    event.assertValue(startTimeInput, dateFormatter.formatTime(someDate));
-    event.assertValue(startDateInput, dateFormatter.formatDateShort(someDate));
+    event.assertValue(startTimeInput, cal.dtz.formatter.formatTime(someDate));
+    event.assertValue(startDateInput, cal.dtz.formatter.formatDateShort(someDate));
 
     // Fill in title, description and calendar.
     await setData(event, iframe, {
       title: TITLE1,
       description: DESC,
       calendar: CALENDARNAME,
     });
 
--- a/calendar/test/browser/views/browser_weekView.js
+++ b/calendar/test/browser/views/browser_weekView.js
@@ -28,18 +28,16 @@ var { cal } = ChromeUtils.import("resour
 var controller = mozmill.getMail3PaneController();
 var { lookup, lookupEventBox } = helpersForController(controller);
 
 var TITLE1 = "Week View Event";
 var TITLE2 = "Week View Event Changed";
 var DESC = "Week View Event Description";
 
 add_task(async function testWeekView() {
-  let dateFormatter = cal.getDateFormatter();
-
   createCalendar(controller, CALENDARNAME);
   switchToView(controller, "week");
   goToDate(controller, 2009, 1, 1);
 
   // Verify date.
   let day = lookup(`
         ${WEEK_VIEW}/{"class":"mainbox"}/{"class":"headerbox"}/
         {"class":"headerdaybox"}/{"selected":"true"}
@@ -55,18 +53,18 @@ add_task(async function testWeekView() {
 
     let startTimeInput = getDateTimePicker("STARTTIME");
     let startDateInput = getDateTimePicker("STARTDATE");
 
     // Check that the start time is correct.
     event.waitForElement(startTimeInput);
     let someDate = cal.createDateTime();
     someDate.resetTo(2009, 0, 1, 8, 0, 0, cal.dtz.floating);
-    event.assertValue(startTimeInput, dateFormatter.formatTime(someDate));
-    event.assertValue(startDateInput, dateFormatter.formatDateShort(someDate));
+    event.assertValue(startTimeInput, cal.dtz.formatter.formatTime(someDate));
+    event.assertValue(startDateInput, cal.dtz.formatter.formatDateShort(someDate));
 
     // Fill in title, description and calendar.
     await setData(event, iframe, {
       title: TITLE1,
       description: DESC,
       calendar: CALENDARNAME,
     });
 
--- a/calendar/test/modules/ItemEditingHelpers.jsm
+++ b/calendar/test/modules/ItemEditingHelpers.jsm
@@ -215,17 +215,17 @@ async function setData(dialog, iframe, d
   let startdateInput = getDateTimePicker("STARTDATE");
   let enddateInput = getDateTimePicker("ENDDATE");
   let starttimeInput = getDateTimePicker("STARTTIME");
   let endtimeInput = getDateTimePicker("ENDTIME");
   let completeddateInput = getDateTimePicker("COMPLETEDDATE");
   let percentCompleteInput = iframeLookup(PERCENT_COMPLETE_INPUT);
   let untilDateInput = getDateTimePicker("UNTILDATE");
 
-  let dateFormatter = cal.getDateFormatter();
+  let dateFormatter = cal.dtz.formatter;
   // Wait for input elements' values to be populated.
   await sleep(iframe.window);
 
   // title
   if (data.title !== undefined) {
     let titleInput = iframeid("item-title");
     replaceText(titleInput, data.title);
   }
--- a/calendar/test/unit/test_datetimeformatter.js
+++ b/calendar/test/unit/test_datetimeformatter.js
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { formatter } = cal.dtz;
 
 function run_test() {
   do_calendar_startup(run_next_test);
 }
 
 // This test assumes the timezone of your system is not set to Pacific/Fakaofo or equivalent.
 
 // Time format is platform dependent, so we use alternative result sets here in 'expected'.
@@ -48,20 +49,17 @@ add_task(async function formatDate_test(
   let i = 0;
   for (let test of data) {
     i++;
     Services.prefs.setIntPref("calendar.date.format", test.input.dateformat);
     let zone =
       test.input.timezone == "floating" ? cal.dtz.floating : tzs.getTimezone(test.input.timezone);
     let date = cal.createDateTime(test.input.datetime).getInTimezone(zone);
 
-    let dtFormatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-      Ci.calIDateTimeFormatter
-    );
-    let formatted = dtFormatter.formatDate(date);
+    let formatted = formatter.formatDate(date);
     ok(
       test.expected.includes(formatted),
       "(test #" + i + ": result '" + formatted + "', expected '" + test.expected + "')"
     );
   }
   // let's reset the preferences
   Services.prefs.setStringPref("calendar.timezone.local", tzlocal);
   Services.prefs.setIntPref("calendar.date.format", dateformat);
@@ -143,21 +141,17 @@ add_task(async function formatDateShort_
   let i = 0;
   for (let test of data) {
     i++;
 
     let zone =
       test.input.timezone == "floating" ? cal.dtz.floating : tzs.getTimezone(test.input.timezone);
     let date = cal.createDateTime(test.input.datetime).getInTimezone(zone);
 
-    let dtFormatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-      Ci.calIDateTimeFormatter
-    );
-
-    let formatted = dtFormatter.formatDateShort(date);
+    let formatted = formatter.formatDateShort(date);
     ok(
       test.expected.includes(formatted),
       "(test #" + i + ": result '" + formatted + "', expected '" + test.expected + "')"
     );
   }
   // let's reset the preferences
   Services.prefs.setStringPref("calendar.timezone.local", tzlocal);
   Services.prefs.setIntPref("calendar.date.format", dateformat);
@@ -239,21 +233,17 @@ add_task(async function formatDateLong_t
   let i = 0;
   for (let test of data) {
     i++;
 
     let zone =
       test.input.timezone == "floating" ? cal.dtz.floating : tzs.getTimezone(test.input.timezone);
     let date = cal.createDateTime(test.input.datetime).getInTimezone(zone);
 
-    let dtFormatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-      Ci.calIDateTimeFormatter
-    );
-
-    let formatted = dtFormatter.formatDateLong(date);
+    let formatted = formatter.formatDateLong(date);
     ok(
       test.expected.includes(formatted),
       "(test #" + i + ": result '" + formatted + "', expected '" + test.expected + "')"
     );
   }
   // let's reset the preferences
   Services.prefs.setStringPref("calendar.timezone.local", tzlocal);
   Services.prefs.setIntPref("calendar.date.format", dateformat);
@@ -335,21 +325,17 @@ add_task(async function formatDateWithou
   let i = 0;
   for (let test of data) {
     i++;
 
     let zone =
       test.input.timezone == "floating" ? cal.dtz.floating : tzs.getTimezone(test.input.timezone);
     let date = cal.createDateTime(test.input.datetime).getInTimezone(zone);
 
-    let dtFormatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-      Ci.calIDateTimeFormatter
-    );
-
-    equal(dtFormatter.formatDateWithoutYear(date), test.expected, "(test #" + i + ")");
+    equal(formatter.formatDateWithoutYear(date), test.expected, "(test #" + i + ")");
   }
   // let's reset the preferences
   Services.prefs.setStringPref("calendar.timezone.local", tzlocal);
   Services.prefs.setIntPref("calendar.date.format", dateformat);
   Services.prefs.setBoolPref("intl.regional_prefs.use_os_locales", useOsLocale);
 });
 
 add_task(async function formatTime_test() {
@@ -403,21 +389,17 @@ add_task(async function formatTime_test(
   let i = 0;
   for (let test of data) {
     i++;
 
     let zone =
       test.input.timezone == "floating" ? cal.dtz.floating : tzs.getTimezone(test.input.timezone);
     let date = cal.createDateTime(test.input.datetime).getInTimezone(zone);
 
-    let dtFormatter = Cc["@mozilla.org/calendar/datetime-formatter;1"].getService(
-      Ci.calIDateTimeFormatter
-    );
-
-    let formatted = dtFormatter.formatTime(date);
+    let formatted = formatter.formatTime(date);
     ok(
       test.expected.includes(formatted),
       "(test #" + i + ": result '" + formatted + "', expected '" + test.expected + "')"
     );
   }
   // let's reset the preferences
   Services.prefs.setStringPref("calendar.timezone.local", tzlocal);
   Services.prefs.setBoolPref("intl.regional_prefs.use_os_locales", useOsLocale);
--- a/calendar/test/unit/test_ltninvitationutils.js
+++ b/calendar/test/unit/test_ltninvitationutils.js
@@ -1236,17 +1236,17 @@ add_task(async function parseCounter_tes
         }
       });
     }
     item = item.join("\r\n");
     return createEventFromIcalString(item);
   };
 
   let formatDt = function(aDateTime) {
-    let datetime = cal.getDateFormatter().formatDateTime(aDateTime);
+    let datetime = cal.dtz.formatter.formatDateTime(aDateTime);
     return datetime + " " + aDateTime.timezone.displayName;
   };
 
   for (let i = 1; i <= data.length; i++) {
     let test = data[i - 1];
     let existingItem = getItem(test.input.existing);
     let proposedItem = getItem(test.input.proposed);
     let parsed = ltn.invitation.parseCounter(proposedItem, existingItem);
--- a/calendar/test/unit/test_recur.js
+++ b/calendar/test/unit/test_recur.js
@@ -35,17 +35,17 @@ function test_rules() {
     let end = createDate(2020, 0, 1);
     let recdates = event.recurrenceInfo.getOccurrenceDates(start, end, 0);
     let occurrences = event.recurrenceInfo.getOccurrences(start, end, 0);
 
     // Check number of items
     dump("Expected " + expected.length + " occurrences\n");
     dump("Got: " + recdates.map(x => x.toString()) + "\n");
     equal(recdates.length, expected.length);
-    let fmt = cal.getDateFormatter();
+    let fmt = cal.dtz.formatter;
 
     for (let i = 0; i < expected.length; i++) {
       // Check each date
       let expectedDate = cal.createDateTime(expected[i]);
       dump(
         "Expecting instance at " + expectedDate + "(" + fmt.dayName(expectedDate.weekday) + ")\n"
       );
       dump("Recdate:");