1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * 26 * @private 27 */ 28 AjxDateUtil = function() { 29 }; 30 31 AjxDateUtil.YEAR = 1; 32 AjxDateUtil.MONTH = 2; 33 AjxDateUtil.WEEK = 3; 34 AjxDateUtil.DAY = 4; 35 AjxDateUtil.TWO_WEEKS = 5; 36 37 AjxDateUtil.MSEC_PER_MINUTE = 60000; 38 AjxDateUtil.MSEC_PER_FIFTEEN_MINUTES = 900000; 39 AjxDateUtil.MSEC_PER_HALF_HOUR = 1800000; 40 AjxDateUtil.MSEC_PER_HOUR = 3600000; 41 AjxDateUtil.MSEC_PER_DAY = 24 * AjxDateUtil.MSEC_PER_HOUR; 42 43 AjxDateUtil.MINUTES_PER_DAY = 60 * 24; 44 AjxDateUtil.SECONDS_PER_DAY = 60 * 60 * 24; 45 46 AjxDateUtil.DAYS_PER_WEEK = 7; 47 48 AjxDateUtil.WEEKDAY_SHORT = AjxDateFormat.WeekdaySegment.WEEKDAYS[AjxDateFormat.SHORT]; 49 AjxDateUtil.WEEKDAY_MEDIUM = AjxDateFormat.WeekdaySegment.WEEKDAYS[AjxDateFormat.MEDIUM]; 50 AjxDateUtil.WEEKDAY_LONG = AjxDateFormat.WeekdaySegment.WEEKDAYS[AjxDateFormat.LONG]; 51 52 AjxDateUtil.MONTH_SHORT = AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.SHORT]; 53 AjxDateUtil.MONTH_MEDIUM = AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.MEDIUM]; 54 AjxDateUtil.MONTH_LONG = AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.LONG]; 55 56 AjxDateUtil._daysPerMonth = { 57 0:31, 58 1:29, 59 2:31, 60 3:30, 61 4:31, 62 5:30, 63 6:31, 64 7:31, 65 8:30, 66 9:31, 67 10:30, 68 11:31 69 }; 70 71 AjxDateUtil.MAX_DAYS_PER_MONTH = 31; 72 73 AjxDateUtil.WEEK_ONE_JAN_DATE = 1; 74 75 AjxDateUtil._init = 76 function() { 77 AjxDateUtil._dateFormat = AjxDateFormat.getDateInstance(AjxDateFormat.SHORT).clone(); 78 var segments = AjxDateUtil._dateFormat.getSegments(); 79 for (var i = 0; i < segments.length; i++) { 80 if (segments[i] instanceof AjxDateFormat.YearSegment) { 81 segments[i] = new AjxDateFormat.YearSegment(AjxDateUtil._dateFormat, "yyyy"); 82 } 83 } 84 AjxDateUtil._dateTimeFormat = 85 new AjxDateFormat(AjxDateUtil._dateFormat.toPattern() + " " + AjxDateFormat.getTimeInstance(AjxDateFormat.SHORT)); 86 87 AjxDateUtil._dateFormatNoYear = new AjxDateFormat(AjxMsg.formatDateMediumNoYear); 88 }; 89 90 AjxDateUtil._init(); 91 92 /* return true if the specified date (yyyy|yy, m (0-11), d (1-31)) 93 * is valid or not. 94 */ 95 AjxDateUtil.validDate = 96 function(y, m, d) { 97 var date = new Date(y, m, d); 98 var year = y > 999 ? date.getFullYear() : date.getYear(); 99 return date.getMonth() == m && date.getDate() == d && year == y; 100 }; 101 102 /* return number of days (1-31) in specified month (yyyy, mm (0-11)) 103 */ 104 AjxDateUtil.daysInMonth = 105 function(y, m) { 106 var date = new Date(y, m, 1, 12); 107 date.setMonth(date.getMonth()+1); 108 date.setDate(date.getDate()-1); 109 return date.getDate(); 110 }; 111 112 /* return true if year is a leap year 113 */ 114 AjxDateUtil.isLeapYear = 115 function(y) { 116 return (new Date(y, 1, 29)).getMonth() == 1; 117 }; 118 119 /* returns true if user's locale uses 24-hour time 120 */ 121 AjxDateUtil.isLocale24Hour = 122 function() { 123 // XXX: is there better/easier way to determine this?! 124 var timeFormatter = AjxDateFormat.getTimeInstance(AjxDateFormat.SHORT); 125 var len = timeFormatter._segments.length; 126 for (var j = 0; j < len; j++) { 127 if (timeFormatter._segments[j]._s == "a") 128 return false; 129 } 130 return true; 131 }; 132 133 /** 134 * rolls the month/year. If the day of month in the date passed in is greater 135 * then the max day in the new month, set it to the max. The date passed in is 136 * modified and also returned. 137 */ 138 AjxDateUtil.roll = 139 function(date, field, offset) { 140 var d = date.getDate(); 141 // move back to first day before rolling in case previous 142 // month/year has less days 143 144 if (field == AjxDateUtil.MONTH) { 145 date.setDate(1); 146 date.setMonth(date.getMonth() + offset); 147 var max = AjxDateUtil.daysInMonth(date.getFullYear(), date.getMonth()); 148 date.setDate(Math.min(d, max)); 149 } else if (field == AjxDateUtil.YEAR) { 150 date.setDate(1); 151 date.setFullYear(date.getFullYear() + offset); 152 var max = AjxDateUtil.daysInMonth(date.getFullYear(), date.getMonth()); 153 date.setDate(Math.min(d, max)); 154 } else if (field == AjxDateUtil.WEEK) { 155 date.setDate(date.getDate() + 7*offset); 156 } else if (field == AjxDateUtil.DAY) { 157 date.setDate(date.getDate() + offset); 158 } else if (field == AjxDateUtil.TWO_WEEKS) { 159 date.setDate(date.getDate() + 14*offset); 160 } else { 161 return date; 162 } 163 return date; 164 }; 165 166 /** 167 * checks whether given date is derived from DST shift 168 */ 169 AjxDateUtil.isDayShifted = 170 function(date) { 171 var refDate = new Date(date.getTime()); 172 173 //advance it by 1 day and reset to beginning of the day 174 refDate.setDate(refDate.getDate() +1) 175 refDate.setHours(0,0,0,0); 176 177 //if DST has no effect the advanced time should differ from given time 178 return refDate.getTime() == date.getTime(); 179 }; 180 181 /** 182 * rolls to next day. This can be used to roll to next day avoiding the daylight saving shift in time. 183 */ 184 AjxDateUtil.rollToNextDay = 185 function(date) { 186 date.setHours(0,0,0,0); 187 date.setTime(date.getTime() + AjxDateUtil.MSEC_PER_DAY); 188 }; 189 190 // Computes the difference between now and <dateMSec>. Returns a string describing 191 // the difference 192 AjxDateUtil.computeDateDelta = 193 function(dateMSec) { 194 var deltaMSec = (new Date()).getTime() - dateMSec; 195 var durationStr = AjxDateUtil.computeDuration(deltaMSec); 196 return durationStr ? (durationStr + " " + AjxMsg.ago) : null; 197 }; 198 199 // Computes the difference between now and <dateMSec>. Returns a simplified string describing 200 // the difference 201 AjxDateUtil.agoTime = 202 function(dateMSec) { 203 var deltaMSec = (new Date()).getTime() - dateMSec; 204 var durationStr = AjxDateUtil.computeDuration(deltaMSec, false, true); 205 return durationStr ? (durationStr + " " + AjxMsg.ago) : null; 206 }; 207 208 209 210 // Returns a string describing the duration, which is in milliseconds. 211 AjxDateUtil.computeDuration = 212 function(duration, brief, simplified) { 213 // bug fix #2203 - if delta is less than zero, dont bother computing 214 if (duration < 0) return null; 215 216 var years = Math.floor(duration / (AjxDateUtil.MSEC_PER_DAY * 365)); 217 if (years != 0) 218 duration -= years * AjxDateUtil.MSEC_PER_DAY * 365; 219 var months = Math.floor(duration / (AjxDateUtil.MSEC_PER_DAY * 30.42)); 220 if (months > 0) 221 duration -= Math.floor(months * AjxDateUtil.MSEC_PER_DAY * 30.42); 222 var days = Math.floor(duration / AjxDateUtil.MSEC_PER_DAY); 223 if (days > 0) 224 duration -= days * AjxDateUtil.MSEC_PER_DAY; 225 var hours = Math.floor(duration / AjxDateUtil.MSEC_PER_HOUR); 226 if (hours > 0) 227 duration -= hours * AjxDateUtil.MSEC_PER_HOUR; 228 var mins = Math.floor(duration / 60000); 229 if (mins > 0) 230 duration -= mins * 60000; 231 var secs = Math.floor(duration / 1000); 232 233 var formatter = brief ? AjxDurationFormatConcise : AjxDurationFormatVerbose; 234 if (years > 0) { 235 return simplified 236 ? formatter.formatYears(years) 237 : formatter.formatYears(years, months); 238 } else if (months > 0) { 239 return simplified 240 ? formatter.formatMonths(months) 241 : formatter.formatMonths(months, days); 242 } else if (days > 0) { 243 return simplified 244 ? formatter.formatDays(days) 245 : formatter.formatDays(days, hours); 246 } else if (hours > 0) { 247 return simplified 248 ? formatter.formatHours(hours) 249 : formatter.formatHours(hours, mins); 250 } else if (mins > 0) { 251 return simplified 252 ? formatter.formatMinutes(mins) 253 : formatter.formatMinutes(mins, secs); 254 } else { 255 return formatter.formatSeconds(secs); 256 } 257 }; 258 259 AjxDateUtil.simpleComputeDateStr = 260 function(date, stringToPrepend) { 261 var dateStr = AjxDateUtil._dateFormat.format(date); 262 return stringToPrepend ? stringToPrepend + dateStr : dateStr; 263 }; 264 AjxDateUtil.simpleParseDateStr = 265 function(dateStr) { 266 return AjxDateUtil._dateFormat.parse(dateStr); 267 }; 268 269 AjxDateUtil.simpleComputeDateTimeStr = 270 function(date, stringToPrepend) { 271 var dateTimeStr = AjxDateUtil._dateTimeFormat.format(date); 272 return stringToPrepend ? stringToPrepend + dateTimeStr : dateTimeStr; 273 }; 274 AjxDateUtil.simpleParseDateTimeStr = 275 function(dateTimeStr) { 276 return AjxDateUtil._dateTimeFormat.parse(dateTimeStr); 277 }; 278 279 AjxDateUtil.longComputeDateStr = 280 function(date) { 281 var formatter = AjxDateFormat.getDateInstance(AjxDateFormat.FULL); 282 return formatter.format(date); 283 } 284 285 AjxDateUtil.computeDateStr = 286 function(now, dateMSec) { 287 if (dateMSec == null) 288 return ""; 289 290 var date = new Date(dateMSec); 291 if (now.getTime() - dateMSec < AjxDateUtil.MSEC_PER_DAY && 292 now.getDay() == date.getDay()) { 293 return AjxDateUtil.computeTimeString(date); 294 } 295 296 if (now.getFullYear() == date.getFullYear()) { 297 return AjxDateUtil._dateFormatNoYear.format(date); 298 } 299 300 return AjxDateUtil.simpleComputeDateStr(date); 301 }; 302 303 AjxDateUtil.computeDateStrNoYear = 304 function(date) { 305 return AjxDateUtil._dateFormatNoYear.format(date); 306 }; 307 308 // Example output: "Today, 9:44 AM" "Yesterday, 12:22 PM" "Sun, 1/11/01 1:11 PM" 309 AjxDateUtil.computeWordyDateStr = 310 function(now, dateMSec) { 311 if (dateMSec == null) { 312 return ""; 313 } 314 315 var date = new Date(dateMSec); 316 if (now.getTime() - dateMSec < AjxDateUtil.MSEC_PER_DAY && now.getDay() == date.getDay()) { 317 if (!AjxDateUtil._wordyDateToday) { 318 AjxDateUtil._wordyDateToday = new AjxDateFormat(AjxMsg.formatWordyDateToday); 319 } 320 return AjxDateUtil._wordyDateToday.format(date); 321 } else if ((now.getTime() - dateMSec) < (2 * AjxDateUtil.MSEC_PER_DAY) && (now.getDay() - 1) == date.getDay()) { 322 if (!AjxDateUtil._wordyDateYesterday) { 323 AjxDateUtil._wordyDateYesterday = new AjxDateFormat(AjxMsg.formatWordyDateYesterday); 324 } 325 return AjxDateUtil._wordyDateYesterday.format(date); 326 } else { 327 if (!AjxDateUtil._wordyDate) { 328 AjxDateUtil._wordyDate = new AjxDateFormat(AjxMsg.formatWordyDate); 329 } 330 return AjxDateUtil._wordyDate.format(date); 331 } 332 }; 333 334 /* returns true if dateString is a valid and understandable date string 335 * in compliance with the locale of the user ie. dd/mm/yy or mm/dd/yy etc. 336 * Also for date strings like 1/32/2000 (that roll over to 2/1/2000), false is returned. 337 */ 338 AjxDateUtil.isValidSimpleDateStr = 339 function(str){ 340 if(!str) {return false}; 341 var dateValue = AjxDateUtil.getSimpleDateFormat().parse(str); 342 if (!dateValue) {return false}; 343 var dateValueStr = AjxDateUtil.simpleComputeDateStr(dateValue); 344 return (str == dateValueStr); 345 } 346 347 AjxDateUtil.computeTimeString = 348 function(date) { 349 var formatter = AjxDateFormat.getTimeInstance(AjxDateFormat.SHORT); 350 return formatter.format(date); 351 }; 352 353 AjxDateUtil.computeDateTimeString = 354 function(date) { 355 var formatter = AjxDateFormat.getDateTimeInstance(AjxDateFormat.LONG); 356 return formatter.format(date); 357 }; 358 359 AjxDateUtil._getHoursStr = 360 function(date, pad, useMilitary) { 361 var myVal = date.getHours(); 362 if (!useMilitary) { 363 myVal %= 12; 364 if (myVal == 0) myVal = 12; 365 } 366 return pad ? AjxDateUtil._pad(myVal) : myVal; 367 }; 368 369 AjxDateUtil._getMinutesStr = 370 function(date) { 371 return AjxDateUtil._pad(date.getMinutes()); 372 }; 373 374 AjxDateUtil._getSecondsStr = 375 function(date) { 376 return AjxDateUtil._pad(date.getSeconds()); 377 }; 378 379 AjxDateUtil._getAMPM = 380 function (date, upper) { 381 var myHour = date.getHours(); 382 return (myHour < 12) ? (upper ? 'AM' : 'am') : (upper ? 'PM' : 'pm'); 383 }; 384 385 AjxDateUtil._getMonthName = 386 function(date, abbreviated) { 387 return abbreviated 388 ? AjxDateUtil.MONTH_MEDIUM[date.getMonth()] 389 : AjxDateUtil.MONTH_LONG[date.getMonth()]; 390 }; 391 392 AjxDateUtil._getMonth = 393 function(date, pad) { 394 var myMonth = date.getMonth() + 1; 395 if (pad) { 396 return AjxDateUtil._pad(myMonth); 397 } else { 398 return myMonth; 399 } 400 }; 401 402 AjxDateUtil._getDate = 403 function(date, pad) { 404 var myVal = date.getDate(); 405 return pad ? AjxDateUtil._pad(myVal) : myVal; 406 }; 407 408 AjxDateUtil._getWeekday = 409 function (date) { 410 var myVal = date.getDay(); 411 return AjxDateUtil.WEEKDAY_LONG[myVal]; 412 }; 413 414 // Returns "Mon", "Tue", etc. 415 AjxDateUtil._getWeekdayMedium = 416 function (date) { 417 var myVal = date.getDay(); 418 return AjxDateUtil.WEEKDAY_MEDIUM[myVal]; 419 }; 420 421 AjxDateUtil._getFullYear = 422 function(date) { 423 return date.getFullYear(); 424 }; 425 426 AjxDateUtil.getFirstDayOfWeek = 427 function (dt, startOfWeek) { 428 startOfWeek = startOfWeek || 0; 429 var dayOfWeekIndex = dt.getDay(); 430 var dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7; 431 dt.setDate(dt.getDate() - dayOfWeek); 432 return dt; 433 }; 434 435 AjxDateUtil.getLastDayOfWeek = 436 function (dt, startOfWeek) { 437 startOfWeek = startOfWeek || 0; 438 var dayOfWeekIndex = dt.getDay(); 439 var dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7; 440 dt.setDate(dt.getDate() - dayOfWeek + 6); 441 dt.setHours(23, 59, 59, 999); 442 return dt; 443 }; 444 445 AjxDateUtil.getWeekNumber = 446 function(date, firstDayOfWeek, janDate, isISO8601WeekNum) { 447 448 // Setup Defaults 449 firstDayOfWeek = firstDayOfWeek || 0; 450 janDate = janDate || AjxDateUtil.WEEK_ONE_JAN_DATE; 451 date = date || new Date(); 452 453 date.setHours(12,0,0,0); 454 var targetDate = date, 455 startOfWeek, 456 endOfWeek; 457 458 if (targetDate.getDay() === firstDayOfWeek) { 459 startOfWeek = targetDate; 460 } else { 461 startOfWeek = AjxDateUtil.getFirstDayOfWeek(targetDate, firstDayOfWeek); 462 } 463 464 var startYear = startOfWeek.getFullYear(), 465 startTime = startOfWeek.getTime(); 466 467 // DST shouldn't be a problem here, math is quicker than setDate(); 468 endOfWeek = new Date(startOfWeek.getTime() + 6*AjxDateUtil.MSEC_PER_DAY); 469 470 var weekNum; 471 472 if(!isISO8601WeekNum) { 473 if (startYear !== endOfWeek.getFullYear() && endOfWeek.getDate() >= janDate) { 474 weekNum = 1; 475 } else { 476 var weekOne = (new Date(startYear, 0, janDate)); 477 weekOne.setHours(12,0,0,0); 478 var weekOneDayOne = AjxDateUtil.getFirstDayOfWeek(weekOne, firstDayOfWeek); 479 480 // Round days to smoothen out 1 hr DST diff 481 var daysDiff = Math.round((targetDate.getTime() - weekOneDayOne.getTime())/AjxDateUtil.MSEC_PER_DAY); 482 483 // Calc. Full Weeks 484 var rem = daysDiff % 7; 485 var weeksDiff = (daysDiff - rem)/7; 486 weekNum = weeksDiff + 1; 487 } 488 return weekNum; 489 }else { 490 491 var newYear = new Date(date.getFullYear(),0,1); 492 var day = newYear.getDay() - 1; 493 day = (day >= 0 ? day : day + 7); 494 var dayOftheYear = Math.floor((date.getTime()-newYear.getTime() - (date.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/AjxDateUtil.MSEC_PER_DAY) + 1; 495 496 if(day < 4) 497 { 498 weekNum = Math.floor((dayOftheYear+day-1)/7) + 1; 499 if(weekNum > 52) 500 { 501 var nxtYear = new Date(date.getFullYear() + 1,0,1); 502 var nxtDay = nxtYear.getDay() - 1; 503 nxtDay = nxtDay >= 0 ? nxtDay : nxtDay + 7; 504 weekNum = nxtDay < 4 ? 1 : 53; 505 } 506 }else { 507 weekNum = Math.floor((dayOftheYear+day -1 )/7); 508 if(weekNum == 0) 509 { 510 var prevYear = new Date(date.getFullYear()-1,0,1); 511 var prevDay = prevYear.getDay()-1; 512 prevDay = (prevDay >= 0 ? prevDay : prevDay + 7); 513 weekNum = ( prevDay==3 || ( AjxDateUtil.isLeapYear(prevYear.getFullYear()) && prevDay==2 ) ) ? 53 : 52; 514 } 515 } 516 return weekNum; 517 } 518 }; 519 520 AjxDateUtil.getTimeStr = 521 function(date, format) { 522 var s = format; 523 s = s.replace(/%d/g, AjxDateUtil._getDate(date, true)); // zero padded day of the month 524 s = s.replace(/%D/g, AjxDateUtil._getDate(date, false)); // day of the month without padding 525 s = s.replace(/%w/g, AjxDateUtil._getWeekday(date)); // day of the week 526 s = s.replace(/%M/g, AjxDateUtil._getMonthName(date)); // full month name 527 s = s.replace(/%t/g, AjxDateUtil._getMonthName(date, true)); // abbr. month name 528 s = s.replace(/%n/g, AjxDateUtil._getMonth(date, true)); // zero padded month 529 s = s.replace(/%Y/g, AjxDateUtil._getFullYear(date)); // full year 530 s = s.replace(/%h/g, AjxDateUtil._getHoursStr(date, false, false)); // non-padded hours 531 s = s.replace(/%H/g, AjxDateUtil._getHoursStr(date, true, false )); // padded hours 532 s = s.replace(/%m/g, AjxDateUtil._getMinutesStr(date)); // padded minutes 533 s = s.replace(/%s/g, AjxDateUtil._getSecondsStr(date)); // padded seconds 534 s = s.replace(/%P/g, AjxDateUtil._getAMPM(date, true)); // upper case AM PM 535 s = s.replace(/%p/g, AjxDateUtil._getAMPM(date, false)); // lower case AM PM 536 return s; 537 }; 538 539 AjxDateUtil.getRoundedMins = 540 function (date, roundTo) { 541 var mins = date.getMinutes(); 542 if (mins != 0 && roundTo) 543 mins = (Math.ceil( (mins/roundTo) )) * roundTo; 544 return mins; 545 }; 546 547 AjxDateUtil.roundTimeMins = 548 function(date, roundTo) { 549 var mins = date.getMinutes(); 550 var hours = date.getHours(); 551 if (mins != 0 && roundTo){ 552 mins = (Math.ceil( (mins/roundTo) )) * roundTo; 553 if (mins == 60) { 554 mins = 0; 555 hours++; 556 } 557 date.setMinutes(mins); 558 date.setHours(hours); 559 } 560 return date; 561 }; 562 563 AjxDateUtil.isInRange = 564 function(startTime1, endTime1, startTime2, endTime2) { 565 return (startTime1 < endTime2 && endTime1 > startTime2); 566 } 567 568 AjxDateUtil.getSimpleDateFormat = 569 function() { 570 return AjxDateUtil._dateFormat; 571 }; 572 573 /** 574 * The following are helper routines for processing server date/time which comes 575 * in this format: YYYYMMDDTHHMMSSZ 576 */ 577 AjxDateUtil.getServerDate = 578 function(date) { 579 if (!AjxDateUtil._serverDateFormatter) { 580 AjxDateUtil._serverDateFormatter = new AjxDateFormat("yyyyMMdd"); 581 } 582 return AjxDateUtil._serverDateFormatter.format(date); 583 }; 584 585 AjxDateUtil.getServerDateTime = 586 function(date, useUTC) { 587 var newDate = date; 588 var formatter = null; 589 590 if (useUTC) { 591 if (!AjxDateUtil._serverDateTimeFormatterUTC) { 592 AjxDateUtil._serverDateTimeFormatterUTC = new AjxDateFormat("yyyyMMdd'T'HHmmss'Z'"); 593 } 594 formatter = AjxDateUtil._serverDateTimeFormatterUTC; 595 // add timezone offset to this UTC date 596 newDate = new Date(date.getTime()); 597 newDate.setMinutes(newDate.getMinutes() + newDate.getTimezoneOffset()); 598 } else { 599 if (!AjxDateUtil._serverDateTimeFormatter) { 600 AjxDateUtil._serverDateTimeFormatter = new AjxDateFormat("yyyyMMdd'T'HHmmss"); 601 } 602 formatter = AjxDateUtil._serverDateTimeFormatter; 603 } 604 605 return formatter.format(newDate); 606 }; 607 608 AjxDateUtil.parseServerTime = 609 function(serverStr, date, noSpecialUtcCase) { 610 if (serverStr.charAt(8) == 'T') { 611 var hh = parseInt(serverStr.substr(9,2), 10); 612 var mm = parseInt(serverStr.substr(11,2), 10); 613 var ss = parseInt(serverStr.substr(13,2), 10); 614 if (!noSpecialUtcCase && serverStr.charAt(15) == 'Z') { 615 mm += AjxTimezone.getOffset(AjxTimezone.DEFAULT, date); 616 } 617 date.setHours(hh, mm, ss, 0); 618 } 619 return date; 620 }; 621 622 AjxDateUtil.parseISO8601Date = function(s) { 623 var formatters = AjxDateUtil.__ISO8601_formats; 624 if (!formatters) { 625 formatters = AjxDateUtil.__ISO8601_formats = [ 626 new AjxDateUtil.TZDFormat("yyyy-MM-dd'T'HH:mm:ss.SZ"), 627 new AjxDateUtil.TZDFormat("yyyy-MM-dd'T'HH:mm:ssZ"), 628 new AjxDateUtil.TZDFormat("yyyy-MM-dd'T'HH:mmZ"), 629 new AjxDateFormat("yyyy-MM-dd"), 630 new AjxDateFormat("yyyy-MM"), 631 new AjxDateFormat("yyyy") 632 ]; 633 } 634 for (var i = 0; i < formatters.length; i++) { 635 var date = formatters[i].parse(s); 636 if (date) return date; 637 } 638 return null; 639 }; 640 641 AjxDateUtil.TZDFormat = function(pattern) { 642 if (arguments.length == 0) return; 643 AjxDateFormat.apply(this, arguments); 644 var segments = this._segments || []; 645 for (var i = 0; i < segments.length; i++) { 646 var segment = segments[i]; 647 if (segment instanceof AjxDateFormat.TimezoneSegment) { 648 segments[i] = new AjxDateUtil.TZDSegment(segment.toSubPattern()); 649 } 650 } 651 }; 652 AjxDateUtil.TZDFormat.prototype = new AjxDateFormat; 653 AjxDateUtil.TZDFormat.prototype.constructor = AjxDateUtil.TZDFormat; 654 AjxDateUtil.TZDFormat.prototype.toString = function() { return "TZDFormat"; }; 655 656 AjxDateUtil.TZDSegment = function(pattern) { 657 if (arguments.length == 0) return; 658 AjxDateFormat.TimezoneSegment.apply(this, arguments); 659 }; 660 AjxDateUtil.TZDSegment.prototype = new AjxDateFormat.TimezoneSegment; 661 AjxDateUtil.TZDSegment.prototype.constructor = AjxDateUtil.TZDSegment; 662 AjxDateUtil.TZDSegment.prototype.toString = function() { return "TZDSegment"; }; 663 664 AjxDateUtil.TZDSegment.prototype.parse = function(o, s, i) { 665 var m = /^(Z)|^(\+|\-)(\d\d):(\d\d)/.exec(s.substr(i)); 666 if (m) { 667 var offset = new Date().getTimezoneOffset(); 668 if (m[1]) o.timezone = offset; 669 else { 670 var hours = parseInt(m[3],10), mins = parseInt(m[4],10); 671 o.timezone = hours * 60 + mins; 672 if (m[2] != "-") o.timezone *= -1; 673 o.timezone -= offset; 674 } 675 } 676 return i + (m ? m[0].length : 0); 677 }; 678 679 AjxDateUtil.parseServerDateTime = 680 function(serverStr, noSpecialUtcCase) { 681 if (serverStr == null) return null; 682 683 var d = new Date(); 684 var yyyy = parseInt(serverStr.substr(0,4), 10); 685 var MM = parseInt(serverStr.substr(4,2), 10); 686 var dd = parseInt(serverStr.substr(6,2), 10); 687 d.setFullYear(yyyy); 688 d.setMonth(MM - 1); 689 d.setMonth(MM - 1); // DON'T remove second call to setMonth (see bug #3839) 690 d.setDate(dd); 691 AjxDateUtil.parseServerTime(serverStr, d, noSpecialUtcCase); 692 return d; 693 }; 694 695 AjxDateUtil._pad = 696 function(n) { 697 return n < 10 ? ('0' + n) : n; 698 }; 699 700 /** 701 * Returns the year portion of the given date as a YYYY string. 702 * 703 * @param {Date} date (optional, defaults to current date) a date 704 * @returns {string} year as YYYY 705 */ 706 AjxDateUtil.getYearStr = function(date) { 707 date = date || new Date(); 708 return date.getFullYear() + ""; 709 }; 710 711 AjxDurationFormatVerbose = function() { } 712 713 AjxDurationFormatVerbose.formatYears = 714 function(years, months) { 715 var deltaStr = years + " "; 716 deltaStr += (years > 1) ? AjxMsg.years : AjxMsg.year; 717 if (years <= 3 && months > 0) { 718 deltaStr += " " + months; 719 deltaStr += " " + ((months > 1) ? AjxMsg.months : AjxMsg.months); 720 } 721 return deltaStr; 722 }; 723 724 AjxDurationFormatVerbose.formatMonths = 725 function(months, days) { 726 var deltaStr = months + " "; 727 deltaStr += (months > 1) ? AjxMsg.months : AjxMsg.month; 728 if (months <= 3 && days > 0) { 729 deltaStr += " " + days; 730 deltaStr += " " + ((days > 1) ? AjxMsg.days : AjxMsg.day); 731 } 732 return deltaStr; 733 }; 734 735 AjxDurationFormatVerbose.formatDays = 736 function(days, hours) { 737 var deltaStr = days + " "; 738 deltaStr += (days > 1) ? AjxMsg.days : AjxMsg.day; 739 if (days <= 2 && hours > 0) { 740 deltaStr += " " + hours; 741 deltaStr += " " + ((hours > 1) ? AjxMsg.hours : AjxMsg.hour); 742 } 743 return deltaStr; 744 }; 745 746 AjxDurationFormatVerbose.formatHours = 747 function(hours, mins) { 748 var deltaStr = hours + " "; 749 deltaStr += (hours > 1) ? AjxMsg.hours : AjxMsg.hour; 750 if (hours < 5 && mins > 0) { 751 deltaStr += " " + mins; 752 deltaStr += " " + ((mins > 1) ? AjxMsg.minutes : AjxMsg.minute); 753 } 754 return deltaStr; 755 }; 756 757 AjxDurationFormatVerbose.formatMinutes = 758 function(mins, secs) { 759 var deltaStr = mins + " "; 760 deltaStr += ((mins > 1) ? AjxMsg.minutes : AjxMsg.minute); 761 if (mins < 5 && secs > 0) { 762 deltaStr += " " + secs; 763 deltaStr += " " + ((secs > 1) ? AjxMsg.seconds : AjxMsg.second); 764 } 765 return deltaStr; 766 }; 767 768 AjxDurationFormatVerbose.formatSeconds = 769 function(secs) { 770 return (secs + " " + ((secs > 1) ? AjxMsg.seconds : AjxMsg.second)); 771 }; 772 773 AjxDurationFormatConcise = function() { } 774 775 AjxDurationFormatConcise.formatYears = 776 function(years, months) { 777 return this._format(years, months); 778 }; 779 780 AjxDurationFormatConcise.formatMonths = 781 function(months, days) { 782 return this._format(months, days); 783 }; 784 785 AjxDurationFormatConcise.formatDays = 786 function(days, hours) { 787 return this._format(days, hours); 788 }; 789 790 AjxDurationFormatConcise.formatHours = 791 function(hours, mins) { 792 return this._format(hours, mins); 793 }; 794 795 AjxDurationFormatConcise.formatMinutes = 796 function(mins, secs) { 797 return this._format(mins, secs); 798 }; 799 800 AjxDurationFormatConcise.formatSeconds = 801 function(secs) { 802 return this._format(0, secs); 803 }; 804 805 AjxDurationFormatConcise._format = 806 function(a, b) { 807 var i = 0; 808 var result = []; 809 result[i++] = a; 810 result[i++] = ':'; 811 if (b < 10) { 812 result[i++] = '0'; 813 } 814 result[i++] = b; 815 return result.join(''); 816 }; 817 818 /** 819 * Added more utility functions for date finding and navigating 820 */ 821 822 AjxDateUtil.SUNDAY = 0; 823 AjxDateUtil.MONDAY = 1; 824 AjxDateUtil.TUESDAY = 2; 825 AjxDateUtil.WEDNESDAY = 3; 826 AjxDateUtil.THURSDAY = 4; 827 AjxDateUtil.FRIDAY = 5; 828 AjxDateUtil.SATURDAY = 6; 829 830 /** 831 * 832 * @param fromThisDate The searching starts from this date. 833 * @param thisWeekday The day to find ( eg. AjxDateUtil.SUNDAY) 834 * @param count Which occurence, like first, second.. has to be always positive 835 * 836 */ 837 AjxDateUtil.getDateForNextDay = 838 function(fromThisDate, thisWeekday, count) { 839 count = count || 1; 840 var r = new Date(fromThisDate); 841 for (var i = 0; i < count; i++) { 842 r = AjxDateUtil._getDateForNextWeekday(r, thisWeekday); 843 if (i < count-1) { 844 r.setDate(r.getDate() + 1); 845 } 846 } 847 return r; 848 } 849 850 /** 851 * 852 * @param fromThisDate The searching work week days starting from this date 853 * @param count Which occurence, like first, second.. has to be always positive 854 * 855 */ 856 AjxDateUtil.getDateForNextWorkWeekDay = 857 function(fromThisDate, count) { 858 count = count?count:1; 859 var r = new Date(fromThisDate); 860 for (var i = 0; i < count; i++) { 861 r = AjxDateUtil._getDateForNextWorkWeekday(r); 862 if (i < count-1) { 863 r.setDate(r.getDate() + 1); 864 } 865 } 866 return r; 867 } 868 869 /** 870 * 871 * @param fromThisDate The starting point 872 * @param thisWeekday The day to find 873 * @param count this many positions to navigate, if negative goes in reverse, if positive goes forward 874 */ 875 AjxDateUtil.getDateForThisDay = 876 function(fromThisDate, thisWeekday, count) { 877 if (count < 0 ) { 878 return AjxDateUtil.getDateForPrevDay(fromThisDate, thisWeekday, -count);//-(-) is plus 879 } else { 880 return AjxDateUtil.getDateForNextDay(fromThisDate, thisWeekday, count); 881 } 882 } 883 884 /** 885 * 886 * @param fromThisDate The starting point 887 * @param count this many positions to navigate, if negative goes in reverse, if positive goes forward 888 */ 889 AjxDateUtil.getDateForThisWorkWeekDay = 890 function(fromThisDate, count) { 891 if (count < 0 ) { 892 return AjxDateUtil.getDateForPrevWorkWeekDay(fromThisDate, -count); //-(-) is plus 893 }else{ 894 return AjxDateUtil.getDateForNextWorkWeekDay(fromThisDate, count); 895 } 896 } 897 898 /** 899 * 900 * @param fromThisDate The searching starts from this date in reverse direction. 901 * @param thisWeekday The day to find ( eg. AjxDateUtil.SUNDAY) 902 * @param count Which occurence, like first, second..has to be always positive 903 */ 904 AjxDateUtil.getDateForPrevDay = 905 function(fromThisDate,thisWeekday,count) { 906 count = count || 1; 907 var r = new Date(fromThisDate); 908 for (var i = 0; i < count; i++) { 909 r = AjxDateUtil._getDateForPrevWeekday(r, thisWeekday); 910 if (i < count-1) { 911 r.setDate(r.getDate()-1); 912 } 913 } 914 return r; 915 } 916 917 /** 918 * 919 * @param fromThisDate The searching for work week days starting from this date in reverse direction. 920 * @param count Which occurence, like first, second..has to be always positive 921 */ 922 923 AjxDateUtil.getDateForPrevWorkWeekDay = 924 function(fromThisDate, count) { 925 count = count || 1; 926 var r = new Date(fromThisDate); 927 for(var i = 0; i < count; i++) { 928 r = AjxDateUtil._getDateForPrevWorkWeekday(r); 929 if (i < count-1) { 930 r.setDate(r.getDate()-1); 931 } 932 } 933 return r; 934 } 935 936 /** 937 * note - this deals with the format we save from Prefs page. Careful if using for other cases. 938 * @param value 939 * @return {String} 940 */ 941 AjxDateUtil.dateLocal2GMT = 942 function(value) { 943 if (!value) { return ""; } 944 945 var yr, mo, da, hr, mi, se; // really smart parsing. 946 yr = parseInt(value.substr(0, 4), 10); 947 mo = parseInt(value.substr(4, 2), 10); 948 da = parseInt(value.substr(6, 2), 10); 949 hr = parseInt(value.substr(8, 2), 10); 950 mi = parseInt(value.substr(10, 2), 10); 951 se = parseInt(value.substr(12, 2), 10); 952 var date = new Date(yr, mo - 1, da, hr, mi, se, 0); 953 yr = date.getUTCFullYear(); 954 mo = date.getUTCMonth() + 1; 955 da = date.getUTCDate(); 956 hr = date.getUTCHours(); 957 mi = date.getUTCMinutes(); 958 se = date.getUTCSeconds(); 959 var a = [ yr, mo, da, hr, mi, se ]; 960 for (var i = a.length; --i > 0;) { 961 var n = a[i]; 962 if (n < 10) 963 a[i] = "0" + n; 964 } 965 return (a.join("") + "Z"); 966 }; 967 968 /** 969 * note - this deals with the format we save from Prefs page. Careful if using for other cases. 970 * @param value 971 * @return {String} 972 */ 973 AjxDateUtil.dateGMT2Local = 974 function(value) { 975 if (!value) { return ""; } 976 977 var yr, mo, da, hr, mi, se; // really smart parsing. 978 yr = parseInt(value.substr(0, 4), 10); 979 mo = parseInt(value.substr(4, 2), 10); 980 da = parseInt(value.substr(6, 2), 10); 981 hr = parseInt(value.substr(8, 2), 10); 982 mi = parseInt(value.substr(10, 2), 10); 983 se = parseInt(value.substr(12, 2), 10); 984 var date = new Date(); 985 date.setUTCMilliseconds(0); 986 date.setUTCSeconds(se); 987 date.setUTCMinutes(mi); 988 date.setUTCHours(hr); 989 date.setUTCDate(da); 990 date.setUTCMonth(mo - 1); 991 date.setUTCFullYear(yr); 992 yr = date.getFullYear(); 993 mo = date.getMonth() + 1; 994 da = date.getDate(); 995 hr = date.getHours(); 996 mi = date.getMinutes(); 997 se = date.getSeconds(); 998 var a = [yr, mo, da, hr, mi, se]; 999 for (var i = a.length; --i > 0;) { 1000 var n = a[i]; 1001 if (n < 10) 1002 a[i] = "0" + n; 1003 } 1004 return (a.join("") + "Z"); 1005 }; 1006 1007 1008 AjxDateUtil._getDateForNextWeekday = 1009 function(fromThisDate,thisWeekday) { 1010 var newDate = new Date(fromThisDate); 1011 var weekDay = fromThisDate.getDay(); 1012 if (weekDay == thisWeekday) { 1013 return newDate; 1014 } 1015 var diff = (thisWeekday-weekDay); 1016 if (diff > 0) { 1017 newDate.setDate(fromThisDate.getDate() + diff); 1018 } else { 1019 newDate.setDate(fromThisDate.getDate() + (7 + diff)); 1020 } 1021 return newDate; 1022 } 1023 1024 AjxDateUtil._getDateForNextWorkWeekday = 1025 function(fromThisDate) { 1026 var newDate = new Date(fromThisDate); 1027 var weekDay = fromThisDate.getDay(); 1028 if (weekDay == AjxDateUtil.SUNDAY) { 1029 newDate.setDate(fromThisDate.getDate()+1); 1030 } else if (weekDay == AjxDateUtil.SATURDAY) { 1031 newDate.setDate(fromThisDate.getDate()+2); 1032 } 1033 return newDate; 1034 } 1035 1036 AjxDateUtil._getDateForPrevWeekday = 1037 function(fromThisDate, thisWeekday) { 1038 var newDate = new Date(fromThisDate); 1039 var weekDay = fromThisDate.getDay(); 1040 if (weekDay == thisWeekday) { 1041 return newDate; 1042 } 1043 var diff = (weekDay-thisWeekday); 1044 if (diff > 0) { 1045 newDate.setDate(fromThisDate.getDate() - diff); 1046 } else { 1047 newDate.setDate(fromThisDate.getDate() - (7 + diff)); 1048 } 1049 return newDate; 1050 } 1051 1052 AjxDateUtil._getDateForPrevWorkWeekday = 1053 function(fromThisDate) { 1054 var newDate = new Date(fromThisDate); 1055 var weekDay = fromThisDate.getDay(); 1056 if (weekDay == AjxDateUtil.SUNDAY) { 1057 newDate.setDate(fromThisDate.getDate() - 2); 1058 } else if (weekDay == AjxDateUtil.SATURDAY) { 1059 newDate.setDate(fromThisDate.getDate() - 1); 1060 } 1061 return newDate; 1062 } 1063 1064 // 1065 // Date calculator functions 1066 // 1067 1068 AjxDateUtil.calculate = 1069 function(rule, date) { 1070 // initialize 1071 if (!AjxDateUtil.__calculate_initialized) { 1072 AjxDateUtil.__calculate_initialized = true; 1073 AjxDateUtil.__calculate_init(); 1074 } 1075 1076 var now = date || new Date; 1077 rule = rule.replace(/^\s*|\s*$/, "").replace(/\s*=\s*/g,"=").replace(/\s*,\s*/g,","); 1078 var a = rule.split(/\s+/g); 1079 var s, m, plusminus, number, type, amount, weekord, daynum; 1080 for (var i = 0; i < a.length; i++) { 1081 s = a[i]; 1082 // comment 1083 if (s.match(AjxDateUtil.RE_COMMENT)) { 1084 break; 1085 } 1086 // context date 1087 if (s.match(AjxDateUtil.RE_NOW)) { 1088 date = new Date(now.getTime()); 1089 continue; 1090 } 1091 // add 1092 if (m = s.match(AjxDateUtil.RE_ADD_NUMBER)) { 1093 plusminus = m[1]; 1094 number = AjxDateUtil.__calculate_parseInt(m[2]); 1095 type = a[++i]; 1096 amount = plusminus == '+' ? number : number * -1; 1097 AjxDateUtil.__calculate_add(date, type, amount); 1098 continue; 1099 } 1100 // set 1101 if (m = s.match(AjxDateUtil.RE_SET)) { 1102 AjxDateUtil.__calculate_set(date, m[1], m[2]); 1103 continue; 1104 } 1105 // try to parse as a date 1106 date = AjxDateFormat.parse("yyyyy-MM-dd", s); 1107 if (!date && (date = AjxDateFormat.parse("yyyy-MM-dd'T'hh:mm:ss'Z'", s))) { 1108 date.setMinutes(date.getMinutes() - date.getTimezoneOffset()); 1109 } 1110 if (!date) date = AjxDateFormat.parse("yyyy-MM-dd'T'HH:mm:ss", s); 1111 if (!date) throw "invalid date pattern: \""+s+"\""; 1112 } 1113 return date; 1114 }; 1115 1116 // 1117 // Date calculator constants 1118 // 1119 1120 AjxDateUtil.S_DAYNAME = [ 1121 AjxMsg["calc.dayname.sunday"], 1122 AjxMsg["calc.dayname.monday"], 1123 AjxMsg["calc.dayname.tuesday"], 1124 AjxMsg["calc.dayname.wednesday"], 1125 AjxMsg["calc.dayname.thursday"], 1126 AjxMsg["calc.dayname.friday"], 1127 AjxMsg["calc.dayname.saturday"] 1128 ].join("|"); 1129 1130 AjxDateUtil.S_MONTHNAME = [ 1131 AjxMsg["calc.monthname.january"], 1132 AjxMsg["calc.monthname.february"], 1133 AjxMsg["calc.monthname.march"], 1134 AjxMsg["calc.monthname.april"], 1135 AjxMsg["calc.monthname.may"], 1136 AjxMsg["calc.monthname.june"], 1137 AjxMsg["calc.monthname.july"], 1138 AjxMsg["calc.monthname.august"], 1139 AjxMsg["calc.monthname.september"], 1140 AjxMsg["calc.monthname.october"], 1141 AjxMsg["calc.monthname.november"], 1142 AjxMsg["calc.monthname.december"] 1143 ].join("|"); 1144 1145 AjxDateUtil.S_WEEKORD = [ 1146 AjxMsg["calc.ordinal.first"], 1147 AjxMsg["calc.ordinal.second"], 1148 AjxMsg["calc.ordinal.third"], 1149 AjxMsg["calc.ordinal.fourth"], 1150 AjxMsg["calc.ordinal.fifth"], 1151 AjxMsg["calc.ordinal.last"] 1152 ].join("|"); 1153 1154 AjxDateUtil.WEEKORD_RE = [ 1155 new RegExp("(first|"+AjxMsg["calc.ordinal.first"]+")", "i"), 1156 new RegExp("(second|"+AjxMsg["calc.ordinal.second"]+")", "i"), 1157 new RegExp("(third|"+AjxMsg["calc.ordinal.third"]+")", "i"), 1158 new RegExp("(fourth|"+AjxMsg["calc.ordinal.fourth"]+")", "i"), 1159 new RegExp("(last|"+AjxMsg["calc.ordinal.last"]+")", "i") 1160 ]; 1161 1162 // NOTE: Originally, the keywords for the date calculation rules 1163 // were in the message bundle so that they could be translated. 1164 // But while the keywords were translated, the rules were not 1165 // updated to use the translated keywords. So none of the date 1166 // matching worked in other languages. So I am reverting that 1167 // decision and hard-coding all of the relevant keywords. The 1168 // ordinals, day names, and month names still need to be 1169 // translated, though. 1170 1171 AjxMsg["calc.now"] = "now"; 1172 AjxMsg["calc.date"] = "date"; 1173 1174 AjxMsg["calc.duration.year"] = "year|years"; 1175 AjxMsg["calc.duration.month"] = "mons|month|months"; 1176 AjxMsg["calc.duration.day"] = "day|days"; 1177 AjxMsg["calc.duration.hour"] = "hour|hours"; 1178 AjxMsg["calc.duration.minute"] = "min|mins|minute|minutes"; 1179 AjxMsg["calc.duration.week"] = "week"; 1180 AjxMsg["calc.duration.second"] = "sec|secs|second|seconds"; 1181 AjxMsg["calc.duration.millisecond"] = "milli|millis|millisecond|milliseconds"; 1182 1183 AjxDateUtil.S_DURATION = [ 1184 AjxMsg["calc.duration.year"], 1185 AjxMsg["calc.duration.month"], 1186 AjxMsg["calc.duration.week"], 1187 AjxMsg["calc.duration.day"], 1188 AjxMsg["calc.duration.hour"], 1189 AjxMsg["calc.duration.minute"], 1190 AjxMsg["calc.duration.second"], 1191 AjxMsg["calc.duration.millisecond"] 1192 ].join("|"); 1193 1194 // 1195 // Date calculator private functions 1196 // 1197 1198 AjxDateUtil.__calculate_init = 1199 function() { 1200 AjxDateUtil.WEEKDAYS = {}; 1201 var weekdays = [ 1202 "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" 1203 ]; 1204 for (var i = 0; i < weekdays.length; i++) { 1205 var weekday = AjxMsg["calc.dayname."+weekdays[i]].split("|"); 1206 for (var j = 0; j < weekday.length; j++) { 1207 AjxDateUtil.WEEKDAYS[weekday[j].toLowerCase()] = i; 1208 } 1209 } 1210 1211 AjxDateUtil.MONTHNAME2MONTHNUM = {}; 1212 var months = [ 1213 "january", "february", "march", "april", "may", "june", 1214 "july", "august", "september", "october", "november", "december" 1215 ]; 1216 for (var i = 0; i < months.length; i++) { 1217 var month = AjxMsg["calc.monthname."+months[i]].split("|"); 1218 for (var j = 0; j < month.length; j++) { 1219 AjxDateUtil.MONTHNAME2MONTHNUM[month[j].toLowerCase()] = i; 1220 } 1221 } 1222 1223 AjxDateUtil.RE_YEAR = new RegExp("^("+AjxMsg["calc.duration.year"]+")$", "i"); 1224 AjxDateUtil.RE_MONTH = new RegExp("^("+AjxMsg["calc.duration.month"]+")$", "i"); 1225 AjxDateUtil.RE_WEEK = new RegExp("^("+AjxMsg["calc.duration.week"]+")$", "i"); 1226 AjxDateUtil.RE_DAY = new RegExp("^("+AjxMsg["calc.duration.day"]+")$", "i"); 1227 AjxDateUtil.RE_HOUR = new RegExp("^("+AjxMsg["calc.duration.hour"]+")$", "i"); 1228 AjxDateUtil.RE_MINUTE = new RegExp("^("+AjxMsg["calc.duration.minute"]+")$", "i"); 1229 AjxDateUtil.RE_SECOND = new RegExp("^("+AjxMsg["calc.duration.second"]+")$", "i"); 1230 AjxDateUtil.RE_MILLISECOND = new RegExp("^("+AjxMsg["calc.duration.millisecond"]+")$", "i"); 1231 1232 AjxDateUtil.RE_DATE = new RegExp("^("+AjxMsg["calc.date"]+")$", "i"); 1233 1234 AjxDateUtil.RE_DAYNAME = new RegExp("^("+AjxDateUtil.S_DAYNAME+")$", "i"); 1235 AjxDateUtil.RE_MONTHNAME = new RegExp("^("+AjxDateUtil.S_MONTHNAME+")$", "i"); 1236 AjxDateUtil.RE_WEEKORD = new RegExp("^("+AjxDateUtil.S_WEEKORD+")$", "i"); 1237 1238 AjxDateUtil.RE_COMMENT = /^#/; 1239 AjxDateUtil.RE_NOW = new RegExp("^("+AjxMsg["calc.now"]+")$", "i"); 1240 AjxDateUtil.RE_ADD_NUMBER = new RegExp("^([+\\-])(\\d+)$", "i"); 1241 AjxDateUtil.RE_SET = new RegExp("^("+AjxDateUtil.S_DURATION+"|"+AjxMsg["calc.date"]+")=(.*)$", "i"); 1242 }; 1243 1244 AjxDateUtil.__calculate_normalizeFullWidthDigit = 1245 function(digit) { 1246 var charCode = "0".charCodeAt(0) + digit.charCodeAt(0) - "\uff10".charCodeAt(0); 1247 return String.fromCharCode(charCode); 1248 }; 1249 1250 /** This is needed to handle asian full-width digits. */ 1251 AjxDateUtil.__calculate_replaceFullWidthDigit = 1252 function($0, digit) { 1253 return AjxDateUtil.__calculate_normalizeFullWidthDigit(digit); 1254 }; 1255 1256 AjxDateUtil.__calculate_parseInt = 1257 function(s) { 1258 s = s.replace(/([\uFF10-\uFF19])/g, AjxDateUtil.__calculate_normalizeFullWidthDigit); 1259 return parseInt(s, 10); 1260 }; 1261 1262 AjxDateUtil.__calculate_add = 1263 function(date, type, amount) { 1264 if (type.match(AjxDateUtil.RE_YEAR)) { 1265 date.setFullYear(date.getFullYear() + amount); 1266 return; 1267 } 1268 if (type.match(AjxDateUtil.RE_MONTH)) { 1269 var month = date.getMonth(); 1270 date.setMonth(month + amount); 1271 // avoid roll 1272 if (Math.abs(month + amount) % 12 != date.getMonth()) { 1273 date.setDate(0); 1274 } 1275 return; 1276 } 1277 if (type.match(AjxDateUtil.RE_WEEK)) { 1278 date.setDate(date.getDate() + amount * 7); 1279 return; 1280 } 1281 if (type.match(AjxDateUtil.RE_DAY)) { 1282 date.setDate(date.getDate() + amount); 1283 return; 1284 } 1285 if (type.match(AjxDateUtil.RE_HOUR)) { 1286 date.setHours(date.getHours() + amount); 1287 return; 1288 } 1289 if (type.match(AjxDateUtil.RE_MINUTE)) { 1290 date.setMinutes(date.getMinutes() + amount); 1291 return; 1292 } 1293 if (type.match(AjxDateUtil.RE_SECOND)) { 1294 date.setSeconds(date.getSeconds() + amount); 1295 return; 1296 } 1297 if (type.match(AjxDateUtil.RE_MILLISECOND)) { 1298 date.setMilliseconds(date.getMilliseconds() + amount); 1299 return; 1300 } 1301 if (type.match(AjxDateUtil.RE_MONTHNAME)) { 1302 var monthnum = AjxDateUtil.MONTHNAME2MONTHNUM[type.toLowerCase()]; 1303 if (monthnum < date.getMonth()) { 1304 amount += amount > 0 ? 0 : 1; 1305 } 1306 else if (monthnum > date.getMonth()) { 1307 amount += amount > 0 ? -1 : 0; 1308 } 1309 date.setFullYear(date.getFullYear() + amount, monthnum, 1); 1310 return; 1311 } 1312 if (type.match(AjxDateUtil.RE_DAYNAME)) { 1313 var daynum = AjxDateUtil.WEEKDAYS[type.toLowerCase()]; 1314 if (daynum < date.getDay()) { 1315 amount += amount > 0 ? 0 : 1; 1316 } 1317 else if (daynum > date.getDay()) { 1318 amount += amount > 0 ? -1 : 0; 1319 } 1320 date.setDate(date.getDate() + (daynum - date.getDay()) + 7 * amount); 1321 return; 1322 } 1323 throw "unknown type: "+type; 1324 }; 1325 1326 AjxDateUtil.__calculate_add_ordinal = 1327 function() { 1328 throw "TODO: not implemented"; 1329 }; 1330 1331 AjxDateUtil.__calculate_set = 1332 function(date, type, value) { 1333 var args = value.split(/,/); 1334 //Add support for Japanese Heisei year format represented by H{year-number} 1335 //The year is H23 in H23/12/31, means 2011/12/31; we get that by adding year 1988 to 23 1336 //For example: H23 = 23 + 1988 = 2011(English year) 1337 if(args[0].indexOf("H") == 0) { 1338 args[0] = parseInt(args[0].replace("H", "")) + 1988; 1339 } 1340 if (type.match(AjxDateUtil.RE_YEAR)) { 1341 args[0] = AjxDateUtil.__calculate_fullYear(args[0]); // year 1342 if (args[1] != null) args[1] = AjxDateUtil.__calculate_month(args[1]); // month 1343 if (args[2] != null) args[2] = parseInt(args[2], 10); // date 1344 date.setFullYear.apply(date, args); 1345 return; 1346 } 1347 if (type.match(AjxDateUtil.RE_MONTH)) { 1348 args[0] = AjxDateUtil.__calculate_month(args[0]); // month 1349 if (args[1] != null) args[1] = parseInt(args[1], 10); // date 1350 date.setMonth.apply(date, args); 1351 return; 1352 } 1353 if (type.match(AjxDateUtil.RE_WEEK)) { 1354 var ord = AjxDateUtil.__calculate_week(args[0]); // week 1355 var day = args[1] ? AjxDateUtil.__calculate_day(args[1]) : date.getDay(); // day 1356 1357 var target; 1358 if (ord != -1) { 1359 var firstday = new Date(date.getFullYear(), date.getMonth(), 1, 12, 0, 0, 0); 1360 var firstdow = firstday.getDay(); 1361 var delta = firstdow - day; 1362 1363 target = new Date(firstday.getTime()); 1364 target.setDate(1 - delta); 1365 if (delta > 0) { 1366 target.setDate(target.getDate() + 7); 1367 } 1368 target.setDate(target.getDate() + 7 * ord); 1369 } 1370 else { 1371 var lastday = new Date(date.getFullYear(), date.getMonth()+1, 0, 12, 0, 0, 0); 1372 1373 target = new Date(lastday.getTime()); 1374 target.setDate(target.getDate() - (target.getDay() - day)); 1375 if (target.getMonth() != lastday.getMonth()) { 1376 target.setDate(target.getDate() - 7); 1377 } 1378 } 1379 1380 if (target && (date.getMonth() == target.getMonth())) { 1381 date.setTime(target.getTime()); 1382 } 1383 return; 1384 } 1385 if (type.match(AjxDateUtil.RE_DATE)) { 1386 args[0] = parseInt(args[0], 10); // date 1387 date.setDate.apply(date, args); 1388 return; 1389 } 1390 if (type.match(AjxDateUtil.RE_HOUR)) { 1391 args[0] = parseInt(args[0], 10); // hour 1392 if (args[1] != null) args[1] = parseInt(args[1], 10); // minutes 1393 if (args[2] != null) args[2] = parseInt(args[2], 10); // seconds 1394 if (args[3] != null) args[3] = parseInt(args[3], 10); // milliseconds 1395 date.setHours.apply(date, args); 1396 return; 1397 } 1398 if (type.match(AjxDateUtil.RE_MINUTE)) { 1399 args[0] = parseInt(args[0], 10); // minutes 1400 if (args[1] != null) args[1] = parseInt(args[1], 10); // seconds 1401 if (args[2] != null) args[2] = parseInt(args[2], 10); // milliseconds 1402 date.setMinutes.apply(date, args); 1403 return; 1404 } 1405 if (type.match(AjxDateUtil.RE_SECOND)) { 1406 args[0] = parseInt(args[0], 10); // seconds 1407 if (args[1] != null) args[1] = parseInt(args[1], 10); // milliseconds 1408 date.setSeconds.apply(date, args); 1409 return; 1410 } 1411 if (type.match(AjxDateUtil.RE_MILLISECOND)) { 1412 date.setMilliseconds.apply(date, args); // milliseconds 1413 return; 1414 } 1415 throw "unknown type: "+type; 1416 }; 1417 1418 AjxDateUtil.__calculate_fullYear = 1419 function(value) { 1420 if (value.length == 2) { 1421 var d = new Date; 1422 d.setYear(parseInt(value, 10)); 1423 var fullYear = d.getFullYear(); 1424 if (fullYear <= AjxMsg.dateParsing2DigitStartYear) { 1425 value = String(fullYear + 100); 1426 } 1427 else { 1428 value = String(fullYear).substr(0,2) + value; 1429 } 1430 } 1431 return parseInt(value, 10); 1432 }; 1433 1434 AjxDateUtil.__calculate_month = 1435 function(value) { 1436 var monthnum = AjxDateUtil.MONTHNAME2MONTHNUM[value.toLowerCase()]; 1437 return monthnum != null ? monthnum : parseInt(value, 10) - 1; 1438 }; 1439 1440 AjxDateUtil.__calculate_week = function(value) { 1441 for (var i = 0; i < AjxDateUtil.WEEKORD_RE.length; i++) { 1442 if (value.match(AjxDateUtil.WEEKORD_RE[i])) { 1443 if (i == AjxDateUtil.WEEKORD_RE.length - 1) { 1444 return -1; 1445 } 1446 return i; 1447 } 1448 } 1449 return 0; 1450 }; 1451 1452 AjxDateUtil.__calculate_day = 1453 function(value) { 1454 var daynum = AjxDateUtil.WEEKDAYS[value.toLowerCase()]; 1455 return daynum != null ? daynum : parseInt(value, 10); 1456 }; 1457