// ==UserScript== // // @name eBay Local Time // @description Converts times on ebay.com and ebay.co.uk to the user's local time // @author nice_bow_tie // // @homepage http://userscripts.org/scripts/show/153187 // // @include http://*.ebay.* // // @grant GM_getValue // @grant GM_setValue // // @version 1.2 // // @history 1.2 Add option to toggle between 24 hour and AM/PM display // @history 1.1 Convert some additional date-time fields // @history 1.0 Initial release // // @namespace https://greasyfork.org/users/2542 // @downloadURL https://update.greasyfork.icu/scripts/2195/eBay%20Local%20Time.user.js // @updateURL https://update.greasyfork.icu/scripts/2195/eBay%20Local%20Time.meta.js // ==/UserScript== (function(){ //RFC-2822 month names var standard_months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; var eng_long_months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; var eng_weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; var site_data = {}; //========================================================================= // site_data contains the data used by the script for each supported site //========================================================================= // // site_data[site].abbr_month_names // ------------------------------------------------------------------------ // an array of short month names used on the site // // site_data[site].long_month_names // ------------------------------------------------------------------------ // an array of long month names used on the site // // site_data[site].weekday_names // ------------------------------------------------------------------------ // an array of weekday names used on the site // // site_data[site].timezones // ------------------------------------------------------------------------ // an array of timezones used on the site, such as PST or BST // the timezone name is followed by the signed 4-digit offset from UTC, // for example "PST:-0800" // // site_data[site].timezone_periods // ------------------------------------------------------------------------ // an object defining valid time periods for each timezone, used to find // the correct timezone when it's not part of the displayed time; // each entry is an array of time periods in the format // YYYYMMDDHHMMSS-YYYYMMDDHHMMSS, which define the times when that // timezone is used // // site_data[site].locations // ------------------------------------------------------------------------ // an array of {selector, format} objects that identify the location and // format of the times the script should change: // --------------------------------------------------------------------- // 'selector' = a CSS selector string that selects the nodes containing // times in the format described by 'format' // if the time is split across > 1 node (eg. the date in one node and // the time in another), those nodes must be consecutive in the list of // nodes matching the selector // --------------------------------------------------------------------- // 'format' = a string describing the format of the date/time text in // the nodes (eg. "m-d-y|h:i:s T") // the following placeholders are used to indicate where in the text // that component should appear: // W : weekday name // d : 2-digit day number (01-31) // D : 1 or 2-digit day number (1-31) // m : short month name // M : long month name // n : 2-digit month number (01-12) // Y : 4-digit year // y : 2-digit year // h : 2-digit hour (00-23) // i : 2-digit minute (00-59) // s : 2-digit second (00-59) // T : timezone // other special characters are: // space : matches one or more whitespace characters // | : indicates where the text is split across nodes // all other characters in the format string are treated as literals // //========================================================================= site_data["ebay.com"] = {}; site_data["ebay.com"].abbr_month_names = standard_months; site_data["ebay.com"].long_month_names = eng_long_months; site_data["ebay.com"].weekday_names = eng_weekdays; site_data["ebay.com"].timezones = ["PST:-0800", "PDT:-0700"]; site_data["ebay.com"].timezone_periods = { PST: [ "20080101000000-20080309015959", "20081102020000-20090308015959", "20091101020000-20100314015959", "20101107020000-20110313015959", "20111106020000-20120311015959", "20121104020000-20130310015959", "20131103020000-20140309015959", "20141102020000-20150308015959", "20151101020000-20160313015959", "20161106020000-20170312015959", "20171105020000-20180311015959", "20181104020000-20190310015959", "20191103020000-20200308015959" ], PDT: [ "20080309030000-20081102005959", "20090308030000-20091101005959", "20100314030000-20101107005959", "20110313030000-20111106005959", "20120311030000-20121104005959", "20130310030000-20131103005959", "20140309030000-20141102005959", "20150308030000-20151101005959", "20160313030000-20161106005959", "20170312030000-20171105005959", "20180311030000-20181104005959", "20190310030000-20191103005959" ] }; site_data["ebay.com"].locations = [ //view item page { selector : "div.lable~div span, .vi-is1-dt-eu>span, .vi-is1-dt>span", format : "m d, Y|h:i:s T" }, //revision summary page / bid history page { selector : ".pagecontainer td:nth-child(-n+2), td>div>span, .titleValueFont", format : "m-d-y|h:i:s T" }, //purchase history page / bid history page (ending time) { selector : ".pagecontainer td, .titleValueFont", format : "m-d-y h:i:s T" }, //'last revised' time { selector : ".vi-desc-revHistory div, .vi-cmb>div", format : "m d, Y h:i:s T" }, //item page new design: active listings { selector : ".vi-VR-enddate-sec, .rev-date", format : "m d, Y h:i:s T" }, //item page new design: active listings { selector : ".vi-VR-enddate-no-sec", format : "m d h:i:s T" }, //item new page design: completed listings { selector : ".vi-ended, .vi-ended .endedDate span", format : "m d, Y|h:i:s T" }, //my ebay { selector : ".my_itl-itR div>span", format : "n/d/y at h:i:s" }, //search results { selector : "span.tme>span, div.timeLeftInfo>span", format : "m-d h:i" }, //feedback { selector : ".FbOuterYukon td:last-child", format : "m-d-y h:i" }, ]; site_data["ebay.co.uk"] = {}; site_data["ebay.co.uk"].abbr_month_names = standard_months; site_data["ebay.co.uk"].long_month_names = eng_long_months; site_data["ebay.co.uk"].weekday_names = eng_weekdays; site_data["ebay.co.uk"].timezones = ["GMT:+0000", "BST:+0100"]; site_data["ebay.co.uk"].timezone_periods = { GMT: [ "20080101000000-20080330005959", "20081026020000-20090329005959", "20091025020000-20100328005959", "20101031020000-20110327005959", "20111030020000-20120325005959", "20121028020000-20130331005959", "20131027020000-20140330005959", "20141026020000-20150329005959", "20151025020000-20160327005959", "20161030020000-20170326005959", "20171029020000-20180325005959", "20181028020000-20190331005959", "20191027020000-20200329005959" ], BST: [ "20080330020000-20081026005959", "20090329020000-20091025005959", "20100328020000-20101031005959", "20110327020000-20111030005959", "20120325020000-20121028005959", "20130331020000-20131027005959", "20140330020000-20141026005959", "20150329020000-20151025005959", "20160327020000-20161030005959", "20170326020000-20171029005959", "20180325020000-20181028005959", "20190331020000-20191027005959" ] }; site_data["ebay.co.uk"].locations = [ //view item page { selector : "div.lable~div span, .vi-is1-dt-eu>span, .vi-is1-dt>span", format : "d m, Y|h:i:s T" }, //revision summary page / bid history page { selector : ".pagecontainer td:nth-child(-n+2), td>div>span, .titleValueFont", format : "d-m-y|h:i:s T" }, //purchase history page / bid history page (ending time) { selector : ".pagecontainer td, .titleValueFont", format : "d-m-y h:i:s T" }, //'last revised' time { selector : ".vi-desc-revHistory div, .vi-cmb>div", format : "d m, Y h:i:s T" }, //item page new design: active listings { selector : ".vi-VR-enddate-sec, .rev-date", format : "d m, Y h:i:s T" }, //item page new design: active listings { selector : ".vi-VR-enddate-no-sec", format : "d m h:i:s T" }, //item new page design: completed listings { selector : ".vi-ended, .vi-ended .endedDate span", format : "d m, Y|h:i:s T" }, //my ebay { selector : ".my_itl-itR div>span", format : "d/n/y at h:i:s" }, //search results { selector : "span.tme>span, div.timeLeftInfo>span", format : "d-m h:i" }, //feedback { selector : ".FbOuterYukon td:last-child", format : "d-m-y h:i" }, ]; //========================================================================= //get the site data for the current page var hostname = window.location.hostname; while (hostname && !site_data[hostname]) hostname = hostname.substr(hostname.indexOf(".")+1 || 9999); if (!(site_data = site_data[hostname])) return; var timezones = []; site_data.timezones.forEach(function(tz) { timezones = timezones.concat(tz.split(":"));}); var tz_periods = []; timezones.forEach(function(tz) { if (site_data.timezone_periods && site_data.timezone_periods[tz]) { site_data.timezone_periods[tz].forEach(function(tz_period) { if (/^\d{14}-\d{14}$/.test(tz_period)) { var a = tz_period.split("-"); tz_periods.push({start:a[0], end:a[1], tz:tz}); } }); } }); //regexps that match the format string placeholders var regexp_strings = { W: site_data.weekday_names.join("|"), d: "0?[1-9]|[1-2][0-9]|3[0-1]", D: "[1-9]|[1-2][0-9]|3[0-1]", m: site_data.abbr_month_names.join("|"), M: site_data.long_month_names.join("|"), n: "0[1-9]|1[012]", Y: "20[0-2][0-9]", y: "[0-2][0-9]", h: "[0-1][0-9]|2[0-3]", i: "[0-5][0-9]", s: "[0-5][0-9]", T: timezones.join("|").replace(/[+-]/g, "\\$&"), }; var use_12_hour_clock = false; if (typeof GM_getValue == "function") use_12_hour_clock = !!GM_getValue("Use12HourClock"); //chrome workaround if (window.chrome) use_12_hour_clock = JSON.parse(localStorage.getItem("Use12HourClock")); (function ProcessPage() { var found_nodes = []; //find and process the nodes matching each location selector site_data.locations.forEach(function(location_data) { var nodes = []; var qsa = document.querySelectorAll(location_data.selector.replace(/\s*(,|$)/g, ":not([nbt-processed])$1")); for (var i = 0; i < qsa.length; i++) { found_nodes.push(qsa[i]); for (var j = 0; j < qsa[i].childNodes.length; j++) { var child = qsa[i].childNodes[j]; if(child.nodeType == 3 && child.data.match(/\S/)) nodes.push(child); } } var format_array = location_data.format.split("|"); var node_count = format_array.length; while (nodes.length >= node_count) { if (!ProcessNode(nodes, format_array, node_count)) nodes.shift(); } }); found_nodes.forEach(function(n){n.setAttribute("nbt-processed", 1);}); window.setTimeout(ProcessPage, 2000); })(); //process node_count text nodes from the node_list //if successful, remove the matched node(s) and return true function ProcessNode(node_list, format_array, node_count) { var node_data = []; for (var i = 0; i < node_count; i++) { var format_string = format_array[i]; //create regexp from the format string and match against the text var re = ""; for (var j = 0; j < format_string.length; j++) { var c = format_string[j]; var str = regexp_strings[c] || c.replace(/[-.+\\{}?*()|[\]]/, "\\$&") .replace(" ", "\\s+"); re += "(" + str + ")"; } re = "^([\\s\\S]*?)" + re + "([\\s\\S]*?)$"; var match_result; if (match_result = node_list[i].data.match(re)) { match_result.shift(); match_result.prefix = match_result.shift(); match_result.suffix = match_result.pop(); //match_result[0] -> match_result[n] now contain the node text //corresponding to characters 0 -> n of the format string, //also set node_data[c] to the text corresponding to each //placeholder c in the format string for (var j = 0; j < format_string.length; j++) { var c = format_string[j]; if (regexp_strings[c]) node_data[c] = match_result[j]; } node_data.push(match_result); } else return; } //check it's valid if (node_data.length != node_count || !(node_data['m'] || node_data['M'] || node_data['n']) || !(node_data['d'] || node_data['D']) || !(node_data['h']) ) return; //create array of strings to be used in the Date.parse argument var parse_array = []; parse_array[0] = node_data['d'] || node_data['D']; var month_number = site_data.abbr_month_names.indexOf(node_data['m'])+1 || site_data.long_month_names.indexOf(node_data['M'])+1 || node_data['n']; parse_array[1] = standard_months[month_number-1]; var year = node_data['Y'] || ("20" + node_data['y']); if (year.length != 4) //if not found, assume it's the current year year = new Date().getFullYear() + ""; parse_array[2] = year; parse_array[3] = node_data['h']; parse_array[4] = node_data['i'] || "00"; parse_array[5] = node_data['s'] || "00"; if (!node_data['T']) { //no timezone specified, so get it from tz_periods var date_str = year; date_str += ("0"+month_number).substr(-2); date_str += ("0"+parse_array[0]).substr(-2); date_str += parse_array[3] + parse_array[4] + parse_array[5]; tz_periods.some(function(tz_period) { if (date_str >= tz_period.start && date_str <= tz_period.end) return (node_data['T'] = tz_period.tz); }); if (!node_data['T']) return; } var next_tz = timezones[timezones.indexOf(node_data['T'])+1]; if (!/^[+-]\d{4}$/.test(next_tz)) return; parse_array[6] = next_tz; //parse the time using RFC-2822 format var ts = Date.parse("0 1 2 3:4:5 6".replace(/\d/g, function(i) {return parse_array[i];})); if (!isNaN(ts)) { function fmt(n, pad) {return (n<10 && pad) ? "0"+n : ""+n;} //convert to local time var date = new Date(ts); var new_date = { W: site_data.weekday_names[date.getDay()], d: fmt(date.getDate(), true), D: fmt(date.getDate(), false), m: site_data.abbr_month_names[date.getMonth()], M: site_data.long_month_names[date.getMonth()], n: fmt(date.getMonth()+1, true), Y: fmt(date.getFullYear()), y: fmt(date.getFullYear()).substr(2), h: fmt(date.getHours(), true), i: fmt(date.getMinutes(), true), s: fmt(date.getSeconds(), true), }; //save the original date/time for the tooltip var tooltip = []; for (var i = 0; i < node_count; i++) tooltip.push(node_data[i].join("")); tooltip = tooltip.join(" ").replace(/\s+/g, " "); //update the node data with the new local time values var changed = false; node_data.forEach(function(node_data, i) { var am_pm = ""; var format_str = format_array[i]; for (var j = 0; j < format_str.length; j++) { var c = format_str[j]; if (c == "T" || (c == " " && format_str[j+1] == "T")) node_data[j] = ""; //remove the timezone else if (new_date[c] && new_date[c] != node_data[j]) { node_data[j] = new_date[c]; changed = true; if (c == "h" && use_12_hour_clock) { //change to 12 hour time var hours = +node_data[j]; am_pm = (hours < 12) ? " AM" : " PM"; node_data[j] = (hours + 11) % 12 + 1; } } } if (am_pm != "") node_data.push(am_pm); }); for (var i = 0; i < node_count; i++) { var node = node_list.shift(); if (changed) { //the time has changed, so update the DOM with the new time var new_node = document.createElement("span"); new_node.className = "nbt-localtime"; new_node.style.fontSize = "inherit"; var prefix = node_data[i].prefix; if (prefix) new_node.appendChild(document.createTextNode(prefix)); var span = document.createElement("span"); span.title = tooltip; span.style.fontSize = "inherit"; var t = document.createTextNode(node_data[i].join("")); span.appendChild(t); new_node.appendChild(span); var suffix = node_data[i].suffix; if (suffix) new_node.appendChild(document.createTextNode(suffix)); if (i == node_count-1) { var star = document.createElement("b"); star.style.color = "#00a000"; star.style.fontSize = "inherit"; star.style.fontWeight = "normal"; star.style.cursor = "default"; star.appendChild(document.createTextNode(" * ")); var ref_node = new_node.lastChild; var index = suffix.search(/\s/); if (index == 0) ref_node = span; else if (index > 0) new_node.lastChild.splitText(index); new_node.insertBefore(star, ref_node.nextSibling); if (window.chrome) { //workaround for a Chrome (Webkit) bug that causes //the star to wrap onto a new line if (node.parentNode.className == "endedDate") { var bugfixer = document.createTextNode(" "); node.parentNode.insertBefore(bugfixer, node); } } } new_node.addEventListener("dblclick", ToggleTimeDisplay, false); node.parentNode.replaceChild(new_node, node); } } return true; } } function ToggleTimeDisplay() { var success = false; if (typeof GM_setValue == "function") { GM_setValue("Use12HourClock", !use_12_hour_clock); success = (GM_getValue("Use12HourClock") === !use_12_hour_clock); } //chrome workaround if (window.chrome) { localStorage.setItem("Use12HourClock", !use_12_hour_clock); success = (JSON.parse(localStorage.getItem("Use12HourClock")) === !use_12_hour_clock); } if (success) alert("eBay Local Time display has been changed to: " + (use_12_hour_clock ? "24 hour clock" : "12 hour clock (AM/PM)") + "\n\n(Takes effect from next page load)"); else alert("Error: eBay Local Time display setting could not be changed " + "(requires Greasemonkey or a Greasemonkey-compatible userscript manager)"); } })()