keith_miller@apple.com | bcc77f2 | 2016-07-15 06:03:25 +0000 | [diff] [blame] | 1 | //Date.library.js |
| 2 | // Copyright 2009 the Sputnik authors. All rights reserved. |
| 3 | // This code is governed by the BSD license found in the LICENSE file. |
| 4 | |
| 5 | //15.9.1.2 Day Number and Time within Day |
| 6 | function Day(t) { |
| 7 | return Math.floor(t/msPerDay); |
| 8 | } |
| 9 | |
| 10 | function TimeWithinDay(t) { |
| 11 | return t%msPerDay; |
| 12 | } |
| 13 | |
| 14 | //15.9.1.3 Year Number |
| 15 | function DaysInYear(y){ |
| 16 | if(y%4 != 0) return 365; |
| 17 | if(y%4 == 0 && y%100 != 0) return 366; |
| 18 | if(y%100 == 0 && y%400 != 0) return 365; |
| 19 | if(y%400 == 0) return 366; |
| 20 | } |
| 21 | |
| 22 | function DayFromYear(y) { |
| 23 | return (365*(y-1970) |
| 24 | + Math.floor((y-1969)/4) |
| 25 | - Math.floor((y-1901)/100) |
| 26 | + Math.floor((y-1601)/400)); |
| 27 | } |
| 28 | |
| 29 | function TimeFromYear(y){ |
| 30 | return msPerDay*DayFromYear(y); |
| 31 | } |
| 32 | |
| 33 | function YearFromTime(t) { |
| 34 | t = Number(t); |
| 35 | var sign = ( t < 0 ) ? -1 : 1; |
| 36 | var year = ( sign < 0 ) ? 1969 : 1970; |
| 37 | |
| 38 | for(var time = 0;;year += sign){ |
| 39 | time = TimeFromYear(year); |
| 40 | |
| 41 | if(sign > 0 && time > t){ |
| 42 | year -= sign; |
| 43 | break; |
| 44 | } |
| 45 | else if(sign < 0 && time <= t){ |
| 46 | break; |
| 47 | } |
| 48 | }; |
| 49 | return year; |
| 50 | } |
| 51 | |
| 52 | function InLeapYear(t){ |
| 53 | if(DaysInYear(YearFromTime(t)) == 365) |
| 54 | return 0; |
| 55 | |
| 56 | if(DaysInYear(YearFromTime(t)) == 366) |
| 57 | return 1; |
| 58 | } |
| 59 | |
| 60 | function DayWithinYear(t) { |
| 61 | return Day(t)-DayFromYear(YearFromTime(t)); |
| 62 | } |
| 63 | |
| 64 | //15.9.1.4 Month Number |
| 65 | function MonthFromTime(t){ |
| 66 | var day = DayWithinYear(t); |
| 67 | var leap = InLeapYear(t); |
| 68 | |
| 69 | if((0 <= day) && (day < 31)) return 0; |
| 70 | if((31 <= day) && (day < (59+leap))) return 1; |
| 71 | if(((59+leap) <= day) && (day < (90+leap))) return 2; |
| 72 | if(((90+leap) <= day) && (day < (120+leap))) return 3; |
| 73 | if(((120+leap) <= day) && (day < (151+leap))) return 4; |
| 74 | if(((151+leap) <= day) && (day < (181+leap))) return 5; |
| 75 | if(((181+leap) <= day) && (day < (212+leap))) return 6; |
| 76 | if(((212+leap) <= day) && (day < (243+leap))) return 7; |
| 77 | if(((243+leap) <= day) && (day < (273+leap))) return 8; |
| 78 | if(((273+leap) <= day) && (day < (304+leap))) return 9; |
| 79 | if(((304+leap) <= day) && (day < (334+leap))) return 10; |
| 80 | if(((334+leap) <= day) && (day < (365+leap))) return 11; |
| 81 | } |
| 82 | |
| 83 | //15.9.1.5 Date Number |
| 84 | function DateFromTime(t) { |
| 85 | var day = DayWithinYear(t); |
| 86 | var month = MonthFromTime(t); |
| 87 | var leap = InLeapYear(t); |
| 88 | |
| 89 | if(month == 0) return day+1; |
| 90 | if(month == 1) return day-30; |
| 91 | if(month == 2) return day-58-leap; |
| 92 | if(month == 3) return day-89-leap; |
| 93 | if(month == 4) return day-119-leap; |
| 94 | if(month == 5) return day-150-leap; |
| 95 | if(month == 6) return day-180-leap; |
| 96 | if(month == 7) return day-211-leap; |
| 97 | if(month == 8) return day-242-leap; |
| 98 | if(month == 9) return day-272-leap; |
| 99 | if(month == 10) return day-303-leap; |
| 100 | if(month == 11) return day-333-leap; |
| 101 | } |
| 102 | |
| 103 | //15.9.1.6 Week Day |
| 104 | function WeekDay(t) { |
| 105 | var weekday = (Day(t)+4)%7; |
| 106 | return (weekday < 0 ? 7+weekday : weekday); |
| 107 | } |
| 108 | |
| 109 | //15.9.1.9 Daylight Saving Time Adjustment |
| 110 | $LocalTZ = (new Date()).getTimezoneOffset() / -60; |
| 111 | if (DaylightSavingTA((new Date()).valueOf()) !== 0) { |
| 112 | $LocalTZ -= 1; |
| 113 | } |
| 114 | var LocalTZA = $LocalTZ*msPerHour; |
| 115 | |
| 116 | function DaysInMonth(m, leap) { |
| 117 | m = m%12; |
| 118 | |
| 119 | //April, June, Sept, Nov |
| 120 | if(m == 3 || m == 5 || m == 8 || m == 10 ) { |
| 121 | return 30; |
| 122 | } |
| 123 | |
| 124 | //Jan, March, May, July, Aug, Oct, Dec |
| 125 | if(m == 0 || m == 2 || m == 4 || m == 6 || m == 7 || m == 9 || m == 11){ |
| 126 | return 31; |
| 127 | } |
| 128 | |
| 129 | //Feb |
| 130 | return 28+leap; |
| 131 | } |
| 132 | |
| 133 | function GetSundayInMonth(t, m, count){ |
| 134 | var year = YearFromTime(t); |
| 135 | var tempDate; |
| 136 | |
| 137 | if (count==='"first"') { |
| 138 | for (var d=1; d <= DaysInMonth(m, InLeapYear(t)); d++) { |
| 139 | tempDate = new Date(year, m, d); |
| 140 | if (tempDate.getDay()===0) { |
| 141 | return tempDate.valueOf(); |
| 142 | } |
| 143 | } |
| 144 | } else if(count==='"last"') { |
| 145 | for (var d=DaysInMonth(m, InLeapYear(t)); d>0; d--) { |
| 146 | tempDate = new Date(year, m, d); |
| 147 | if (tempDate.getDay()===0) { |
| 148 | return tempDate.valueOf(); |
| 149 | } |
| 150 | } |
| 151 | } |
| 152 | throw new Error("Unsupported 'count' arg:" + count); |
| 153 | } |
| 154 | /* |
| 155 | function GetSundayInMonth(t, m, count){ |
| 156 | var year = YearFromTime(t); |
| 157 | var leap = InLeapYear(t); |
| 158 | var day = 0; |
| 159 | |
| 160 | if(m >= 1) day += DaysInMonth(0, leap); |
| 161 | if(m >= 2) day += DaysInMonth(1, leap); |
| 162 | if(m >= 3) day += DaysInMonth(2, leap); |
| 163 | if(m >= 4) day += DaysInMonth(3, leap); |
| 164 | if(m >= 5) day += DaysInMonth(4, leap); |
| 165 | if(m >= 6) day += DaysInMonth(5, leap); |
| 166 | if(m >= 7) day += DaysInMonth(6, leap); |
| 167 | if(m >= 8) day += DaysInMonth(7, leap); |
| 168 | if(m >= 9) day += DaysInMonth(8, leap); |
| 169 | if(m >= 10) day += DaysInMonth(9, leap); |
| 170 | if(m >= 11) day += DaysInMonth(10, leap); |
| 171 | |
| 172 | var month_start = TimeFromYear(year)+day*msPerDay; |
| 173 | var sunday = 0; |
| 174 | |
| 175 | if(count === "last"){ |
| 176 | for(var last_sunday = month_start+DaysInMonth(m, leap)*msPerDay; |
| 177 | WeekDay(last_sunday)>0; |
| 178 | last_sunday -= msPerDay |
| 179 | ){}; |
| 180 | sunday = last_sunday; |
| 181 | } |
| 182 | else { |
| 183 | for(var first_sunday = month_start; |
| 184 | WeekDay(first_sunday)>0; |
| 185 | first_sunday += msPerDay |
| 186 | ){}; |
| 187 | sunday = first_sunday+7*msPerDay*(count-1); |
| 188 | } |
| 189 | |
| 190 | return sunday; |
| 191 | }*/ |
| 192 | |
| 193 | function DaylightSavingTA(t) { |
| 194 | // t = t-LocalTZA; |
| 195 | |
| 196 | var DST_start = GetSundayInMonth(t, $DST_start_month, $DST_start_sunday) + |
| 197 | $DST_start_hour*msPerHour + |
| 198 | $DST_start_minutes*msPerMinute; |
| 199 | |
| 200 | var k = new Date(DST_start); |
| 201 | |
| 202 | var DST_end = GetSundayInMonth(t, $DST_end_month, $DST_end_sunday) + |
| 203 | $DST_end_hour*msPerHour + |
| 204 | $DST_end_minutes*msPerMinute; |
| 205 | |
| 206 | if ( t >= DST_start && t < DST_end ) { |
| 207 | return msPerHour; |
| 208 | } else { |
| 209 | return 0; |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | //15.9.1.9 Local Time |
| 214 | function LocalTime(t){ |
| 215 | return t+LocalTZA+DaylightSavingTA(t); |
| 216 | } |
| 217 | |
| 218 | function UTC(t) { |
| 219 | return t-LocalTZA-DaylightSavingTA(t-LocalTZA); |
| 220 | } |
| 221 | |
| 222 | //15.9.1.10 Hours, Minutes, Second, and Milliseconds |
| 223 | function HourFromTime(t){ |
| 224 | return Math.floor(t/msPerHour)%HoursPerDay; |
| 225 | } |
| 226 | |
| 227 | function MinFromTime(t){ |
| 228 | return Math.floor(t/msPerMinute)%MinutesPerHour; |
| 229 | } |
| 230 | |
| 231 | function SecFromTime(t){ |
| 232 | return Math.floor(t/msPerSecond)%SecondsPerMinute; |
| 233 | } |
| 234 | |
| 235 | function msFromTime(t){ |
| 236 | return t%msPerSecond; |
| 237 | } |
| 238 | |
| 239 | //15.9.1.11 MakeTime (hour, min, sec, ms) |
| 240 | function MakeTime(hour, min, sec, ms){ |
| 241 | if ( !isFinite(hour) || !isFinite(min) || !isFinite(sec) || !isFinite(ms)) { |
| 242 | return Number.NaN; |
| 243 | } |
| 244 | |
| 245 | hour = ToInteger(hour); |
| 246 | min = ToInteger(min); |
| 247 | sec = ToInteger(sec); |
| 248 | ms = ToInteger(ms); |
| 249 | |
| 250 | return ((hour*msPerHour) + (min*msPerMinute) + (sec*msPerSecond) + ms); |
| 251 | } |
| 252 | |
| 253 | //15.9.1.12 MakeDay (year, month, date) |
| 254 | function MakeDay(year, month, date) { |
| 255 | if ( !isFinite(year) || !isFinite(month) || !isFinite(date)) { |
| 256 | return Number.NaN; |
| 257 | } |
| 258 | |
| 259 | year = ToInteger(year); |
| 260 | month = ToInteger(month); |
| 261 | date = ToInteger(date ); |
| 262 | |
| 263 | var result5 = year + Math.floor(month/12); |
| 264 | var result6 = month%12; |
| 265 | |
| 266 | var sign = ( year < 1970 ) ? -1 : 1; |
| 267 | var t = ( year < 1970 ) ? 1 : 0; |
| 268 | var y = ( year < 1970 ) ? 1969 : 1970; |
| 269 | |
| 270 | if( sign == -1 ){ |
| 271 | for ( y = 1969; y >= year; y += sign ) { |
| 272 | t += sign * DaysInYear(y)*msPerDay; |
| 273 | } |
| 274 | } else { |
| 275 | for ( y = 1970 ; y < year; y += sign ) { |
| 276 | t += sign * DaysInYear(y)*msPerDay; |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | var leap = 0; |
| 281 | for ( var m = 0; m < month; m++ ) { |
| 282 | //if year is changed, than we need to recalculate leep |
| 283 | leap = InLeapYear(t); |
| 284 | t += DaysInMonth(m, leap)*msPerDay; |
| 285 | } |
| 286 | |
| 287 | if ( YearFromTime(t) != result5 ) { |
| 288 | return Number.NaN; |
| 289 | } |
| 290 | if ( MonthFromTime(t) != result6 ) { |
| 291 | return Number.NaN; |
| 292 | } |
| 293 | if ( DateFromTime(t) != 1 ) { |
| 294 | return Number.NaN; |
| 295 | } |
| 296 | |
| 297 | return Day(t)+date-1; |
| 298 | } |
| 299 | |
| 300 | //15.9.1.13 MakeDate (day, time) |
| 301 | function MakeDate( day, time ) { |
| 302 | if(!isFinite(day) || !isFinite(time)) { |
| 303 | return Number.NaN; |
| 304 | } |
| 305 | |
| 306 | return day*msPerDay+time; |
| 307 | } |
| 308 | |
| 309 | //15.9.1.14 TimeClip (time) |
| 310 | function TimeClip(time) { |
| 311 | if(!isFinite(time) || Math.abs(time) > 8.64e15){ |
| 312 | return Number.NaN; |
| 313 | } |
| 314 | |
| 315 | return ToInteger(time); |
| 316 | } |
| 317 | |
| 318 | //Test Functions |
| 319 | //ConstructDate is considered deprecated, and should not be used directly from |
| 320 | //test262 tests as it's incredibly sensitive to DST start/end dates that |
| 321 | //vary with geographic location. |
| 322 | function ConstructDate(year, month, date, hours, minutes, seconds, ms){ |
| 323 | /* |
| 324 | * 1. Call ToNumber(year) |
| 325 | * 2. Call ToNumber(month) |
| 326 | * 3. If date is supplied use ToNumber(date); else use 1 |
| 327 | * 4. If hours is supplied use ToNumber(hours); else use 0 |
| 328 | * 5. If minutes is supplied use ToNumber(minutes); else use 0 |
| 329 | * 6. If seconds is supplied use ToNumber(seconds); else use 0 |
| 330 | * 7. If ms is supplied use ToNumber(ms); else use 0 |
| 331 | * 8. If Result(1) is not NaN and 0 <= ToInteger(Result(1)) <= 99, Result(8) is |
| 332 | * 1900+ToInteger(Result(1)); otherwise, Result(8) is Result(1) |
| 333 | * 9. Compute MakeDay(Result(8), Result(2), Result(3)) |
| 334 | * 10. Compute MakeTime(Result(4), Result(5), Result(6), Result(7)) |
| 335 | * 11. Compute MakeDate(Result(9), Result(10)) |
| 336 | * 12. Set the [[Value]] property of the newly constructed object to TimeClip(UTC(Result(11))) |
| 337 | */ |
| 338 | var r1 = Number(year); |
| 339 | var r2 = Number(month); |
| 340 | var r3 = ((date && arguments.length > 2) ? Number(date) : 1); |
| 341 | var r4 = ((hours && arguments.length > 3) ? Number(hours) : 0); |
| 342 | var r5 = ((minutes && arguments.length > 4) ? Number(minutes) : 0); |
| 343 | var r6 = ((seconds && arguments.length > 5) ? Number(seconds) : 0); |
| 344 | var r7 = ((ms && arguments.length > 6) ? Number(ms) : 0); |
| 345 | |
| 346 | var r8 = r1; |
| 347 | |
| 348 | if(!isNaN(r1) && (0 <= ToInteger(r1)) && (ToInteger(r1) <= 99)) |
| 349 | r8 = 1900+r1; |
| 350 | |
| 351 | var r9 = MakeDay(r8, r2, r3); |
| 352 | var r10 = MakeTime(r4, r5, r6, r7); |
| 353 | var r11 = MakeDate(r9, r10); |
| 354 | |
| 355 | var retVal = TimeClip(UTC(r11)); |
| 356 | return retVal; |
| 357 | } |
| 358 | |
| 359 | |
| 360 | |
| 361 | /**** Python code for initialize the above constants |
| 362 | // We may want to replicate the following in JavaScript. |
| 363 | // However, using JS date operations to generate parameters that are then used to |
| 364 | // test those some date operations seems unsound. However, it isn't clear if there |
| 365 | //is a good interoperable alternative. |
| 366 | |
| 367 | # Copyright 2009 the Sputnik authors. All rights reserved. |
| 368 | # This code is governed by the BSD license found in the LICENSE file. |
| 369 | |
| 370 | def GetDaylightSavingsTimes(): |
| 371 | # Is the given floating-point time in DST? |
| 372 | def IsDst(t): |
| 373 | return time.localtime(t)[-1] |
| 374 | # Binary search to find an interval between the two times no greater than |
| 375 | # delta where DST switches, returning the midpoint. |
| 376 | def FindBetween(start, end, delta): |
| 377 | while end - start > delta: |
| 378 | middle = (end + start) / 2 |
| 379 | if IsDst(middle) == IsDst(start): |
| 380 | start = middle |
| 381 | else: |
| 382 | end = middle |
| 383 | return (start + end) / 2 |
| 384 | now = time.time() |
| 385 | one_month = (30 * 24 * 60 * 60) |
| 386 | # First find a date with different daylight savings. To avoid corner cases |
| 387 | # we try four months before and after today. |
| 388 | after = now + 4 * one_month |
| 389 | before = now - 4 * one_month |
| 390 | if IsDst(now) == IsDst(before) and IsDst(now) == IsDst(after): |
| 391 | logger.warning("Was unable to determine DST info.") |
| 392 | return None |
| 393 | # Determine when the change occurs between now and the date we just found |
| 394 | # in a different DST. |
| 395 | if IsDst(now) != IsDst(before): |
| 396 | first = FindBetween(before, now, 1) |
| 397 | else: |
| 398 | first = FindBetween(now, after, 1) |
| 399 | # Determine when the change occurs between three and nine months from the |
| 400 | # first. |
| 401 | second = FindBetween(first + 3 * one_month, first + 9 * one_month, 1) |
| 402 | # Find out which switch is into and which if out of DST |
| 403 | if IsDst(first - 1) and not IsDst(first + 1): |
| 404 | start = second |
| 405 | end = first |
| 406 | else: |
| 407 | start = first |
| 408 | end = second |
| 409 | return (start, end) |
| 410 | |
| 411 | |
| 412 | def GetDaylightSavingsAttribs(): |
| 413 | times = GetDaylightSavingsTimes() |
| 414 | if not times: |
| 415 | return None |
| 416 | (start, end) = times |
| 417 | def DstMonth(t): |
| 418 | return time.localtime(t)[1] - 1 |
| 419 | def DstHour(t): |
| 420 | return time.localtime(t - 1)[3] + 1 |
| 421 | def DstSunday(t): |
| 422 | if time.localtime(t)[2] > 15: |
| 423 | return "'last'" |
| 424 | else: |
| 425 | return "'first'" |
| 426 | def DstMinutes(t): |
| 427 | return (time.localtime(t - 1)[4] + 1) % 60 |
| 428 | attribs = { } |
| 429 | attribs['start_month'] = DstMonth(start) |
| 430 | attribs['end_month'] = DstMonth(end) |
| 431 | attribs['start_sunday'] = DstSunday(start) |
| 432 | attribs['end_sunday'] = DstSunday(end) |
| 433 | attribs['start_hour'] = DstHour(start) |
| 434 | attribs['end_hour'] = DstHour(end) |
| 435 | attribs['start_minutes'] = DstMinutes(start) |
| 436 | attribs['end_minutes'] = DstMinutes(end) |
| 437 | return attribs |
| 438 | |
| 439 | *********/ |