]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dojo/date", ["./has", "./_base/lang"], function(has, lang){ |
2 | // module: | |
3 | // dojo/date | |
4 | ||
5 | var date = { | |
6 | // summary: | |
7 | // Date manipulation utilities | |
8 | }; | |
9 | ||
10 | date.getDaysInMonth = function(/*Date*/dateObject){ | |
11 | // summary: | |
12 | // Returns the number of days in the month used by dateObject | |
13 | var month = dateObject.getMonth(); | |
14 | var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; | |
15 | if(month == 1 && date.isLeapYear(dateObject)){ return 29; } // Number | |
16 | return days[month]; // Number | |
17 | }; | |
18 | ||
19 | date.isLeapYear = function(/*Date*/dateObject){ | |
20 | // summary: | |
21 | // Determines if the year of the dateObject is a leap year | |
22 | // description: | |
23 | // Leap years are years with an additional day YYYY-02-29, where the | |
24 | // year number is a multiple of four with the following exception: If | |
25 | // a year is a multiple of 100, then it is only a leap year if it is | |
26 | // also a multiple of 400. For example, 1900 was not a leap year, but | |
27 | // 2000 is one. | |
28 | ||
29 | var year = dateObject.getFullYear(); | |
30 | return !(year%400) || (!(year%4) && !!(year%100)); // Boolean | |
31 | }; | |
32 | ||
33 | // FIXME: This is not localized | |
34 | date.getTimezoneName = function(/*Date*/dateObject){ | |
35 | // summary: | |
36 | // Get the user's time zone as provided by the browser | |
37 | // dateObject: | |
38 | // Needed because the timezone may vary with time (daylight savings) | |
39 | // description: | |
40 | // Try to get time zone info from toString or toLocaleString method of | |
41 | // the Date object -- UTC offset is not a time zone. See | |
42 | // http://www.twinsun.com/tz/tz-link.htm Note: results may be | |
43 | // inconsistent across browsers. | |
44 | ||
45 | var str = dateObject.toString(); // Start looking in toString | |
46 | var tz = ''; // The result -- return empty string if nothing found | |
47 | var match; | |
48 | ||
49 | // First look for something in parentheses -- fast lookup, no regex | |
50 | var pos = str.indexOf('('); | |
51 | if(pos > -1){ | |
52 | tz = str.substring(++pos, str.indexOf(')')); | |
53 | }else{ | |
54 | // If at first you don't succeed ... | |
55 | // If IE knows about the TZ, it appears before the year | |
56 | // Capital letters or slash before a 4-digit year | |
57 | // at the end of string | |
58 | var pat = /([A-Z\/]+) \d{4}$/; | |
59 | if((match = str.match(pat))){ | |
60 | tz = match[1]; | |
61 | }else{ | |
62 | // Some browsers (e.g. Safari) glue the TZ on the end | |
63 | // of toLocaleString instead of putting it in toString | |
64 | str = dateObject.toLocaleString(); | |
65 | // Capital letters or slash -- end of string, | |
66 | // after space | |
67 | pat = / ([A-Z\/]+)$/; | |
68 | if((match = str.match(pat))){ | |
69 | tz = match[1]; | |
70 | } | |
71 | } | |
72 | } | |
73 | ||
74 | // Make sure it doesn't somehow end up return AM or PM | |
75 | return (tz == 'AM' || tz == 'PM') ? '' : tz; // String | |
76 | }; | |
77 | ||
78 | // Utility methods to do arithmetic calculations with Dates | |
79 | ||
80 | date.compare = function(/*Date*/date1, /*Date?*/date2, /*String?*/portion){ | |
81 | // summary: | |
82 | // Compare two date objects by date, time, or both. | |
83 | // description: | |
84 | // Returns 0 if equal, positive if a > b, else negative. | |
85 | // date1: | |
86 | // Date object | |
87 | // date2: | |
88 | // Date object. If not specified, the current Date is used. | |
89 | // portion: | |
90 | // A string indicating the "date" or "time" portion of a Date object. | |
91 | // Compares both "date" and "time" by default. One of the following: | |
92 | // "date", "time", "datetime" | |
93 | ||
94 | // Extra step required in copy for IE - see #3112 | |
95 | date1 = new Date(+date1); | |
96 | date2 = new Date(+(date2 || new Date())); | |
97 | ||
98 | if(portion == "date"){ | |
99 | // Ignore times and compare dates. | |
100 | date1.setHours(0, 0, 0, 0); | |
101 | date2.setHours(0, 0, 0, 0); | |
102 | }else if(portion == "time"){ | |
103 | // Ignore dates and compare times. | |
104 | date1.setFullYear(0, 0, 0); | |
105 | date2.setFullYear(0, 0, 0); | |
106 | } | |
107 | ||
108 | if(date1 > date2){ return 1; } // int | |
109 | if(date1 < date2){ return -1; } // int | |
110 | return 0; // int | |
111 | }; | |
112 | ||
113 | date.add = function(/*Date*/date, /*String*/interval, /*int*/amount){ | |
114 | // summary: | |
115 | // Add to a Date in intervals of different size, from milliseconds to years | |
116 | // date: Date | |
117 | // Date object to start with | |
118 | // interval: | |
119 | // A string representing the interval. One of the following: | |
120 | // "year", "month", "day", "hour", "minute", "second", | |
121 | // "millisecond", "quarter", "week", "weekday" | |
122 | // amount: | |
123 | // How much to add to the date. | |
124 | ||
125 | var sum = new Date(+date); // convert to Number before copying to accomodate IE (#3112) | |
126 | var fixOvershoot = false; | |
127 | var property = "Date"; | |
128 | ||
129 | switch(interval){ | |
130 | case "day": | |
131 | break; | |
132 | case "weekday": | |
133 | //i18n FIXME: assumes Saturday/Sunday weekend, but this is not always true. see dojo/cldr/supplemental | |
134 | ||
135 | // Divide the increment time span into weekspans plus leftover days | |
136 | // e.g., 8 days is one 5-day weekspan / and two leftover days | |
137 | // Can't have zero leftover days, so numbers divisible by 5 get | |
138 | // a days value of 5, and the remaining days make up the number of weeks | |
139 | var days, weeks; | |
140 | var mod = amount % 5; | |
141 | if(!mod){ | |
142 | days = (amount > 0) ? 5 : -5; | |
143 | weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5); | |
144 | }else{ | |
145 | days = mod; | |
146 | weeks = parseInt(amount/5); | |
147 | } | |
148 | // Get weekday value for orig date param | |
149 | var strt = date.getDay(); | |
150 | // Orig date is Sat / positive incrementer | |
151 | // Jump over Sun | |
152 | var adj = 0; | |
153 | if(strt == 6 && amount > 0){ | |
154 | adj = 1; | |
155 | }else if(strt == 0 && amount < 0){ | |
156 | // Orig date is Sun / negative incrementer | |
157 | // Jump back over Sat | |
158 | adj = -1; | |
159 | } | |
160 | // Get weekday val for the new date | |
161 | var trgt = strt + days; | |
162 | // New date is on Sat or Sun | |
163 | if(trgt == 0 || trgt == 6){ | |
164 | adj = (amount > 0) ? 2 : -2; | |
165 | } | |
166 | // Increment by number of weeks plus leftover days plus | |
167 | // weekend adjustments | |
168 | amount = (7 * weeks) + days + adj; | |
169 | break; | |
170 | case "year": | |
171 | property = "FullYear"; | |
172 | // Keep increment/decrement from 2/29 out of March | |
173 | fixOvershoot = true; | |
174 | break; | |
175 | case "week": | |
176 | amount *= 7; | |
177 | break; | |
178 | case "quarter": | |
179 | // Naive quarter is just three months | |
180 | amount *= 3; | |
181 | // fallthrough... | |
182 | case "month": | |
183 | // Reset to last day of month if you overshoot | |
184 | fixOvershoot = true; | |
185 | property = "Month"; | |
186 | break; | |
187 | // case "hour": | |
188 | // case "minute": | |
189 | // case "second": | |
190 | // case "millisecond": | |
191 | default: | |
192 | property = "UTC"+interval.charAt(0).toUpperCase() + interval.substring(1) + "s"; | |
193 | } | |
194 | ||
195 | if(property){ | |
196 | sum["set"+property](sum["get"+property]()+amount); | |
197 | } | |
198 | ||
199 | if(fixOvershoot && (sum.getDate() < date.getDate())){ | |
200 | sum.setDate(0); | |
201 | } | |
202 | ||
203 | return sum; // Date | |
204 | }; | |
205 | ||
206 | date.difference = function(/*Date*/date1, /*Date?*/date2, /*String?*/interval){ | |
207 | // summary: | |
208 | // Get the difference in a specific unit of time (e.g., number of | |
209 | // months, weeks, days, etc.) between two dates, rounded to the | |
210 | // nearest integer. | |
211 | // date1: | |
212 | // Date object | |
213 | // date2: | |
214 | // Date object. If not specified, the current Date is used. | |
215 | // interval: | |
216 | // A string representing the interval. One of the following: | |
217 | // "year", "month", "day", "hour", "minute", "second", | |
218 | // "millisecond", "quarter", "week", "weekday" | |
219 | // | |
220 | // Defaults to "day". | |
221 | ||
222 | date2 = date2 || new Date(); | |
223 | interval = interval || "day"; | |
224 | var yearDiff = date2.getFullYear() - date1.getFullYear(); | |
225 | var delta = 1; // Integer return value | |
226 | ||
227 | switch(interval){ | |
228 | case "quarter": | |
229 | var m1 = date1.getMonth(); | |
230 | var m2 = date2.getMonth(); | |
231 | // Figure out which quarter the months are in | |
232 | var q1 = Math.floor(m1/3) + 1; | |
233 | var q2 = Math.floor(m2/3) + 1; | |
234 | // Add quarters for any year difference between the dates | |
235 | q2 += (yearDiff * 4); | |
236 | delta = q2 - q1; | |
237 | break; | |
238 | case "weekday": | |
239 | var days = Math.round(date.difference(date1, date2, "day")); | |
240 | var weeks = parseInt(date.difference(date1, date2, "week")); | |
241 | var mod = days % 7; | |
242 | ||
243 | // Even number of weeks | |
244 | if(mod == 0){ | |
245 | days = weeks*5; | |
246 | }else{ | |
247 | // Weeks plus spare change (< 7 days) | |
248 | var adj = 0; | |
249 | var aDay = date1.getDay(); | |
250 | var bDay = date2.getDay(); | |
251 | ||
252 | weeks = parseInt(days/7); | |
253 | mod = days % 7; | |
254 | // Mark the date advanced by the number of | |
255 | // round weeks (may be zero) | |
256 | var dtMark = new Date(date1); | |
257 | dtMark.setDate(dtMark.getDate()+(weeks*7)); | |
258 | var dayMark = dtMark.getDay(); | |
259 | ||
260 | // Spare change days -- 6 or less | |
261 | if(days > 0){ | |
262 | switch(true){ | |
263 | // Range starts on Sat | |
264 | case aDay == 6: | |
265 | adj = -1; | |
266 | break; | |
267 | // Range starts on Sun | |
268 | case aDay == 0: | |
269 | adj = 0; | |
270 | break; | |
271 | // Range ends on Sat | |
272 | case bDay == 6: | |
273 | adj = -1; | |
274 | break; | |
275 | // Range ends on Sun | |
276 | case bDay == 0: | |
277 | adj = -2; | |
278 | break; | |
279 | // Range contains weekend | |
280 | case (dayMark + mod) > 5: | |
281 | adj = -2; | |
282 | } | |
283 | }else if(days < 0){ | |
284 | switch(true){ | |
285 | // Range starts on Sat | |
286 | case aDay == 6: | |
287 | adj = 0; | |
288 | break; | |
289 | // Range starts on Sun | |
290 | case aDay == 0: | |
291 | adj = 1; | |
292 | break; | |
293 | // Range ends on Sat | |
294 | case bDay == 6: | |
295 | adj = 2; | |
296 | break; | |
297 | // Range ends on Sun | |
298 | case bDay == 0: | |
299 | adj = 1; | |
300 | break; | |
301 | // Range contains weekend | |
302 | case (dayMark + mod) < 0: | |
303 | adj = 2; | |
304 | } | |
305 | } | |
306 | days += adj; | |
307 | days -= (weeks*2); | |
308 | } | |
309 | delta = days; | |
310 | break; | |
311 | case "year": | |
312 | delta = yearDiff; | |
313 | break; | |
314 | case "month": | |
315 | delta = (date2.getMonth() - date1.getMonth()) + (yearDiff * 12); | |
316 | break; | |
317 | case "week": | |
318 | // Truncate instead of rounding | |
319 | // Don't use Math.floor -- value may be negative | |
320 | delta = parseInt(date.difference(date1, date2, "day")/7); | |
321 | break; | |
322 | case "day": | |
323 | delta /= 24; | |
324 | // fallthrough | |
325 | case "hour": | |
326 | delta /= 60; | |
327 | // fallthrough | |
328 | case "minute": | |
329 | delta /= 60; | |
330 | // fallthrough | |
331 | case "second": | |
332 | delta /= 1000; | |
333 | // fallthrough | |
334 | case "millisecond": | |
335 | delta *= date2.getTime() - date1.getTime(); | |
336 | } | |
337 | ||
338 | // Round for fractional values and DST leaps | |
339 | return Math.round(delta); // Number (integer) | |
340 | }; | |
341 | ||
342 | // Don't use setObject() because it may overwrite dojo/date/stamp (if that has already been loaded) | |
343 | 1 && lang.mixin(lang.getObject("dojo.date", true), date); | |
344 | ||
345 | return date; | |
346 | }); |