From c729e6d3a8075c11cab3896f25dd9e81af60217b Mon Sep 17 00:00:00 2001 From: JackDandy Date: Wed, 17 Dec 2014 18:25:23 +0000 Subject: [PATCH] Add lowercase PM to the General Config/Interface/Time style selection. Change re-factor sbdatetime classes. Add param "markup" to sbdatetime.sbftime() and sbdatetime.sbfdatetime() that when set True will return time dimensions HTML class wrapped. Add HTML class wrapping to the dimension parser of fuzzy time. Change General Config/Interface/Trim zero padding to Trim date and time, now handles 2:00 pm > 2 pm. Fix trim zero of military time hour to not use 12 hr time. --- CHANGES.md | 11 +- gui/slick/css/dark.css | 5 - gui/slick/css/light.css | 5 - gui/slick/css/style.css | 14 +- .../interfaces/default/config_general.tmpl | 39 +-- gui/slick/js/config.js | 179 ++++++----- gui/slick/js/fuzzyMoment.js | 284 ++++++++++-------- sickbeard/sbdatetime.py | 185 +++++------- 8 files changed, 370 insertions(+), 352 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 89d2737b..ac50f223 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,13 @@ ### 0.x.x (2014-12-xx xx:xx:xx UTC) -* Add network logo's for BBC Canada, Crackle, El Rey Network, SKY Atlantic, and Watch +* Add network logos BBC Canada, Crackle, El Rey Network, SKY Atlantic, and Watch * Change Yahoo! screen network logo -* Change added and updated Discovery Network's channel logo's -* Remove unrequired duplicate network logo's -* Add Discovery Network International/A&E Network International/Scripps Networks International missing channel logos +* Add and update Discovery Network's channel logos +* Add A&E Network International/Scripps Networks International channel logos +* Remove non required duplicate network logos +* Add lowercase PM to the General Config/Interface/Time style selection +* Change General Config/Interface/Trim zero padding to Trim date and time, now handles 2:00 pm > 2 pm +* Fix trim zero of military time hour to not use 12 hr time [develop changelog] diff --git a/gui/slick/css/dark.css b/gui/slick/css/dark.css index b78b3bc9..01402916 100644 --- a/gui/slick/css/dark.css +++ b/gui/slick/css/dark.css @@ -1375,11 +1375,6 @@ config*.tmpl padding: 12px 0px; } -#config div.field-pair input { - float: left; - margin-right: 6px; -} - #config .nocheck, #config div #customQuality, .metadataDiv { padding-left: 20px; } diff --git a/gui/slick/css/light.css b/gui/slick/css/light.css index 416ec481..b5b79722 100644 --- a/gui/slick/css/light.css +++ b/gui/slick/css/light.css @@ -1358,11 +1358,6 @@ config*.tmpl padding: 12px 0px; } -#config div.field-pair input { - float: left; - margin-right: 6px; -} - #config .nocheck, #config div #customQuality, .metadataDiv { padding-left: 20px; } diff --git a/gui/slick/css/style.css b/gui/slick/css/style.css index 7c00de52..bd02c682 100644 --- a/gui/slick/css/style.css +++ b/gui/slick/css/style.css @@ -153,7 +153,7 @@ inc_top.tmpl /* background: url("../images/bg.png") repeat 0 0 transparent; */ } -[class^="icon-"], +[class^="icon-"], [class*=" icon-"] { background-image: url("../images/glyphicons-halflings.png"); } @@ -1591,10 +1591,13 @@ config*.tmpl padding: 12px 0 } -.stepDiv .component-desc select, -.stepDiv .component-desc input, #config div.field-pair select, #config div.field-pair input { + margin-right: 6px; +} + +.stepDiv .component-desc select, +.stepDiv .component-desc input { margin-right: 15px; } @@ -3153,6 +3156,11 @@ span.token-input-delete-token { z-index: 0; background-image: url(../images/poster-dark.jpg) } + +.time-am-pm { + margin-left: 2px; +} + /* ======================================================================= jquery.confirm.css ========================================================================== */ diff --git a/gui/slick/interfaces/default/config_general.tmpl b/gui/slick/interfaces/default/config_general.tmpl index 02e2a74c..2817d0e0 100644 --- a/gui/slick/interfaces/default/config_general.tmpl +++ b/gui/slick/interfaces/default/config_general.tmpl @@ -103,9 +103,9 @@ as the default selection when adding new shows @@ -127,7 +127,7 @@ Show root directories

where the files of shows are located

- #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl') +#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
@@ -246,10 +246,10 @@
@@ -259,16 +259,16 @@ Date style: @@ -279,11 +279,12 @@ Time style: - note: seconds are only shown on the History page + note: seconds are only shown on the History page @@ -450,9 +451,9 @@ Branch version:

select branch to use (restart required)

@@ -485,9 +486,9 @@ CPU throttling: Normal (default). High is lower and Low is higher CPU use diff --git a/gui/slick/js/config.js b/gui/slick/js/config.js index e908b0f8..41046e69 100644 --- a/gui/slick/js/config.js +++ b/gui/slick/js/config.js @@ -1,85 +1,126 @@ $(document).ready(function(){ - $('.enabler').each(function(){ - if (!$(this).prop('checked')) - $('#content_' + $(this).attr('id')).hide(); - }); + var enabler = $('.enabler'), + viewIf = $('.viewIf'); - $('.enabler').click(function(){ - if ($(this).prop('checked')) - $('#content_' + $(this).attr('id')).fadeIn('fast', 'linear'); - else - $('#content_' + $(this).attr('id')).fadeOut('fast', 'linear'); - }); + enabler.each(function(){ + if (!$(this).prop('checked')) + $('#content_' + $(this).attr('id')).hide(); + }); - $('.viewIf').click(function(){ - if ($(this).prop('checked')) { - $('.hide_if_' + $(this).attr('id')).css('display','none'); - $('.show_if_' + $(this).attr('id')).fadeIn('fast', 'linear'); - } else { - $('.show_if_' + $(this).attr('id')).css('display','none'); - $('.hide_if_' + $(this).attr('id')).fadeIn('fast', 'linear'); - } - }); + enabler.click(function(){ + var content_id = $('#content_' + $(this).attr('id')); + if ($(this).prop('checked')) + content_id.fadeIn('fast', 'linear'); + else + content_id.fadeOut('fast', 'linear'); + }); - $('.datePresets').click(function(){ - var def = $('#date_presets').val() - if ($(this).prop('checked') && '%x' == def) { - def = '%a, %b %d, %Y' - $('#date_use_system_default').html('1') - } else if (!$(this).prop('checked') && '1' == $('#date_use_system_default').html()) - def = '%x' + viewIf.each(function(){ + $(($(this).prop('checked') ? '.hide_if_' : '.show_if_') + $(this).attr('id')).hide(); + }); - $('#date_presets').attr('name', 'date_preset_old') - $('#date_presets').attr('id', 'date_presets_old') + viewIf.click(function(){ + var if_id = '_if_' + $(this).attr('id'); + if ($(this).prop('checked')) { + $('.hide' + if_id).fadeOut('fast', 'linear'); + $('.show' + if_id).fadeIn('fast', 'linear'); + } else { + $('.show' + if_id).fadeOut('fast', 'linear'); + $('.hide' + if_id).fadeIn('fast', 'linear'); + } + }); - $('#date_presets_na').attr('name', 'date_preset') - $('#date_presets_na').attr('id', 'date_presets') + var ui_update_trim_zero = (function() { + var secs = ('00' + new Date().getSeconds().toString()).slice(-2), + elSecs = $('#trim_info_seconds'), + elTrimZero = $('#trim_zero'); + elTrimZero.each(function() { + var checked = $(this).prop('checked') && $('#fuzzy_dating').prop('checked'); - $('#date_presets_old').attr('name', 'date_preset_na') - $('#date_presets_old').attr('id', 'date_presets_na') + $('#time_presets').find('option').each(function() { + var text = ($(this).text()); + $(this).text(checked + ? text.replace(/(\b\d+:\d\d):\d+/mg, '$1') + : text.replace(/(\b\d+:\d\d)(?:.\d+)?/mg, '$1:' + secs)); + }); + }); - if (def) - $('#date_presets').val(def) - }); + if ($('#fuzzy_dating').prop('checked')) + if (elTrimZero.prop('checked')) + elSecs.fadeOut('fast', 'linear'); + else + elSecs.fadeIn('fast', 'linear'); + else + elSecs.fadeIn('fast', 'linear'); + }); - // bind 'myForm' and provide a simple callback function - $('#configForm').ajaxForm({ - beforeSubmit: function(){ - $('.config_submitter').each(function(){ - $(this).attr('disabled', 'disabled'); - $(this).after(' Saving...'); - $(this).hide(); - }); - }, - success: function(){ - setTimeout('config_success()', 2000) - } - }); + $('#trim_zero, #fuzzy_dating').click(function() { + ui_update_trim_zero(); + }); - $('#api_key').click(function(){ $('#api_key').select() }); - $("#generate_new_apikey").click(function(){ - $.get(sbRoot + '/config/general/generateKey', - function(data){ - if (data.error != undefined) { - alert(data.error); - return; - } - $('#api_key').val(data); - }); - }); + ui_update_trim_zero(); - $('#branchCheckout').click(function(){ - url = sbRoot + '/home/branchCheckout?branch=' + $('#branchVersion').val(); - window.location.href = url; - }); + $('.datePresets').click(function(){ + var elDatePresets = $('#date_presets'), + defaultPreset = elDatePresets.val(); + if ($(this).prop('checked') && '%x' == defaultPreset) { + defaultPreset = '%a, %b %d, %Y'; + $('#date_use_system_default').html('1') + } else if (!$(this).prop('checked') && '1' == $('#date_use_system_default').html()) + defaultPreset = '%x'; + + elDatePresets.attr('name', 'date_preset_old'); + elDatePresets.attr('id', 'date_presets_old'); + + var elDatePresets_na = $('#date_presets_na'); + elDatePresets_na.attr('name', 'date_preset'); + elDatePresets_na.attr('id', 'date_presets'); + + var elDatePresets_old = $('#date_presets_old'); + elDatePresets_old.attr('name', 'date_preset_na'); + elDatePresets_old.attr('id', 'date_presets_na'); + + if (defaultPreset) + elDatePresets.val(defaultPreset) + }); + + // bind 'myForm' and provide a simple callback function + $('#configForm').ajaxForm({ + beforeSubmit: function(){ + $('.config_submitter').each(function(){ + $(this).attr('disabled', 'disabled'); + $(this).after(' Saving...'); + $(this).hide(); + }); + }, + success: function(){ + setTimeout('config_success()', 2000) + } + }); + + $('#api_key').click(function(){ $('#api_key').select() }); + $("#generate_new_apikey").click(function(){ + $.get(sbRoot + '/config/general/generateKey', + function(data){ + if (data.error != undefined) { + alert(data.error); + return; + } + $('#api_key').val(data); + }); + }); + + $('#branchCheckout').click(function(){ + window.location.href = sbRoot + '/home/branchCheckout?branch=' + $('#branchVersion').val(); + }); }); function config_success(){ - $('.config_submitter').each(function(){ - $(this).removeAttr('disabled'); - $(this).next().remove(); - $(this).show(); - }); - $('#email_show').trigger('notify'); + $('.config_submitter').each(function(){ + $(this).removeAttr('disabled'); + $(this).next().remove(); + $(this).show(); + }); + $('#email_show').trigger('notify'); } diff --git a/gui/slick/js/fuzzyMoment.js b/gui/slick/js/fuzzyMoment.js index dc81c399..ab319f94 100644 --- a/gui/slick/js/fuzzyMoment.js +++ b/gui/slick/js/fuzzyMoment.js @@ -11,152 +11,170 @@ */ function fuzzyMoment(fmConfig) { - var containerClass = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.containerClass)) ? '.fuzzydate' : fmConfig.containerClass), - dateWithTime = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dateHasTime)) ? false : !!fmConfig.dateHasTime), - dateFormat = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dateFormat)) ? '' : fmConfig.dateFormat), - timeFormat = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.timeFormat)) ? '' : fmConfig.timeFormat), - trimZero = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.trimZero)) ? false : !!fmConfig.trimZero), - dtGlue = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dtGlue)) ? '
' : fmConfig.dtGlue), - dtInline = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dtInline)) ? false : fmConfig.dtInline), + var containerClass = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.containerClass)) ? '.fuzzydate' : fmConfig.containerClass), + dateWithTime = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dateHasTime)) ? false : !!fmConfig.dateHasTime), + dateFormat = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dateFormat)) ? '' : fmConfig.dateFormat), + timeFormat = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.timeFormat)) ? '' : fmConfig.timeFormat), + trimZero = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.trimZero)) ? false : !!fmConfig.trimZero), + dtGlue = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dtGlue)) ? '
' : fmConfig.dtGlue), + dtInline = (/undefined/i.test(typeof(fmConfig)) || /undefined/i.test(typeof(fmConfig.dtInline)) ? false : fmConfig.dtInline), - jd = (function (str) { - var token_map = ['a', 'ddd', 'A', 'dddd', 'b', 'MMM', 'B', 'MMMM', 'd', 'DD', 'm', 'MM', 'y', 'YY', 'Y', 'YYYY', 'x', 'L', - 'H', 'HH', 'I', 'hh', 'M', 'mm', 'S', 'ss', 'p', 'A'], - result = ''; + jd = (function (str) { + var token_map = ['a', 'ddd', 'A', 'dddd', 'b', 'MMM', 'B', 'MMMM', 'd', 'DD', 'm', 'MM', 'y', 'YY', 'Y', 'YYYY', 'x', 'L', + 'H', 'HH', 'I', 'hh', 'M', 'mm', 'S', 'ss', 'p', 'A', 'P', 'a'], + result = ''; - for (var i = 0; i < str.length; i++) - if (/[aAbBdmyYxHIMSp]/.test(str[i])) { - for (var t = 0; t < token_map.length; t = t + 2) - if (str[i] == token_map[t]) { - result += token_map[t + 1]; - break; - } - } else if ('%' != str[i]) - result += str[i]; + for (var i = 0; i < str.length; i++) + if (/[aAbBdmyYxHIMSpP]/.test(str[i])) { + for (var t = 0; t < token_map.length; t = t + 2) + if (str[i] == token_map[t]) { + result += token_map[t + 1]; + break; + } + } else if ('%' != str[i]) + result += str[i]; - return result; - }), - dateToken = jd(dateFormat), - timeToken = jd(timeFormat), + return result; + }), + dateTemplate = jd(dateFormat), + timeTemplate = jd(timeFormat), - addQTip = (function() { - $(this).css('cursor', 'help'); - $(this).qtip({ - show: { - solo: true - }, - position: { - viewport: $(window), - my: 'left center', - adjust: { - y: -10, - x: 2 - } - }, - style: { - classes: 'qtip-dark qtip-rounded qtip-shadow' - } - }); - }); + addQTip = (function() { + $(this).css('cursor', 'help'); + $(this).qtip({ + show: { + solo: true + }, + position: { + viewport: $(window), + my: 'left center', + adjust: { + y: -10, + x: 2 + } + }, + style: { + classes: 'qtip-dark qtip-rounded qtip-shadow' + } + }); + }); - if (trimZero) { - timeToken = timeToken.replace(/hh/ig, 'h'); - dateToken = dateToken.replace(/\bDD\b/g, 'D'); - } + if (trimZero) { + timeTemplate = timeTemplate.replace(/hh/g, 'h'); + timeTemplate = timeTemplate.replace(/HH/g, 'H'); + dateTemplate = dateTemplate.replace(/\bDD\b/g, 'D'); + } - $(containerClass).each(function() { - var input = $(this).text(), - dateA = '[', - dtSeparator = ' ', - timeA = ']', timeB = '[' + timeA; + $(containerClass).each(function() { + var input = $(this).text(), + dateA = '[', + dtSeparator = ' ', + timeA = ']', timeB = '[' + timeA, + timeToken = timeTemplate; - if (dateWithTime) { - var timeMeta = input.match(/^.{6,}?([,\s]+)(\d{1,2}).(?:\d{2,2})(?:.(\d{2,2}))?(?:\s([ap]m))?$/im); - if (null != timeMeta) { - dtSeparator = (! /undefined/i.test(typeof(timeMeta[1])) ? timeMeta[1] : dtSeparator); - // adjust timeToken to num digits of input hours - timeToken = (! /undefined/i.test(typeof(timeMeta[2])) && 1 == timeMeta[2].length ? timeToken.replace(/hh/ig, 'h') : timeToken); - // adjust timeToken to use seconds if input has them - timeToken = (! /undefined/i.test(typeof(timeMeta[3])) && 2 == timeMeta[3].length ? timeToken : timeToken.replace(/.ss/, '')); - // adjust timeToken to am/pm or AM/PM if input has it - timeToken = (! /undefined/i.test(typeof(timeMeta[4])) && 2 == timeMeta[4].length ? timeToken.replace(/A$/, (/[ap]m/.test(timeMeta[4]) ? 'a' : 'A')) : timeToken); - } - timeA = '
' + dtGlue + ']' + timeToken + '[' + timeA; - timeB = '[' + dtGlue + ']' + timeToken + timeB; - } + if (dateWithTime) { + var timeMeta = input.match(/([,\s]+)(\d{1,2})(?:(.)(\d\d)(?:(.)(\d\d))?)?(?:\s?([ap]m))?$/im); + if (null != timeMeta) { + dtSeparator = (! /undefined/i.test(typeof(timeMeta[1])) ? timeMeta[1] : dtSeparator); + // adjust timeToken to num digits of input hours + timeToken = (! /undefined/i.test(typeof(timeMeta[2])) && 1 == timeMeta[2].length ? timeToken.replace(/hh/ig, 'h') : timeToken); + // adjust timeToken to remove min if input has one and there is a pm + timeToken = (trimZero && ! /undefined/i.test(typeof(timeMeta[7])) + && (/undefined/i.test(typeof(timeMeta[4])) + || '00' == timeMeta[4]) ? timeToken.replace(/.mm/ig, '') : timeToken); + // adjust timeToken to use seconds if input has them + timeToken = (! /undefined/i.test(typeof(timeMeta[5])) && 2 == timeMeta[5].length ? timeToken : timeToken.replace(/.ss/, '')); + // adjust timeToken to am/pm or AM/PM if input has it + timeToken = (! /undefined/i.test(typeof(timeMeta[7])) && 2 == timeMeta[7].length ? timeToken.replace(/A$/, (/[ap]m/.test(timeMeta[7]) ? 'a' : 'A')) : timeToken); + } - var inputTokens = dateToken + dtSeparator + (dateWithTime ? timeToken : 'HH:mm:ss'); + var token_build = (/h+/i.test(timeToken) ? timeToken.replace(/^(h+).*/i, '[]$1[]') : ''); + if (/m+/i.test(timeToken)) { + token_build += (! /undefined/i.test(typeof(timeMeta[3])) ? '[]' + timeMeta[3] + '[]' : '') + + (! /undefined/i.test(typeof(timeMeta[4])) ? timeToken.replace(/.*?(m+).*/i, '[]$1[]') : ''); + if (/s+/i.test(timeToken)) { + token_build += (! /undefined/i.test(typeof(timeMeta[5])) ? '[]' + timeMeta[5] + '[]' : '') + + (! /undefined/i.test(typeof(timeMeta[6])) ? timeToken.replace(/.*?(s+).*/i, '[]$1[]') : ''); + } + } + timeToken = token_build + (! /undefined/i.test(typeof(timeMeta[7])) ? timeToken.replace(/.*?[\s0-9](a).*/i, '[]$1[]') : ''); - if (! moment(input + (dateWithTime ? '' : dtSeparator + '00:00:00'), inputTokens).isValid()) - return; + timeA = '' + dtGlue + ']' + timeToken + '[' + timeA; + timeB = '[' + dtGlue + ']' + timeToken + timeB; + } - moment.lang('en', { - calendar: { - lastDay:dateA + 'Yesterday' + timeA, sameDay:dateA + 'Today' + timeA, nextDay:dateA + 'Tomorrow' + timeA, - lastWeek:dateA + 'last] ddd' + timeB, nextWeek:dateA + 'on] ddd' + timeB, - sameElse:dateA + ']ddd, MMM D YYYY[' + timeA - }, - relativeTime: { - future:'in %s', past:'%s ago', s:'seconds', m:'a minute', mm:'%d minutes', h:'an hour', hh:'%d hours', - d:'a day', dd:'%d days', M:'a month', MM:'%d months', y:'a year', yy:'%d years' - } - }); + var inputTokens = dateTemplate + dtSeparator + (dateWithTime ? timeToken : 'HH:mm:ss'); - var airdatetime = moment(input + (dateWithTime ? '' : dtSeparator + '00:00:00'), inputTokens), - airdate = airdatetime.clone().hour(0).minute(0).second(0).millisecond(0), - today = moment({}), - day = Math.abs(airdate.diff(today, 'days')), - week = Math.abs(weekdiff = airdate.diff(today, 'week')), isPast = weekdiff < 0, - titleThis = false, qTipTime = false, - result = (0 == week ? airdatetime.calendar() : ''); + if (! moment(input + (dateWithTime ? '' : dtSeparator + '00:00:00'), inputTokens).isValid()) + return; - if (/\bOn\b/i.test(result)) { - var fuzzer = false, weekday = today.day(); - if (3 == weekday) - fuzzer = (5 <= day); - else if (4 == weekday || 5 == weekday) - fuzzer = (4 <= day); - else - fuzzer = (6 == day); - if (fuzzer) - result = result.replace(/\bOn\b/i, 'Next'); + moment.lang('en', { + calendar: { + lastDay:dateA + 'Yesterday' + timeA, sameDay:dateA + 'Today' + timeA, nextDay:dateA + 'Tomorrow' + timeA, + lastWeek:dateA + 'last] ddd' + timeB, nextWeek:dateA + 'on] ddd' + timeB, + sameElse:dateA + ']ddd, MMM D YYYY[' + timeA + }, + relativeTime: { + future:'in %s', past:'%s ago', s:'seconds', m:'a minute', mm:'%d minutes', h:'an hour', hh:'%d hours', + d:'a day', dd:'%d days', M:'a month', MM:'%d months', y:'a year', yy:'%d years' + } + }); - } else if (! /\b((yester|to)day\b|tomo|last\b)/i.test(result)) { - if (14 > day) - result = airdate.from(today) + (dateWithTime ? dtGlue + airdatetime.format(timeToken) : ''); - else if (4 > week) { - result = (isPast ? '' : 'in ') + (1 == week ? 'a' : week) + ' week' + (1 == week ? '' : 's') + (isPast ? ' ago' : ''); - qTipTime = true; - } else { - result = airdate.from(today); - qTipTime = true; - var month = airdate.diff(today, 'month'); - if (1 == parseInt(airdate.year() - today.year())) - result += (dtInline ? ' ' : '
') + '(Next Year)'; - } - titleThis = true; - } + var airdatetime = moment(input + (dateWithTime ? '' : dtSeparator + '00:00:00'), inputTokens), + airdate = airdatetime.clone().hour(0).minute(0).second(0).millisecond(0), + today = moment({}), + day = Math.abs(airdate.diff(today, 'days')), + week = Math.abs(weekdiff = airdate.diff(today, 'week')), isPast = weekdiff < 0, + titleThis = false, qTipTime = false, + result = (0 == week ? airdatetime.calendar() : ''); - var n = false; // disable for prod - $(this).html(result); - if (dateWithTime && /(yester|to)day/i.test(result)) - $(this).find('.fd').attr('title',(n?'1) ':'') + moment.duration(airdatetime.diff(moment(),'seconds'),'seconds').humanize(true)).each(addQTip); - else if (dateWithTime) - $(this).find('.fd').attr('title',(n?'2) ':'') + airdate.from(today)).each(addQTip); - else if (! /today/i.test(result)) - $(this).find('.fd').attr('title',(n?'3) ':'') + airdate.from(today)).each(addQTip); - else - titleThis = false; + if (/\bOn\b/i.test(result)) { + var fuzzer = false, weekday = today.day(); + if (3 == weekday) + fuzzer = (5 <= day); + else if (4 == weekday || 5 == weekday) + fuzzer = (4 <= day); + else + fuzzer = (6 == day); + if (fuzzer) + result = result.replace(/\bOn\b/i, 'Next'); - if (titleThis) - if (dateWithTime && qTipTime) - $(this).attr('title',(n?'4) ':'') + airdatetime.format(inputTokens)).each(addQTip); - else - $(this).attr('title',(n?'5) ':'') + airdate.format(dateToken)).each(addQTip); - else - if (dateWithTime && qTipTime) - $(this).find('.ft').attr('title',(n?'6) ':'') + airdatetime.format(inputTokens)).each(addQTip); - else - $(this).find('.ft').attr('title',(n?'7) ':'') + airdate.format(dateToken)).each(addQTip); - }); + } else if (! /\b((yester|to)day\b|tomo|last\b)/i.test(result)) { + if (14 > day) + result = airdate.from(today) + (dateWithTime ? dtGlue + airdatetime.format(timeToken) : ''); + else if (4 > week) { + result = (isPast ? '' : 'in ') + (1 == week ? 'a' : week) + ' week' + (1 == week ? '' : 's') + (isPast ? ' ago' : ''); + qTipTime = true; + } else { + result = airdate.from(today); + qTipTime = true; + var month = airdate.diff(today, 'month'); + if (1 == parseInt(airdate.year() - today.year())) + result += (dtInline ? ' ' : '
') + '(Next Year)'; + } + titleThis = true; + } + + var n = false; // disable for prod + $(this).html(result); + if (dateWithTime && /(yester|to)day/i.test(result)) + $(this).find('.fd').attr('title',(n?'1) ':'') + moment.duration(airdatetime.diff(moment(),'seconds'),'seconds').humanize(true)).each(addQTip); + else if (dateWithTime) + $(this).find('.fd').attr('title',(n?'2) ':'') + airdate.from(today)).each(addQTip); + else if (! /today/i.test(result)) + $(this).find('.fd').attr('title',(n?'3) ':'') + airdate.from(today)).each(addQTip); + else + titleThis = false; + + if (titleThis) + if (dateWithTime && qTipTime) + $(this).attr('title',(n?'4) ':'') + airdatetime.format(inputTokens)).each(addQTip); + else + $(this).attr('title',(n?'5) ':'') + airdate.format(dateTemplate)).each(addQTip); + else + if (dateWithTime && qTipTime) + $(this).find('.ft').attr('title',(n?'6) ':'') + airdatetime.format(inputTokens)).each(addQTip); + else + $(this).find('.ft').attr('title',(n?'7) ':'') + airdate.format(dateTemplate)).each(addQTip); + }); } diff --git a/sickbeard/sbdatetime.py b/sickbeard/sbdatetime.py index 5e96f667..9f49b865 100644 --- a/sickbeard/sbdatetime.py +++ b/sickbeard/sbdatetime.py @@ -19,6 +19,7 @@ import datetime import locale import functools +import re import sickbeard from sickbeard.network_timezones import sb_timezone @@ -82,12 +83,12 @@ date_presets = ('%Y-%m-%d', '%A, %b %d, %Y', '%B %d, %Y', '%a, %B %d, %Y', - '%A, %B %d, %Y' -) + '%A, %B %d, %Y') time_presets = ('%I:%M:%S %p', - '%H:%M:%S' -) + '%I:%M:%S %P', + '%H:%M:%S') + # helper class class static_or_instance(object): @@ -104,142 +105,98 @@ class sbdatetime(datetime.datetime): @static_or_instance def convert_to_setting(self, dt=None): + obj = (dt, self)[self is not None] try: - if sickbeard.TIMEZONE_DISPLAY == 'local': - if self is None: - return dt.astimezone(sb_timezone) - else: - return self.astimezone(sb_timezone) - else: - if self is None: - return dt - else: - return self + if 'local' == sickbeard.TIMEZONE_DISPLAY: + return obj.astimezone(sb_timezone) except: - if self is None: - return dt - else: - return self + pass + + return obj + + @static_or_instance + def setlocale(self, setlocale=True, use_has_locale=None, locale_str=''): + if setlocale: + try: + if None is use_has_locale or use_has_locale: + locale.setlocale(locale.LC_TIME, locale_str) + except: + if None is not use_has_locale: + sbdatetime.has_locale = False + pass # display Time in SickGear Format @static_or_instance - def sbftime(self, dt=None, show_seconds=False, t_preset=None): + def sbftime(self, dt=None, show_seconds=False, t_preset=None, setlocale=True, markup=False): - try:locale.setlocale(locale.LC_TIME, '') - except:pass - - try: - if sbdatetime.has_locale: - locale.setlocale(locale.LC_TIME, 'us_US') - except: - sbdatetime.has_locale = False + sbdatetime.setlocale(setlocale=setlocale, use_has_locale=sbdatetime.has_locale, locale_str='us_US') strt = '' try: - if self is None: - if dt is not None: - if t_preset is not None: - strt = dt.strftime(t_preset) - elif show_seconds: - strt = dt.strftime(sickbeard.TIME_PRESET_W_SECONDS) - else: - strt = dt.strftime(sickbeard.TIME_PRESET) - else: - if t_preset is not None: - strt = self.strftime(t_preset) - elif show_seconds: - strt = self.strftime(sickbeard.TIME_PRESET_W_SECONDS) - else: - strt = self.strftime(sickbeard.TIME_PRESET) - finally: - try: - if sbdatetime.has_locale: - locale.setlocale(locale.LC_TIME, '') - except: - sbdatetime.has_locale = False + obj = (dt, self)[self is not None] + if None is not obj: + tmpl = (((sickbeard.TIME_PRESET, sickbeard.TIME_PRESET_W_SECONDS)[show_seconds]), + t_preset)[None is not t_preset] + tmpl = (tmpl.replace(':%S', ''), tmpl)[show_seconds] + strt = obj.strftime(tmpl.replace('%P', '%p')) + + if sickbeard.TRIM_ZERO: + strt = re.sub(r'^0(\d:\d\d)', r'\1', strt) + + if re.search(r'(?im)%p$', tmpl): + if '%p' in tmpl: + strt = strt.upper() + elif '%P' in tmpl: + strt = strt.lower() + + if sickbeard.TRIM_ZERO: + strt = re.sub(r'(?im)^(\d+)(?::00)?(\s?[ap]m)', r'\1\2', strt) + + if markup: + match = re.search(r'(?im)(\d{1,2})(?:(.)(\d\d)(?:(.)(\d\d))?)?(?:\s?([ap]m))?$', strt) + if match: + strt = ('%s%s%s%s%s%s' % ( + ('%s' % match.group(1), '')[None is match.group(1)], + ('%s' % match.group(2), '')[None is match.group(2)], + ('%s' % match.group(3), '')[None is match.group(3)], + ('%s' % match.group(4), '')[None is match.group(4)], + ('%s' % match.group(5), '')[None is match.group(5)], + ('%s' % match.group(6), '')[None is match.group(6)])) + + finally: + sbdatetime.setlocale(setlocale=setlocale, use_has_locale=sbdatetime.has_locale) return strt # display Date in SickGear Format @static_or_instance - def sbfdate(self, dt=None, d_preset=None): + def sbfdate(self, dt=None, d_preset=None, setlocale=True): - try: - locale.setlocale(locale.LC_TIME, '') - except: - pass + sbdatetime.setlocale(setlocale=setlocale) strd = '' try: - if self is None: - if dt is not None: - if d_preset is not None: - strd = dt.strftime(d_preset) - else: - strd = dt.strftime(sickbeard.DATE_PRESET) - else: - if d_preset is not None: - strd = self.strftime(d_preset) - else: - strd = self.strftime(sickbeard.DATE_PRESET) + obj = (dt, self)[self is not None] + if None is not obj: + strd = obj.strftime((sickbeard.DATE_PRESET, d_preset)[None is not d_preset]) + finally: - - try: - locale.setlocale(locale.LC_TIME, '') - except: - pass - + sbdatetime.setlocale(setlocale=setlocale) return strd # display Datetime in SickGear Format @static_or_instance - def sbfdatetime(self, dt=None, show_seconds=False, d_preset=None, t_preset=None): + def sbfdatetime(self, dt=None, show_seconds=False, d_preset=None, t_preset=None, markup=False): - try: - locale.setlocale(locale.LC_TIME, '') - except: - pass + sbdatetime.setlocale() strd = '' + obj = (dt, self)[self is not None] try: - if self is None: - if dt is not None: - if d_preset is not None: - strd = dt.strftime(d_preset) - else: - strd = dt.strftime(sickbeard.DATE_PRESET) - try: - if sbdatetime.has_locale: - locale.setlocale(locale.LC_TIME, 'us_US') - except: - sbdatetime.has_locale = False - if t_preset is not None: - strd += u', ' + dt.strftime(t_preset) - elif show_seconds: - strd += u', ' + dt.strftime(sickbeard.TIME_PRESET_W_SECONDS) - else: - strd += u', ' + dt.strftime(sickbeard.TIME_PRESET) - else: - if d_preset is not None: - strd = self.strftime(d_preset) - else: - strd = self.strftime(sickbeard.DATE_PRESET) - try: - if sbdatetime.has_locale: - locale.setlocale(locale.LC_TIME, 'us_US') - except: - sbdatetime.has_locale = False - if t_preset is not None: - strd += u', ' + self.strftime(t_preset) - elif show_seconds: - strd += u', ' + self.strftime(sickbeard.TIME_PRESET_W_SECONDS) - else: - strd += u', ' + self.strftime(sickbeard.TIME_PRESET) - finally: - try: - if sbdatetime.has_locale: - locale.setlocale(locale.LC_TIME, '') - except: - sbdatetime.has_locale = False + if None is not obj: + strd = u'%s, %s' % (obj.strftime((sickbeard.DATE_PRESET, d_preset)[None is not d_preset]), + sbdatetime.sbftime(dt, show_seconds, t_preset, False, markup)) + finally: + sbdatetime.setlocale(use_has_locale=sbdatetime.has_locale) return strd