document-lastModified-utils.js (3745B)
1 const DOCUMENT_LASTMODIFIED_REGEX = /^([0-9]{2})\/([0-9]{2})\/([0-9]{4}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/; 2 3 function assert_document_lastmodified_string_approximately_now(str) { 4 // We want to test that |str| was a time in the user's local 5 // timezone generated within a few seconds prior to the present. 6 // This requires some care, since it is possible that: 7 // - the few second difference may have crossed a 8 // year/month/day/hour/minute boundary 9 // - the few second difference may have crossed a change in the 10 // local timezone's UTC offset 11 // - the local time might be one that has multiple valid UTC 12 // representations (for example, because it's in the hour 13 // following a shift from summer time to winter time) 14 // We will make some assumptions to do this: 15 // - local time's UTC offset doesn't change more than once per 16 // minute 17 // - local time's UTC offset only changes by integral numbers of 18 // minutes 19 20 // The date must be equal to or earlier than the present time. 21 var dmax = new Date(); 22 23 // The date must be equal to or later than 2.5 seconds ago. 24 var TOLERANCE_MILLISECONDS = 2500; 25 var dmin = new Date(); 26 dmin.setTime(dmax.getTime() - TOLERANCE_MILLISECONDS); 27 28 // Extract the year/month/date/hours/minutes/seconds from str. It 29 // is important that we do *not* try to construct a Date object from 30 // these, since the core of the date object is a timestamp in UTC, 31 // and there are cases (such as the hour on each side of a change 32 // from summer time to winter time) where there are multiple 33 // possible UTC timestamps for a given YYYY-MM-DD HH:MM:SS, and 34 // constructing a Date object would pick one of them, which might be 35 // the wrong one. However, we already have the right one in dmin 36 // and dmax, so we should instead extract local time from those 37 // rather than converting these values to UTC. 38 var m = DOCUMENT_LASTMODIFIED_REGEX.exec(str); 39 var syear = Number(m[3]); 40 var smonth = Number(m[1]) - 1; // match Javascript 0-based months 41 var sdate = Number(m[2]); 42 var shours = Number(m[4]); 43 var sminutes = Number(m[5]); 44 var sseconds = Number(m[6]); 45 46 if (dmin.getFullYear() == dmax.getFullYear() && 47 dmin.getMonth() == dmax.getMonth() && 48 dmin.getDate() == dmax.getDate() && 49 dmin.getHours() == dmax.getHours() && 50 dmin.getMinutes() == dmax.getMinutes()) { 51 // min and max have the same minute 52 assert_equals(smonth, dmin.getMonth(), "month"); 53 assert_equals(sdate, dmin.getDate(), "date"); 54 assert_equals(syear, dmin.getFullYear(), "year"); 55 assert_equals(shours, dmin.getHours(), "hours"); 56 assert_equals(sminutes, dmin.getMinutes(), "minutes"); 57 assert_true(dmin.getSeconds() <= sseconds && 58 sseconds <= dmax.getSeconds(), "seconds"); 59 } else if (dmin.getFullYear() == syear && 60 dmin.getMonth() == smonth && 61 dmin.getDate() == sdate && 62 dmin.getHours() == shours && 63 dmin.getMinutes() == sminutes) { 64 // actual value has the same minute as min 65 assert_true(dmin.getSeconds() <= sseconds, "dmin.getSeconds() <= sseconds"); 66 assert_true(57 <= dmin.getSeconds(), "unexpected local time rules (dmin match)"); 67 } else if (dmax.getFullYear() == syear && 68 dmax.getMonth() == smonth && 69 dmax.getDate() == sdate && 70 dmax.getHours() == shours && 71 dmax.getMinutes() == sminutes) { 72 // actual value has the same minute as max 73 assert_true(sseconds <= dmax.getSeconds(), "sseconds <= dmax.getSeconds()"); 74 assert_true(dmax.getSeconds() <= 2, "unexpected local time rules (dmax match)"); 75 } else { 76 assert_unreached("unexpected local time rules (no match)"); 77 } 78 }