1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is OEone Calendar Code, released October 31st, 2001.
15 *
16 * The Initial Developer of the Original Code is
17 * OEone Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2001
19 * the Initial Developer. All Rights Reserved.
20 *
21 * Contributor(s):
22 * Garth Smedley <garths@oeone.com>
23 * Mike Potter <mikep@oeone.com>
24 * Eric Belhaire <belhaire@ief.u-psud.fr>
25 * Michiel van Leeuwen <mvl@exedo.nl>
26 * Matthew Willis <lilmatt@mozilla.com>
27 *
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
39 *
40 * ***** END LICENSE BLOCK ***** */
41
42 const nsIScriptableDateFormat = Components.interfaces.nsIScriptableDateFormat;
43
calDateTimeFormatter
44 function calDateTimeFormatter() {
45 var strBundleService =
46 Components.classes["@mozilla.org/intl/stringbundle;1"]
47 .getService(Components.interfaces.nsIStringBundleService);
48 this.mDateStringBundle = strBundleService.createBundle("chrome://calendar/locale/dateFormat.properties");
49
50 this.mDateService =
51 Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
52 .getService(nsIScriptableDateFormat);
53
54 // Do does the month or day come first in this locale?
55 this.mMonthFirst = false;
56
57 // If LONG FORMATTED DATE is same as short formatted date,
58 // then OS has poor extended/long date config, so use workaround.
59 this.mUseLongDateService = true;
60 var probeDate =
61 Components.classes["@mozilla.org/calendar/datetime;1"]
62 .createInstance(Components.interfaces.calIDateTime);
63 probeDate.timezone = UTC();
64 probeDate.year = 2002;
65 probeDate.month = 3;
66 probeDate.day = 5;
67 try {
68 // We're try/catching the calls to nsScriptableDateFormat since it's
69 // outside this module. We're also reusing probeDate rather than
70 // creating 3 discrete calDateTimes for performance.
71 var probeStringA = this.formatDateShort(probeDate);
72 var longProbeString = this.formatDateLong(probeDate);
73 probeDate.month = 4;
74 var probeStringB = this.formatDateShort(probeDate);
75 probeDate.month = 3;
76 probeDate.day = 6;
77 var probeStringC = this.formatDateShort(probeDate);
78
79 // Compare the index of the first differing character between
80 // probeStringA to probeStringB and probeStringA to probeStringC.
81 for (var i=0; i < probeStringA.length ; i++) {
82 if (probeStringA[i] != probeStringB[i]) {
83 this.mMonthFirst = true;
84 break;
85 } else if (probeStringA[i] != probeStringC[i]) {
86 this.mMonthFirst = false;
87 break;
88 }
89 }
90
91 // On Unix extended/long date format may be created using %Ex instead
92 // of %x. Some systems may not support it and return "Ex" or same as
93 // short string. In that case, don't use long date service, use a
94 // workaround hack instead.
95 if (longProbeString == null ||
96 longProbeString.length < 4 ||
97 longProbeString == probeStringA)
98 this.mUseLongDateService = false;
99 } catch (e) {
100 this.mUseLongDateService = false;
101 }
102
103 }
104
105 calDateTimeFormatter.prototype.QueryInterface =
QueryInterface
106 function QueryInterface(aIID) {
107 if (!aIID.equals(Components.interfaces.nsISupports) &&
108 !aIID.equals(Components.interfaces.calIDateTimeFormatter)) {
109 throw Components.results.NS_ERROR_NO_INTERFACE;
110 }
111
112 return this;
113 };
114
115 calDateTimeFormatter.prototype.formatDate =
formatDate
116 function formatDate(aDate) {
117 // Format the date using user's format preference (long or short)
118 var format;
119 var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
120 .getService(Components.interfaces.nsIPrefBranch);
121 try {
122 format = prefBranch.getIntPref("calendar.date.format");
123 } catch(e) {
124 format = 0;
125 }
126
127 if (format == 0)
128 return this.formatDateLong(aDate);
129 else
130 return this.formatDateShort(aDate);
131 };
132
133 calDateTimeFormatter.prototype.formatDateShort =
formatDateShort
134 function formatDateShort(aDate) {
135 return this.mDateService.FormatDate("",
136 nsIScriptableDateFormat.dateFormatShort,
137 aDate.year,
138 aDate.month + 1,
139 aDate.day);
140 };
141
142 calDateTimeFormatter.prototype.formatDateLong =
formatDateLong
143 function formatDateLong(aDate) {
144 if (this.mUseLongDateService) {
145 return this.mDateService.FormatDate("",
146 nsIScriptableDateFormat.dateFormatLong,
147 aDate.year,
148 aDate.month + 1,
149 aDate.day);
150 } else {
151 // HACK We are probably on Linux and want a string in long format.
152 // dateService.dateFormatLong on Linux may return a short string, so
153 // build our own.
154 return this.shortDayName(aDate.weekday) + " " +
155 aDate.day + " " +
156 this.shortMonthName(aDate.month) + " " +
157 aDate.year;
158 }
159 };
160
161 calDateTimeFormatter.prototype.formatDateWithoutYear =
formatDateWithoutYear
162 function formatDateWithoutYear(aDate) {
163 // Doing this the hard way, because nsIScriptableDateFormat doesn't
164 // have a way to not include the year.
165 if (this.mMonthFirst) {
166 return this.shortMonthName(aDate.month) + " " + aDate.day;
167 } else {
168 return aDate.day + " " + this.shortMonthName(aDate.month);
169 }
170 };
171
172
173 calDateTimeFormatter.prototype.formatTime =
formatTime
174 function formatTime(aDate) {
175 if (aDate.isDate)
176 return this.mDateStringBundle.GetStringFromName("AllDay");
177
178 return this.mDateService.FormatTime("",
179 nsIScriptableDateFormat.timeFormatNoSeconds,
180 aDate.hour,
181 aDate.minute,
182 0);
183 };
184
185 calDateTimeFormatter.prototype.formatDateTime =
formatDateTime
186 function formatDateTime(aDate) {
187 var formattedDate = this.formatDate(aDate);
188 var formattedTime = this.formatTime(aDate);
189
190 var timeBeforeDate;
191 var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
192 .getService(Components.interfaces.nsIPrefBranch);
193 try {
194 timeBeforeDate = prefBranch.getBoolPref("calendar.date.formatTimeBeforeDate");
195 } catch(e) {
196 timeBeforeDate = 0;
197 }
198
199 if (timeBeforeDate)
200 return formattedTime+" "+formattedDate;
201 else
202 return formattedDate+" "+formattedTime;
203 };
204
205 calDateTimeFormatter.prototype.formatInterval =
formatInterval
206 function formatInterval(aStartDate, aEndDate, aStartString, aEndString) {
207 // make sure start and end use the same timezone when formatting intervals:
208 var endDate = aEndDate.getInTimezone(aStartDate.timezone);
209 // EndDate is exclusive. For all-day events, we ened to substract one day,
210 // to get into a format that's understandable.
211 if (aStartDate.isDate) {
212 endDate.day -= 1;
213 }
214
215 var testdate = aStartDate.clone();
216 testdate.isDate = true;
217 var sameDay = (testdate.compare(endDate) == 0);
218
219 if (aStartDate.isDate) {
220 // All-day interval, so we should leave out the time part
221 if (sameDay) {
222 // Start and end on the same day: only give return the start
223 // date.
224 // "5 Jan 2006"
225 aStartString.value = this.formatDate(aStartDate);
226 aEndString.value = "";
227 } else {
228 // Spanning multiple days, return both dates
229 // "5 Jan 2006 - 7 Jan 2006"
230 aStartString.value = this.formatDate(aStartDate);
231 aEndString.value = this.formatDate(endDate);
232 }
233 } else {
234 // non-allday, so need to return date and time
235 if (sameDay) {
236 // End is on the same day as start, so we can leave out the
237 // end date (but still include end time)
238 // "5 Jan 2006 13:00 - 17:00"
239 aStartString.value = this.formatDate(aStartDate)+" "+this.formatTime(aStartDate);
240 aEndString.value = this.formatTime(endDate);
241 } else {
242 // Spanning multiple days, so need to include date and time
243 // for start and end
244 // "5 Jan 2006 13:00 - 7 Jan 2006 9:00"
245 aStartString.value = this.formatDateTime(aStartDate);
246 aEndString.value = this.formatDateTime(endDate);
247 }
248 }
249 return 1;
250 };
251
252 calDateTimeFormatter.prototype.monthName =
253 function monthName(aMonthIndex) {
254 var oneBasedMonthIndex = aMonthIndex + 1;
255 return this.mDateStringBundle.GetStringFromName("month." + oneBasedMonthIndex + ".name" );
256 };
257
258 calDateTimeFormatter.prototype.shortMonthName =
259 function shortMonthName(aMonthIndex) {
260 var oneBasedMonthIndex = aMonthIndex + 1;
261 return this.mDateStringBundle.GetStringFromName("month." + oneBasedMonthIndex + ".Mmm" );
262 };
263
264 calDateTimeFormatter.prototype.dayName =
monthName
265 function monthName(aDayIndex) {
266 var oneBasedDayIndex = aDayIndex + 1;
267 return this.mDateStringBundle.GetStringFromName("day." + oneBasedDayIndex + ".name" );
268 };
269
270 calDateTimeFormatter.prototype.shortDayName =
271 function shortDayName(aDayIndex) {
272 var oneBasedDayIndex = aDayIndex + 1;
273 return this.mDateStringBundle.GetStringFromName("day." + oneBasedDayIndex + ".Mmm" );
274 };