From 192a243b4d0e10d8c16b65b6ccea15147ecdf807 Mon Sep 17 00:00:00 2001 From: zwim Date: Thu, 17 Nov 2022 05:53:35 +0100 Subject: [PATCH] Add datetime.lua Move date and time related functions from util.lua (and the statistics plugin) to a new datetime.lua. --- frontend/apps/reader/modules/readerfooter.lua | 6 +- frontend/datetime.lua | 296 ++++++++++++++++++ frontend/readhistory.lua | 3 +- .../elements/common_settings_menu_table.lua | 8 +- frontend/ui/screensaver.lua | 3 +- frontend/ui/widget/bookstatuswidget.lua | 3 +- frontend/ui/widget/touchmenu.lua | 4 +- frontend/util.lua | 213 ------------- plugins/autodim.koplugin/main.lua | 6 +- plugins/autosuspend.koplugin/main.lua | 14 +- plugins/autoturn.koplugin/main.lua | 6 +- plugins/autowarmth.koplugin/main.lua | 4 +- plugins/batterystat.koplugin/main.lua | 4 +- plugins/readtimer.koplugin/main.lua | 8 +- plugins/statistics.koplugin/calendarview.lua | 1 + plugins/statistics.koplugin/main.lua | 100 ++---- .../statistics.koplugin/readerprogress.lua | 12 +- plugins/systemstat.koplugin/main.lua | 9 +- spec/unit/datetime_spec.lua | 188 +++++++++++ spec/unit/util_spec.lua | 139 -------- 20 files changed, 564 insertions(+), 463 deletions(-) create mode 100644 frontend/datetime.lua create mode 100644 spec/unit/datetime_spec.lua diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index 6355189c0..edbc1869d 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -20,8 +20,8 @@ local UIManager = require("ui/uimanager") local VerticalGroup = require("ui/widget/verticalgroup") local VerticalSpan = require("ui/widget/verticalspan") local WidgetContainer = require("ui/widget/container/widgetcontainer") +local datetime = require("datetime") local logger = require("logger") -local util = require("util") local T = require("ffi/util").template local _ = require("gettext") local C_ = _.pgettext @@ -229,7 +229,7 @@ local footerTextGeneratorMap = { time = function(footer) local symbol_type = footer.settings.item_prefix local prefix = symbol_prefix[symbol_type].time - local clock = util.secondsToHour(os.time(), G_reader_settings:isTrue("twelve_hour_clock")) + local clock = datetime.secondsToHour(os.time(), G_reader_settings:isTrue("twelve_hour_clock")) if not prefix then return clock else @@ -2098,7 +2098,7 @@ function ReaderFooter:getDataFromStatistics(title, pages) local average_time_per_page = self:getAvgTimePerPage() local user_duration_format = G_reader_settings:readSetting("duration_format", "classic") if average_time_per_page then - sec = util.secondsToClockDuration(user_duration_format, pages * average_time_per_page, true) + sec = datetime.secondsToClockDuration(user_duration_format, pages * average_time_per_page, true) end return title .. sec end diff --git a/frontend/datetime.lua b/frontend/datetime.lua new file mode 100644 index 000000000..26317c3b2 --- /dev/null +++ b/frontend/datetime.lua @@ -0,0 +1,296 @@ +--[[-- +This module contains date translations and helper functions for the KOReader frontend. +]] + +local BaseUtil = require("ffi/util") +local _ = require("gettext") +local C_ = _.pgettext +local T = BaseUtil.template + +local datetime = {} + +datetime.weekDays = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } -- in Lua wday order + +datetime.shortMonthTranslation = { + ["Jan"] = _("Jan"), + ["Feb"] = _("Feb"), + ["Mar"] = _("Mar"), + ["Apr"] = _("Apr"), + ["May"] = _("May"), + ["Jun"] = _("Jun"), + ["Jul"] = _("Jul"), + ["Aug"] = _("Aug"), + ["Sep"] = _("Sep"), + ["Oct"] = _("Oct"), + ["Nov"] = _("Nov"), + ["Dec"] = _("Dec"), +} + +datetime.longMonthTranslation = { + ["January"] = _("January"), + ["February"] = _("February"), + ["March"] = _("March"), + ["April"] = _("April"), + ["May"] = _("May"), + ["June"] = _("June"), + ["July"] = _("July"), + ["August"] = _("August"), + ["September"] = _("September"), + ["October"] = _("October"), + ["November"] = _("November"), + ["December"] = _("December"), +} + +datetime.shortDayOfWeekTranslation = { + ["Mon"] = _("Mon"), + ["Tue"] = _("Tue"), + ["Wed"] = _("Wed"), + ["Thu"] = _("Thu"), + ["Fri"] = _("Fri"), + ["Sat"] = _("Sat"), + ["Sun"] = _("Sun"), +} + +datetime.shortDayOfWeekToLongTranslation = { + ["Mon"] = _("Monday"), + ["Tue"] = _("Tuesday"), + ["Wed"] = _("Wednesday"), + ["Thu"] = _("Thursday"), + ["Fri"] = _("Friday"), + ["Sat"] = _("Saturday"), + ["Sun"] = _("Sunday"), +} + +--[[-- +Converts seconds to a clock string. + +Source: https://gist.github.com/jesseadams/791673 +]] +---- @int seconds number of seconds +---- @bool withoutSeconds if true 00:00, if false 00:00:00 +---- @treturn string clock string in the form of 00:00 or 00:00:00 +function datetime.secondsToClock(seconds, withoutSeconds, withDays) + seconds = tonumber(seconds) + if not seconds then + if withoutSeconds then + return "--:--" + else + return "--:--:--" + end + elseif seconds == 0 or seconds ~= seconds then + if withoutSeconds then + return "00:00" + else + return "00:00:00" + end + else + local round = withoutSeconds and require("optmath").round or function(n) return n end + local days = "0" + local hours + if withDays then + days = string.format("%d", seconds * (1/(24*3600))) -- implicit math.floor for string.format + hours = string.format("%02d", (seconds * (1/3600)) % 24) + else + hours = string.format("%02d", seconds * (1/3600)) + end + local mins = string.format("%02d", round(seconds % 3600 * (1/60))) + if withoutSeconds then + if mins == "60" then + -- Can only happen because of rounding, which only happens if withoutSeconds... + mins = string.format("%02d", 0) + hours = string.format("%02d", hours + 1) + end + return (days ~= "0" and (days .. "d") or "") .. hours .. ":" .. mins + else + local secs = string.format("%02d", seconds % 60) + return (days ~= "0" and (days .. "d") or "") .. hours .. ":" .. mins .. ":" .. secs + end + end +end + +--- Converts seconds to a period of time string. +---- @int seconds number of seconds +---- @bool withoutSeconds if true 1h30', if false 1h30'10" +---- @bool hmsFormat, if true format 1h30m10s +---- @bool withDays, if true format 1d12h30m10s +---- @treturn string clock string in the form of 1h30'10" or 1h30m10s +function datetime.secondsToHClock(seconds, withoutSeconds, hmsFormat, withDays, compact) + local SECONDS_SYMBOL = "\"" + seconds = tonumber(seconds) + if seconds == 0 then + if withoutSeconds then + if hmsFormat then + return T(_("%1m"), "0") + else + return "0'" + end + else + if hmsFormat then + return T(C_("Time", "%1s"), "0") + else + return "0" .. SECONDS_SYMBOL + end + end + elseif seconds < 60 then + if withoutSeconds and seconds < 30 then + if hmsFormat then + return T(C_("Time", "%1m"), "0") + else + return "0'" + end + elseif withoutSeconds and seconds >= 30 then + if hmsFormat then + return T(C_("Time", "%1m"), "1") + else + return "1'" + end + else + if hmsFormat then + if compact then + return T(C_("Time", "%1s"), string.format("%d", seconds)) + else + return T(C_("Time", "%1m%2s"), "0", string.format("%02d", seconds)) + end + else + if compact then + return string.format("%d", seconds) .. SECONDS_SYMBOL + else + return "0'" .. string.format("%02d", seconds) .. SECONDS_SYMBOL + end + end + end + else + local time_string = datetime.secondsToClock(seconds, withoutSeconds, withDays) + if withoutSeconds then + time_string = time_string .. ":" + end + time_string = time_string:gsub(":", C_("Time", "h"), 1) + time_string = time_string:gsub(":", C_("Time", "m"), 1) + time_string = time_string:gsub("^00" .. C_("Time", "h"), "") -- delete leading "00h" + time_string = time_string:gsub("^00" .. C_("Time", "m"), "") -- delete leading "00m" + if time_string:find("^0%d") then + time_string = time_string:gsub("^0", "") -- delete leading "0" + end + if withoutSeconds and time_string == "" then + time_string = "0" .. C_("Time", "m") + end + + if hmsFormat then + return withoutSeconds and time_string or (time_string .. C_("Time", "s")) + else + time_string = time_string:gsub(C_("Time", "m"), "'") -- replace m with ' + return withoutSeconds and time_string or (time_string .. SECONDS_SYMBOL) + end + end +end + +--- Converts seconds to a clock type (classic or modern), based on the given format preference +--- "Classic" format calls secondsToClock, and "Modern" format calls secondsToHClock +---- @string Either "modern" for 1h30'10" or "classic" for 1:30:10 +---- @bool withoutSeconds if true 1h30' or 1h30m, if false 1h30'10" or 1h30m10s +---- @bool hmsFormat, modern format only, if true format 1h30m or 1h30m10s +---- @bool withDays, if hours>=24 include days in clock string 1d12h10m10s +---- @bool compact, if set removes all leading zeros (incl. units if necessary) +---- @treturn string clock string in the specific format of 1h30', 1h30'10" resp. 1h30m, 1h30m10s +function datetime.secondsToClockDuration(format, seconds, withoutSeconds, hmsFormat, withDays, compact) + if format == "modern" then + return datetime.secondsToHClock(seconds, withoutSeconds, hmsFormat, withDays, compact) + else + -- Assume "classic" to give safe default + return datetime.secondsToClock(seconds, withoutSeconds, withDays) + end +end + +if jit.os == "Windows" then + --- Converts timestamp to an hour string + ---- @int seconds number of seconds + ---- @bool twelve_hour_clock + ---- @treturn string hour string + ---- @note: The MS CRT doesn't support either %l & %k, or the - format modifier (as they're not technically C99 or POSIX). + ---- They are otherwise supported on Linux, BSD & Bionic, so, just special-case Windows... + ---- We *could* arguably feed the os.date output to gsub("^0(%d)(.*)$", "%1%2"), but, while unlikely, + ---- it's conceivable that a translator would put something other that the hour at the front of the string ;). + function datetime.secondsToHour(seconds, twelve_hour_clock) + if twelve_hour_clock then + if os.date("%p", seconds) == "AM" then + -- @translators This is the time in the morning using a 12-hour clock (%I is the hour, %M the minute). + return os.date(_("%I:%M AM"), seconds) + else + -- @translators This is the time in the afternoon using a 12-hour clock (%I is the hour, %M the minute). + return os.date(_("%I:%M PM"), seconds) + end + else + -- @translators This is the time using a 24-hour clock (%H is the hour, %M the minute). + return os.date(_("%H:%M"), seconds) + end + end +else + function datetime.secondsToHour(seconds, twelve_hour_clock, pad_with_spaces) + if twelve_hour_clock then + if os.date("%p", seconds) == "AM" then + if pad_with_spaces then + -- @translators This is the time in the morning using a 12-hour clock (%_I is the hour, %M the minute). + return os.date(_("%_I:%M AM"), seconds) + else + -- @translators This is the time in the morning using a 12-hour clock (%-I is the hour, %M the minute). + return os.date(_("%-I:%M AM"), seconds) + end + else + if pad_with_spaces then + -- @translators This is the time in the afternoon using a 12-hour clock (%_I is the hour, %M the minute). + return os.date(_("%_I:%M PM"), seconds) + else + -- @translators This is the time in the afternoon using a 12-hour clock (%-I is the hour, %M the minute). + return os.date(_("%-I:%M PM"), seconds) + end + end + else + if pad_with_spaces then + -- @translators This is the time using a 24-hour clock (%_H is the hour, %M the minute). + return os.date(_("%_H:%M"), seconds) + else + -- @translators This is the time using a 24-hour clock (%-H is the hour, %M the minute). + return os.date(_("%-H:%M"), seconds) + end + end + end +end + +--- Converts timestamp to a date string +---- @int seconds number of seconds +---- @use_locale if true allows to translate the date-time string, if false return "%Y-%m-%d time" +---- @treturn string date string +function datetime.secondsToDate(seconds, use_locale) + seconds = seconds or os.time() + if use_locale then + local wday = os.date("%a", seconds) + local month = os.date("%b", seconds) + local day = os.date("%d", seconds) + local year = os.date("%Y", seconds) + + -- @translators Use the following placeholders in the desired order: %1 name of day, %2 name of month, %3 day, %4 year + return T(C_("Date string", "%1 %2 %3 %4"), + datetime.shortDayOfWeekTranslation[wday], datetime.shortMonthTranslation[month], day, year) + else + -- @translators This is the date (%Y is the year, %m the month, %d the day) + return os.date(C_("Date string", "%Y-%m-%d"), seconds) + end +end + +--- Converts timestamp to a date+time string +---- @int seconds number of seconds +---- @bool twelve_hour_clock +---- @use_locale if true allows to translate the date-time string, if false return "%Y-%m-%d time" +---- @treturn string date+time +function datetime.secondsToDateTime(seconds, twelve_hour_clock, use_locale) + seconds = seconds or os.time() + local BD = require("ui/bidi") + local date_string = datetime.secondsToDate(seconds, use_locale) + local time_string = datetime.secondsToHour(seconds, twelve_hour_clock, not use_locale) + + -- @translators Use the following placeholders in the desired order: %1 date, %2 time + local message_text = T(C_("Date string", "%1 %2"), BD.wrap(date_string), BD.wrap(time_string)) + return message_text +end + +return datetime diff --git a/frontend/readhistory.lua b/frontend/readhistory.lua index 26a9d1d8a..2afc7782a 100644 --- a/frontend/readhistory.lua +++ b/frontend/readhistory.lua @@ -1,5 +1,6 @@ local DataStorage = require("datastorage") local DocSettings = require("docsettings") +local datetime = require("datetime") local dump = require("dump") local ffiutil = require("ffi/util") local util = require("util") @@ -41,7 +42,7 @@ local function buildEntry(input_time, input_file) -- we fallback to if no sidecar file) last_read_ts = DocSettings:getLastSaveTime(file_path) or input_time end - return util.secondsToDate(last_read_ts, G_reader_settings:isTrue("twelve_hour_clock")) + return datetime.secondsToDateTime(last_read_ts, G_reader_settings:isTrue("twelve_hour_clock")) end, select_enabled_func = function() return lfs.attributes(file_path, "mode") == "file" diff --git a/frontend/ui/elements/common_settings_menu_table.lua b/frontend/ui/elements/common_settings_menu_table.lua index d5af2ae43..b89234db2 100644 --- a/frontend/ui/elements/common_settings_menu_table.lua +++ b/frontend/ui/elements/common_settings_menu_table.lua @@ -91,9 +91,9 @@ common_settings.time = { sub_item_table = { { text_func = function() - local util = require('util') + local datetime = require("datetime") -- sample text shows 1:23:45 - local duration_format_str = util.secondsToClockDuration("classic", 5025, false) + local duration_format_str = datetime.secondsToClockDuration("classic", 5025, false) return T(_("Classic (%1)"), duration_format_str) end, checked_func = function() @@ -106,9 +106,9 @@ common_settings.time = { }, { text_func = function() - local util = require('util') + local datetime = require("datetime") -- sample text shows 1h23m45s - local duration_format_str = util.secondsToClockDuration("modern", 5025, false) + local duration_format_str = datetime.secondsToClockDuration("modern", 5025, false) return T(_("Modern (%1)"), duration_format_str) end, checked_func = function() diff --git a/frontend/ui/screensaver.lua b/frontend/ui/screensaver.lua index 75800ba61..a23c5bd5c 100644 --- a/frontend/ui/screensaver.lua +++ b/frontend/ui/screensaver.lua @@ -19,6 +19,7 @@ local TopContainer = require("ui/widget/container/topcontainer") local UIManager = require("ui/uimanager") local VerticalGroup = require("ui/widget/verticalgroup") local VerticalSpan = require("ui/widget/verticalspan") +local datetime = require("datetime") local ffiUtil = require("ffi/util") local lfs = require("libs/libkoreader-lfs") local logger = require("logger") @@ -130,7 +131,7 @@ function Screensaver:_calcAverageTimeForPages(pages) -- Compare average_time_per_page against itself to make sure it's not nan if average_time_per_page and average_time_per_page == average_time_per_page and pages then local user_duration_format = G_reader_settings:readSetting("duration_format", "classic") - sec = util.secondsToClockDuration(user_duration_format, pages * average_time_per_page, true) + sec = datetime.secondsToClockDuration(user_duration_format, pages * average_time_per_page, true) end return sec end diff --git a/frontend/ui/widget/bookstatuswidget.lua b/frontend/ui/widget/bookstatuswidget.lua index 9939335f5..402801d03 100644 --- a/frontend/ui/widget/bookstatuswidget.lua +++ b/frontend/ui/widget/bookstatuswidget.lua @@ -24,6 +24,7 @@ local ToggleSwitch = require("ui/widget/toggleswitch") local UIManager = require("ui/uimanager") local VerticalGroup = require("ui/widget/verticalgroup") local VerticalSpan = require("ui/widget/verticalspan") +local datetime = require("datetime") local util = require("util") local _ = require("gettext") local Screen = Device.screen @@ -144,7 +145,7 @@ end function BookStatusWidget:getStatHours() if stats_book.time then local user_duration_format = G_reader_settings:readSetting("duration_format", "classic") - return util.secondsToClockDuration(user_duration_format, stats_book.time, false) + return datetime.secondsToClockDuration(user_duration_format, stats_book.time, false) else return _("N/A") end diff --git a/frontend/ui/widget/touchmenu.lua b/frontend/ui/widget/touchmenu.lua index 81e69edf8..9283d489d 100644 --- a/frontend/ui/widget/touchmenu.lua +++ b/frontend/ui/widget/touchmenu.lua @@ -27,7 +27,7 @@ local UIManager = require("ui/uimanager") local UnderlineContainer = require("ui/widget/container/underlinecontainer") local VerticalGroup = require("ui/widget/verticalgroup") local VerticalSpan = require("ui/widget/verticalspan") -local util = require("util") +local datetime = require("datetime") local getMenuText = require("ui/widget/menu").getMenuText local _ = require("gettext") local T = require("ffi/util").template @@ -704,7 +704,7 @@ function TouchMenu:updateItems() self.page_info_left_chev:enableDisable(self.page > 1) self.page_info_right_chev:enableDisable(self.page < self.page_num) - local time_info_txt = util.secondsToHour(os.time(), G_reader_settings:isTrue("twelve_hour_clock")) + local time_info_txt = datetime.secondsToHour(os.time(), G_reader_settings:isTrue("twelve_hour_clock")) local powerd = Device:getPowerDevice() if Device:hasBattery() then local batt_lvl = powerd:getCapacity() diff --git a/frontend/util.lua b/frontend/util.lua index 65b8d5313..ffaad46a8 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -101,219 +101,6 @@ function util.gsplit(str, pattern, capture, capture_empty_entity) end) end --- Stupid helper for the duration stuff -local function passthrough(n) - return n -end - ---[[-- -Converts seconds to a clock string. - -Source: https://gist.github.com/jesseadams/791673 -]] ----- @int seconds number of seconds ----- @bool withoutSeconds if true 00:00, if false 00:00:00 ----- @treturn string clock string in the form of 00:00 or 00:00:00 -function util.secondsToClock(seconds, withoutSeconds, withDays) - seconds = tonumber(seconds) - if not seconds then - if withoutSeconds then - return "--:--" - else - return "--:--:--" - end - elseif seconds == 0 or seconds ~= seconds then - if withoutSeconds then - return "00:00" - else - return "00:00:00" - end - else - local round = withoutSeconds and require("optmath").round or passthrough - local days = "0" - local hours - if withDays then - days = string.format("%d", seconds * (1/(24*3600))) -- implicit math.floor for string.format - hours = string.format("%02d", (seconds * (1/3600)) % 24) - else - hours = string.format("%02d", seconds * (1/3600)) - end - local mins = string.format("%02d", round(seconds % 3600 * (1/60))) - if withoutSeconds then - if mins == "60" then - -- Can only happen because of rounding, which only happens if withoutSeconds... - mins = string.format("%02d", 0) - hours = string.format("%02d", hours + 1) - end - return (days ~= "0" and (days .. "d") or "") .. hours .. ":" .. mins - else - local secs = string.format("%02d", seconds % 60) - return (days ~= "0" and (days .. "d") or "") .. hours .. ":" .. mins .. ":" .. secs - end - end -end - ---- Converts seconds to a period of time string. ----- @int seconds number of seconds ----- @bool withoutSeconds if true 1h30', if false 1h30'10" ----- @bool hmsFormat, if true format 1h30m10s ----- @bool withDays, if true format 1d12h30m10s ----- @treturn string clock string in the form of 1h30'10" or 1h30m10s -function util.secondsToHClock(seconds, withoutSeconds, hmsFormat, withDays, compact) - local SECONDS_SYMBOL = "\"" - seconds = tonumber(seconds) - if seconds == 0 then - if withoutSeconds then - if hmsFormat then - return T(_("%1m"), "0") - else - return "0'" - end - else - if hmsFormat then - return T(C_("Time", "%1s"), "0") - else - return "0" .. SECONDS_SYMBOL - end - end - elseif seconds < 60 then - if withoutSeconds and seconds < 30 then - if hmsFormat then - return T(C_("Time", "%1m"), "0") - else - return "0'" - end - elseif withoutSeconds and seconds >= 30 then - if hmsFormat then - return T(C_("Time", "%1m"), "1") - else - return "1'" - end - else - if hmsFormat then - if compact then - return T(C_("Time", "%1s"), string.format("%d", seconds)) - else - return T(C_("Time", "%1m%2s"), "0", string.format("%02d", seconds)) - end - else - if compact then - return string.format("%d", seconds) .. SECONDS_SYMBOL - else - return "0'" .. string.format("%02d", seconds) .. SECONDS_SYMBOL - end - end - end - else - local time_string = util.secondsToClock(seconds, withoutSeconds, withDays) - if withoutSeconds then - time_string = time_string .. ":" - end - time_string = time_string:gsub(":", C_("Time", "h"), 1) - time_string = time_string:gsub(":", C_("Time", "m"), 1) - time_string = time_string:gsub("^00" .. C_("Time", "h"), "") -- delete leading "00h" - time_string = time_string:gsub("^00" .. C_("Time", "m"), "") -- delete leading "00m" - if time_string:find("^0%d") then - time_string = time_string:gsub("^0", "") -- delete leading "0" - end - if withoutSeconds and time_string == "" then - time_string = "0" .. C_("Time", "m") - end - - if hmsFormat then - return withoutSeconds and time_string or (time_string .. C_("Time", "s")) - else - time_string = time_string:gsub(C_("Time", "m"), "'") -- replace m with ' - return withoutSeconds and time_string or (time_string .. SECONDS_SYMBOL) - end - end -end - ---- Converts seconds to a clock type (classic or modern), based on the given format preference ---- "Classic" format calls secondsToClock, and "Modern" format calls secondsToHClock ----- @string Either "modern" for 1h30'10" or "classic" for 1:30:10 ----- @bool withoutSeconds if true 1h30' or 1h30m, if false 1h30'10" or 1h30m10s ----- @bool hmsFormat, modern format only, if true format 1h30m or 1h30m10s ----- @bool withDays, if hours>=24 include days in clock string 1d12h10m10s ----- @bool compact, if set removes all leading zeros (incl. units if necessary) ----- @treturn string clock string in the specific format of 1h30', 1h30'10" resp. 1h30m, 1h30m10s -function util.secondsToClockDuration(format, seconds, withoutSeconds, hmsFormat, withDays, compact) - if format == "modern" then - return util.secondsToHClock(seconds, withoutSeconds, hmsFormat, withDays, compact) - else - -- Assume "classic" to give safe default - return util.secondsToClock(seconds, withoutSeconds, withDays) - end -end - -if jit.os == "Windows" then - --- Converts timestamp to an hour string - ---- @int seconds number of seconds - ---- @bool twelve_hour_clock - ---- @treturn string hour string - ---- @note: The MS CRT doesn't support either %l & %k, or the - format modifier (as they're not technically C99 or POSIX). - ---- They are otherwise supported on Linux, BSD & Bionic, so, just special-case Windows... - ---- We *could* arguably feed the os.date output to gsub("^0(%d)(.*)$", "%1%2"), but, while unlikely, - ---- it's conceivable that a translator would put something other that the hour at the front of the string ;). - function util.secondsToHour(seconds, twelve_hour_clock) - if twelve_hour_clock then - if os.date("%p", seconds) == "AM" then - -- @translators This is the time in the morning using a 12-hour clock (%I is the hour, %M the minute). - return os.date(_("%I:%M AM"), seconds) - else - -- @translators This is the time in the afternoon using a 12-hour clock (%I is the hour, %M the minute). - return os.date(_("%I:%M PM"), seconds) - end - else - -- @translators This is the time using a 24-hour clock (%H is the hour, %M the minute). - return os.date(_("%H:%M"), seconds) - end - end -else - function util.secondsToHour(seconds, twelve_hour_clock, pad_with_spaces) - if twelve_hour_clock then - if os.date("%p", seconds) == "AM" then - if pad_with_spaces then - -- @translators This is the time in the morning using a 12-hour clock (%_I is the hour, %M the minute). - return os.date(_("%_I:%M AM"), seconds) - else - -- @translators This is the time in the morning using a 12-hour clock (%-I is the hour, %M the minute). - return os.date(_("%-I:%M AM"), seconds) - end - else - if pad_with_spaces then - -- @translators This is the time in the afternoon using a 12-hour clock (%_I is the hour, %M the minute). - return os.date(_("%_I:%M PM"), seconds) - else - -- @translators This is the time in the afternoon using a 12-hour clock (%-I is the hour, %M the minute). - return os.date(_("%-I:%M PM"), seconds) - end - end - else - if pad_with_spaces then - -- @translators This is the time using a 24-hour clock (%_H is the hour, %M the minute). - return os.date(_("%_H:%M"), seconds) - else - -- @translators This is the time using a 24-hour clock (%-H is the hour, %M the minute). - return os.date(_("%-H:%M"), seconds) - end - end - end -end - ---- Converts timestamp to a date string ----- @int seconds number of seconds ----- @bool twelve_hour_clock ----- @treturn string date string -function util.secondsToDate(seconds, twelve_hour_clock) - local BD = require("ui/bidi") - -- In order to keep stuff aligned, we'll want to *keep* the padding, but using blanks instead of zeroes. - local time = util.secondsToHour(seconds, twelve_hour_clock, true) - -- @translators This is the date (%Y is the year, %m the month, %d the day) - local day = os.date(_("%Y-%m-%d"), seconds) - return BD.wrap(day) .. " " .. BD.wrap(time) -end - --[[-- Compares values in two different tables. diff --git a/plugins/autodim.koplugin/main.lua b/plugins/autodim.koplugin/main.lua index eaec00a20..d52baa181 100644 --- a/plugins/autodim.koplugin/main.lua +++ b/plugins/autodim.koplugin/main.lua @@ -10,8 +10,8 @@ local SpinWidget = require("ui/widget/spinwidget") local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") local TrapWidget = require("ui/widget/trapwidget") +local datetime = require("datetime") local time = require("ui/time") -local util = require("util") local _ = require("gettext") local C_ = _.pgettext local Powerd = Device.powerd @@ -55,7 +55,7 @@ function AutoDim:getAutoDimMenu() text_func = function() return self.autodim_starttime_m <= 0 and _("Idle time for dimmer") or T(_("Idle time for dimmer: %1"), - util.secondsToClockDuration("modern", self.autodim_starttime_m * 60, false, true, false, true)) + datetime.secondsToClockDuration("modern", self.autodim_starttime_m * 60, false, true, false, true)) end, checked_func = function() return self.autodim_starttime_m > 0 end, callback = function(touchmenu_instance) @@ -94,7 +94,7 @@ function AutoDim:getAutoDimMenu() { text_func = function() return T(_("Dimmer duration: %1"), - util.secondsToClockDuration("modern", self.autodim_duration_s, false, true, false, true)) + datetime.secondsToClockDuration("modern", self.autodim_duration_s, false, true, false, true)) end, enabled_func = function() return self.autodim_starttime_m > 0 end, callback = function(touchmenu_instance) diff --git a/plugins/autosuspend.koplugin/main.lua b/plugins/autosuspend.koplugin/main.lua index d4214e37c..f414bc6cc 100644 --- a/plugins/autosuspend.koplugin/main.lua +++ b/plugins/autosuspend.koplugin/main.lua @@ -6,15 +6,15 @@ if not Device:canSuspend() then end local Event = require("ui/event") +local Math = require("optmath") local NetworkMgr = require("ui/network/manager") local PluginShare = require("pluginshare") local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") +local datetime = require("datetime") local logger = require("logger") -local util = require("util") local time = require("ui/time") local _ = require("gettext") -local Math = require("optmath") local T = require("ffi/util").template local default_autoshutdown_timeout_seconds = 3*24*60*60 -- three days @@ -427,14 +427,14 @@ function AutoSuspend:pickTimeoutValue(touchmenu_instance, title, info, setting, self:_start() end if touchmenu_instance then touchmenu_instance:updateItems() end - local time_string = util.secondsToClockDuration("modern", self[setting], + local time_string = datetime.secondsToClockDuration("modern", self[setting], time_scale == 2 or time_scale == 1, true, true) UIManager:show(InfoMessage:new{ text = T(_("%1: %2"), title, time_string), timeout = 3, }) end, - default_value = util.secondsToClockDuration("modern", default_value, + default_value = datetime.secondsToClockDuration("modern", default_value, time_scale == 2 or time_scale == 1, true, true), default_callback = function() local day, hour, min, sec -- luacheck: ignore 431 @@ -481,7 +481,7 @@ function AutoSuspend:addToMainMenu(menu_items) end, text_func = function() if self.auto_suspend_timeout_seconds and self.auto_suspend_timeout_seconds > 0 then - local time_string = util.secondsToClockDuration("modern", + local time_string = datetime.secondsToClockDuration("modern", self.auto_suspend_timeout_seconds, true, true, true) return T(_("Autosuspend timeout: %1"), time_string) else @@ -507,7 +507,7 @@ function AutoSuspend:addToMainMenu(menu_items) end, text_func = function() if self.autoshutdown_timeout_seconds and self.autoshutdown_timeout_seconds > 0 then - local time_string = util.secondsToClockDuration("modern", self.autoshutdown_timeout_seconds, + local time_string = datetime.secondsToClockDuration("modern", self.autoshutdown_timeout_seconds, true, true, true) return T(_("Autoshutdown timeout: %1"), time_string) else @@ -546,7 +546,7 @@ Upon user input, the device needs a certain amount of time to wake up. Generally end, text_func = function() if self.auto_standby_timeout_seconds and self.auto_standby_timeout_seconds > 0 then - local time_string = util.secondsToClockDuration("modern", self.auto_standby_timeout_seconds, + local time_string = datetime.secondsToClockDuration("modern", self.auto_standby_timeout_seconds, false, true, true, true) return T(_("Autostandby timeout: %1"), time_string) else diff --git a/plugins/autoturn.koplugin/main.lua b/plugins/autoturn.koplugin/main.lua index 204632d69..1184968ef 100644 --- a/plugins/autoturn.koplugin/main.lua +++ b/plugins/autoturn.koplugin/main.lua @@ -3,9 +3,9 @@ local Event = require("ui/event") local PluginShare = require("pluginshare") local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") +local datetime = require("datetime") local logger = require("logger") local time = require("ui/time") -local util = require("util") local _ = require("gettext") local T = require("ffi/util").template @@ -67,7 +67,7 @@ function AutoTurn:_start() local text if self.autoturn_distance == 1 then - local time_string = util.secondsToClockDuration("modern", self.autoturn_sec, false, true, true, true) + local time_string = datetime.secondsToClockDuration("modern", self.autoturn_sec, false, true, true, true) text = T(_("Autoturn is now active and will automatically turn the page every %1."), time_string) else text = T(_("Autoturn is now active and will automatically scroll %1 % of the page every %2 seconds."), @@ -141,7 +141,7 @@ function AutoTurn:addToMainMenu(menu_items) menu_items.autoturn = { sorting_hint = "navi", text_func = function() - local time_string = util.secondsToClockDuration("modern", self.autoturn_sec, false, true, true, true) + local time_string = datetime.secondsToClockDuration("modern", self.autoturn_sec, false, true, true, true) return self:_enabled() and T(_("Autoturn: %1"), time_string) or _("Autoturn") end, checked_func = function() return self:_enabled() end, diff --git a/plugins/autowarmth.koplugin/main.lua b/plugins/autowarmth.koplugin/main.lua index 7c2935cb7..d2b45664b 100644 --- a/plugins/autowarmth.koplugin/main.lua +++ b/plugins/autowarmth.koplugin/main.lua @@ -27,7 +27,7 @@ local C_ = _.pgettext local Powerd = Device.powerd local T = FFIUtil.template local Screen = require("device").screen -local util = require("util") +local datetime = require("datetime") local activate_sun = 1 local activate_schedule = 2 @@ -519,7 +519,7 @@ function AutoWarmth:hoursToClock(hours) if hours then hours = hours % 24 * 3600 + 0.01 -- round up, due to reduced precision in settings.reader.lua end - return util.secondsToClock(hours, self.easy_mode) + return datetime.secondsToClock(hours, self.easy_mode) end function AutoWarmth:addToMainMenu(menu_items) diff --git a/plugins/batterystat.koplugin/main.lua b/plugins/batterystat.koplugin/main.lua index b26b59ba4..2ce81156a 100644 --- a/plugins/batterystat.koplugin/main.lua +++ b/plugins/batterystat.koplugin/main.lua @@ -6,9 +6,9 @@ local LuaSettings = require("luasettings") local PowerD = require("device"):getPowerDevice() local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") +local datetime = require("datetime") local dbg = require("dbg") local time = require("ui/time") -local util = require("util") local _ = require("gettext") local T = require("ffi/util").template @@ -81,7 +81,7 @@ end local function duration(number) local duration_fmt = G_reader_settings:readSetting("duration_format", "classic") return type(number) ~= "number" and number or - util.secondsToClockDuration(duration_fmt, number, true, true, true) + datetime.secondsToClockDuration(duration_fmt, number, true, true, true) end function Usage:dump(kv_pairs, id) diff --git a/plugins/readtimer.koplugin/main.lua b/plugins/readtimer.koplugin/main.lua index 45d8d0c50..a27db1967 100644 --- a/plugins/readtimer.koplugin/main.lua +++ b/plugins/readtimer.koplugin/main.lua @@ -3,7 +3,7 @@ local InfoMessage = require("ui/widget/infomessage") local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") local logger = require("logger") -local util = require("util") +local datetime = require("datetime") local _ = require("gettext") local T = require("ffi/util").template @@ -70,7 +70,7 @@ function ReadTimer:addToMainMenu(menu_items) if self:scheduled() then local user_duration_format = G_reader_settings:readSetting("duration_format") return T(_("Read timer (%1)"), - util.secondsToClockDuration(user_duration_format, self:remaining(), false)) + datetime.secondsToClockDuration(user_duration_format, self:remaining(), false)) else return _("Read timer") end @@ -107,7 +107,7 @@ function ReadTimer:addToMainMenu(menu_items) -- @translators %1:%2 is a clock time (HH:MM), %3 is a duration text = T(_("Timer set for %1:%2.\n\nThat's %3 from now."), string.format("%02d", time.hour), string.format("%02d", time.min), - util.secondsToClockDuration(user_duration_format, seconds, false)), + datetime.secondsToClockDuration(user_duration_format, seconds, false)), timeout = 5, }) else @@ -151,7 +151,7 @@ function ReadTimer:addToMainMenu(menu_items) UIManager:show(InfoMessage:new{ -- @translators This is a duration text = T(_("Timer will expire in %1."), - util.secondsToClockDuration(user_duration_format, seconds, true)), + datetime.secondsToClockDuration(user_duration_format, seconds, true)), timeout = 5, }) remain_time = {time.hour, time.min} diff --git a/plugins/statistics.koplugin/calendarview.lua b/plugins/statistics.koplugin/calendarview.lua index 08b37cdd5..4058b5f96 100644 --- a/plugins/statistics.koplugin/calendarview.lua +++ b/plugins/statistics.koplugin/calendarview.lua @@ -1,3 +1,4 @@ + local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local BottomContainer = require("ui/widget/container/bottomcontainer") diff --git a/plugins/statistics.koplugin/main.lua b/plugins/statistics.koplugin/main.lua index 1a31f9da6..3b04f5a92 100644 --- a/plugins/statistics.koplugin/main.lua +++ b/plugins/statistics.koplugin/main.lua @@ -17,6 +17,7 @@ local SQ3 = require("lua-ljsqlite3/init") local SyncService = require("frontend/apps/cloudstorage/syncservice") local UIManager = require("ui/uimanager") local Widget = require("ui/widget/widget") +local datetime = require("datetime") local lfs = require("libs/libkoreader-lfs") local logger = require("logger") local util = require("util") @@ -75,43 +76,6 @@ local ReaderStatistics = Widget:extend{ data = nil, -- table } -local weekDays = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } -- in Lua wday order - -local shortDayOfWeekTranslation = { - ["Mon"] = _("Mon"), - ["Tue"] = _("Tue"), - ["Wed"] = _("Wed"), - ["Thu"] = _("Thu"), - ["Fri"] = _("Fri"), - ["Sat"] = _("Sat"), - ["Sun"] = _("Sun"), -} - -local longDayOfWeekTranslation = { - ["Mon"] = _("Monday"), - ["Tue"] = _("Tuesday"), - ["Wed"] = _("Wednesday"), - ["Thu"] = _("Thursday"), - ["Fri"] = _("Friday"), - ["Sat"] = _("Saturday"), - ["Sun"] = _("Sunday"), -} - -local monthTranslation = { - ["January"] = _("January"), - ["February"] = _("February"), - ["March"] = _("March"), - ["April"] = _("April"), - ["May"] = _("May"), - ["June"] = _("June"), - ["July"] = _("July"), - ["August"] = _("August"), - ["September"] = _("September"), - ["October"] = _("October"), - ["November"] = _("November"), - ["December"] = _("December"), -} - function ReaderStatistics:isDocless() return self.ui == nil or self.ui.document == nil or self.ui.document.is_pic == true end @@ -1023,26 +987,26 @@ The max value ensures a page you stay on for a long time (because you fell aslee { text_func = function() return T(_("Calendar weeks start on %1"), - longDayOfWeekTranslation[weekDays[self.settings.calendar_start_day_of_week]]) + datetime.shortDayOfWeekToLongTranslation[datetime.weekDays[self.settings.calendar_start_day_of_week]]) end, sub_item_table = { { -- Friday (Bangladesh and Maldives) - text = longDayOfWeekTranslation[weekDays[6]], + text = datetime.shortDayOfWeekToLongTranslation[datetime.weekDays[6]], checked_func = function() return self.settings.calendar_start_day_of_week == 6 end, callback = function() self.settings.calendar_start_day_of_week = 6 end }, { -- Saturday (some Middle East countries) - text = longDayOfWeekTranslation[weekDays[7]], + text = datetime.shortDayOfWeekToLongTranslation[datetime.weekDays[7]], checked_func = function() return self.settings.calendar_start_day_of_week == 7 end, callback = function() self.settings.calendar_start_day_of_week = 7 end }, { -- Sunday - text = longDayOfWeekTranslation[weekDays[1]], + text = datetime.shortDayOfWeekToLongTranslation[datetime.weekDays[1]], checked_func = function() return self.settings.calendar_start_day_of_week == 1 end, callback = function() self.settings.calendar_start_day_of_week = 1 end }, { -- Monday - text = longDayOfWeekTranslation[weekDays[2]], + text = datetime.shortDayOfWeekToLongTranslation[datetime.weekDays[2]], checked_func = function() return self.settings.calendar_start_day_of_week == 2 end, callback = function() self.settings.calendar_start_day_of_week = 2 end }, @@ -1491,27 +1455,27 @@ function ReaderStatistics:getCurrentStat() return { -- Global statistics (may consider other books than current book) -- since last resume - { _("Time spent reading this session"), util.secondsToClockDuration(user_duration_format, current_duration, false) }, + { _("Time spent reading this session"), datetime.secondsToClockDuration(user_duration_format, current_duration, false) }, { _("Pages read this session"), tonumber(current_pages) }, -- today - { _("Time spent reading today"), util.secondsToClockDuration(user_duration_format, today_duration, false) }, + { _("Time spent reading today"), datetime.secondsToClockDuration(user_duration_format, today_duration, false) }, { _("Pages read today"), tonumber(today_pages), separator = true }, -- Current book statistics -- Includes re-reads - { _("Total time spent on this book"), util.secondsToClockDuration(user_duration_format, total_time_book, false) }, + { _("Total time spent on this book"), datetime.secondsToClockDuration(user_duration_format, total_time_book, false) }, -- Capped to self.settings.max_sec per distinct page - { _("Time spent reading this book"), util.secondsToClockDuration(user_duration_format, book_read_time, false) }, + { _("Time spent reading this book"), datetime.secondsToClockDuration(user_duration_format, book_read_time, false) }, -- per days { _("Reading started"), os.date("%Y-%m-%d (%H:%M)", tonumber(first_open))}, { _("Days reading this book"), tonumber(total_days) }, - { _("Average time per day"), util.secondsToClockDuration(user_duration_format, book_read_time/tonumber(total_days), false) }, + { _("Average time per day"), datetime.secondsToClockDuration(user_duration_format, book_read_time/tonumber(total_days), false) }, -- per page (% read) - { _("Average time per page"), util.secondsToClockDuration(user_duration_format, self.avg_time, false) }, + { _("Average time per page"), datetime.secondsToClockDuration(user_duration_format, self.avg_time, false) }, { _("Pages read"), string.format("%d (%d%%)", total_read_pages, Math.round(100*total_read_pages/self.data.pages)) }, -- current page (% completed) { _("Current page/Total pages"), string.format("%d/%d (%d%%)", self.curr_page, self.data.pages, Math.round(100*self.curr_page/self.data.pages)) }, -- estimation, from current page to end of book - { _("Estimated time to read"), estimates_valid and util.secondsToClockDuration(user_duration_format, time_to_read, false) or _("N/A") }, + { _("Estimated time to read"), estimates_valid and datetime.secondsToClockDuration(user_duration_format, time_to_read, false) or _("N/A") }, { _("Estimated reading finished"), estimates_valid and T(N_("%1 (1 day)", "%1 (%2 days)", estimate_days_to_read), estimate_end_of_read_date, estimate_days_to_read) or _("N/A") }, @@ -1612,10 +1576,10 @@ function ReaderStatistics:getBookStat(id_book) { _("Reading started"), os.date("%Y-%m-%d (%H:%M)", tonumber(first_open))}, { _("Last read"), os.date("%Y-%m-%d (%H:%M)", tonumber(last_open))}, { _("Days reading this book"), tonumber(total_days) }, - { _("Total time spent on this book"), util.secondsToClockDuration(user_duration_format, total_time_book, false) }, - { _("Time spent reading this book"), util.secondsToClockDuration(user_duration_format, book_read_time, false) }, - { _("Average time per day"), util.secondsToClockDuration(user_duration_format, book_read_time/tonumber(total_days), false) }, - { _("Average time per page"), util.secondsToClockDuration(user_duration_format, avg_time_per_page, false) }, + { _("Total time spent on this book"), datetime.secondsToClockDuration(user_duration_format, total_time_book, false) }, + { _("Time spent reading this book"), datetime.secondsToClockDuration(user_duration_format, book_read_time, false) }, + { _("Average time per day"), datetime.secondsToClockDuration(user_duration_format, book_read_time/tonumber(total_days), false) }, + { _("Average time per page"), datetime.secondsToClockDuration(user_duration_format, avg_time_per_page, false) }, { _("Pages read"), string.format("%d (%d%%)", total_read_pages, Math.round(100*total_read_pages/pages)) }, { _("Last read page/Total pages"), string.format("%d/%d (%d%%)", last_page, pages, Math.round(100*last_page/pages)) }, { _("Highlights"), highlights, separator = true }, @@ -1813,13 +1777,13 @@ function ReaderStatistics:getDatesFromAll(sdays, ptype, book_mode) if ptype == "daily_weekday" then date_text = string.format("%s (%s)", os.date("%Y-%m-%d", timestamp), - shortDayOfWeekTranslation[os.date("%a", timestamp)]) + datetime.shortDayOfWeekTranslation[os.date("%a", timestamp)]) elseif ptype == "daily" then date_text = result_book[1][i] elseif ptype == "weekly" then date_text = T(_("%1 Week %2"), os.date("%Y", timestamp), os.date(" %W", timestamp)) elseif ptype == "monthly" then - date_text = monthTranslation[os.date("%B", timestamp)] .. os.date(" %Y", timestamp) + date_text = datetime.longMonthTranslation[os.date("%B", timestamp)] .. os.date(" %Y", timestamp) else date_text = result_book[1][i] end @@ -1839,7 +1803,7 @@ function ReaderStatistics:getDatesFromAll(sdays, ptype, book_mode) local stop_month = os.time{year=year_end, month=month_end, day=1, hour=0, min=0 } table.insert(results, { date_text, - T(_("Pages: (%1) Time: %2"), tonumber(result_book[2][i]), util.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false)), + T(_("Pages: (%1) Time: %2"), tonumber(result_book[2][i]), datetime.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false)), callback = function() self:callbackMonthly(start_month, stop_month, date_text, book_mode) end, @@ -1853,7 +1817,7 @@ function ReaderStatistics:getDatesFromAll(sdays, ptype, book_mode) begin_week = begin_week - weekday * 86400 table.insert(results, { date_text, - T(_("Pages: (%1) Time: %2"), tonumber(result_book[2][i]), util.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false)), + T(_("Pages: (%1) Time: %2"), tonumber(result_book[2][i]), datetime.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false)), callback = function() self:callbackWeekly(begin_week, begin_week + 7 * 86400, date_text, book_mode) end, @@ -1864,7 +1828,7 @@ function ReaderStatistics:getDatesFromAll(sdays, ptype, book_mode) - 60 * tonumber(string.sub(time_book,3,4)) - tonumber(string.sub(time_book,5,6)) table.insert(results, { date_text, - T(_("Pages: (%1) Time: %2"), tonumber(result_book[2][i]), util.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false)), + T(_("Pages: (%1) Time: %2"), tonumber(result_book[2][i]), datetime.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false)), callback = function() self:callbackDaily(begin_day, begin_day + 86400, date_text) end, @@ -1905,7 +1869,7 @@ function ReaderStatistics:getDaysFromPeriod(period_begin, period_end) day=string.sub(result_book[1][i],9,10), hour=0, min=0, sec=0 } table.insert(results, { result_book[1][i], - T(_("Pages: (%1) Time: %2"), tonumber(result_book[2][i]), util.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false)), + T(_("Pages: (%1) Time: %2"), tonumber(result_book[2][i]), datetime.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false)), callback = function() local kv = self.kv UIManager:close(kv) @@ -1949,7 +1913,7 @@ function ReaderStatistics:getBooksFromPeriod(period_begin, period_end, callback_ for i=1, #result_book.title do table.insert(results, { result_book[1][i], - T(_("%1 (%2)"), util.secondsToClockDuration(user_duration_format, tonumber(result_book[2][i]), false), tonumber(result_book[3][i])), + T(_("%1 (%2)"), datetime.secondsToClockDuration(user_duration_format, tonumber(result_book[2][i]), false), tonumber(result_book[3][i])), book_id = tonumber(result_book[4][i]), callback = function() local kv = self.kv @@ -2056,7 +2020,7 @@ function ReaderStatistics:getDatesForBook(id_book) for i=1, #result_book.dates do table.insert(results, { result_book[1][i], - T(_("Pages: (%1) Time: %2"), tonumber(result_book[2][i]), util.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false)), + T(_("Pages: (%1) Time: %2"), tonumber(result_book[2][i]), datetime.secondsToClockDuration(user_duration_format, tonumber(result_book[3][i]), false)), hold_callback = function(kv_page, kv_item) self:resetStatsForBookForPeriod(id_book, result_book[4][i], result_book[5][i], result_book[1][i], function() kv_page:removeKeyValueItem(kv_item) -- Reset, refresh what's displayed @@ -2155,7 +2119,7 @@ function ReaderStatistics:getTotalStats() end table.insert(total_stats, { book_title, - util.secondsToClockDuration(user_duration_format, total_time_book, false), + datetime.secondsToClockDuration(user_duration_format, total_time_book, false), callback = function() local kv = self.kv UIManager:close(self.kv) @@ -2176,7 +2140,7 @@ function ReaderStatistics:getTotalStats() end conn:close() - return T(_("Total time spent reading: %1"), util.secondsToClockDuration(user_duration_format, total_books_time, false)), total_stats + return T(_("Total time spent reading: %1"), datetime.secondsToClockDuration(user_duration_format, total_books_time, false)), total_stats end function ReaderStatistics:genResetBookSubItemTable() @@ -2256,7 +2220,7 @@ function ReaderStatistics:resetPerBook() if id_book ~= self.id_curr_book then table.insert(total_stats, { book_title, - util.secondsToClockDuration(user_duration_format, total_time_book, false), + datetime.secondsToClockDuration(user_duration_format, total_time_book, false), id_book, callback = function(kv_page, kv_item) UIManager:show(ConfirmBox:new{ @@ -2599,9 +2563,9 @@ function ReaderStatistics:onShowCalendarView() local CalendarView = require("calendarview") UIManager:show(CalendarView:new{ reader_statistics = self, - monthTranslation = monthTranslation, - shortDayOfWeekTranslation = shortDayOfWeekTranslation, - longDayOfWeekTranslation = longDayOfWeekTranslation, + monthTranslation = datetime.longMonthTranslation, + shortDayOfWeekTranslation = datetime.shortDayOfWeekTranslation, + longDayOfWeekTranslation = datetime.shortDayOfWeekToLongTranslation, start_day_of_week = self.settings.calendar_start_day_of_week, nb_book_spans = self.settings.calendar_nb_book_spans, show_hourly_histogram = self.settings.calendar_show_histogram, @@ -2616,7 +2580,7 @@ function ReaderStatistics:onShowCalendarDayView() local title_callback = function(this) local day = os.date("%Y-%m-%d", this.day_ts + 10800) -- use 03:00 to determine date (summer time change) local date = os.date("*t", this.day_ts + 10800) - return string.format("%s (%s)", day, longDayOfWeekTranslation[CalendarView.weekdays[date.wday]]) + return string.format("%s (%s)", day, datetime.longDayOfWeekTranslation[CalendarView.weekdays[date.wday]]) end CalendarView:showCalendarDayView(self, title_callback) end diff --git a/plugins/statistics.koplugin/readerprogress.lua b/plugins/statistics.koplugin/readerprogress.lua index ee145a05e..928ac9ee5 100644 --- a/plugins/statistics.koplugin/readerprogress.lua +++ b/plugins/statistics.koplugin/readerprogress.lua @@ -18,7 +18,7 @@ local TitleBar = require("ui/widget/titlebar") local UIManager = require("ui/uimanager") local VerticalGroup = require("ui/widget/verticalgroup") local VerticalSpan = require("ui/widget/verticalspan") -local util = require("util") +local datetime = require("datetime") local _ = require("gettext") local Screen = Device.screen @@ -251,7 +251,7 @@ function ReaderProgress:genWeekStats(stats_day) dimen = Geom:new{ w = self.screen_width , h = height * (1/3) }, TextWidget:new{ padding = Size.padding.small, - text = date_format_show .. " - " .. util.secondsToClockDuration(user_duration_format, select_day_time, true), + text = date_format_show .. " - " .. datetime.secondsToClockDuration(user_duration_format, select_day_time, true), face = Font:getFace("smallffont"), }, }, @@ -345,7 +345,7 @@ function ReaderProgress:genSummaryDay(width) CenterContainer:new{ dimen = Geom:new{ w = tile_width, h = tile_height }, TextWidget:new{ - text = util.secondsToClockDuration(user_duration_format, self.current_duration, true), + text = datetime.secondsToClockDuration(user_duration_format, self.current_duration, true), face = self.medium_font_face, }, }, @@ -359,7 +359,7 @@ function ReaderProgress:genSummaryDay(width) CenterContainer:new{ dimen = Geom:new{ w = tile_width, h = tile_height }, TextWidget:new{ - text = util.secondsToClockDuration(user_duration_format, self.today_duration, true), + text = datetime.secondsToClockDuration(user_duration_format, self.today_duration, true), face = self.medium_font_face, }, }, @@ -447,7 +447,7 @@ function ReaderProgress:genSummaryWeek(width) CenterContainer:new{ dimen = Geom:new{ w = tile_width, h = tile_height }, TextWidget:new{ - text = util.secondsToClockDuration(user_duration_format, math.floor(total_time), true), + text = datetime.secondsToClockDuration(user_duration_format, math.floor(total_time), true), face = self.medium_font_face, }, }, @@ -461,7 +461,7 @@ function ReaderProgress:genSummaryWeek(width) CenterContainer:new{ dimen = Geom:new{ w = tile_width, h = tile_height }, TextWidget:new{ - text = util.secondsToClockDuration(user_duration_format, math.floor(total_time) * (1/7), true), + text = datetime.secondsToClockDuration(user_duration_format, math.floor(total_time) * (1/7), true), face = self.medium_font_face, } } diff --git a/plugins/systemstat.koplugin/main.lua b/plugins/systemstat.koplugin/main.lua index 2fea900b5..30bbcca58 100644 --- a/plugins/systemstat.koplugin/main.lua +++ b/plugins/systemstat.koplugin/main.lua @@ -4,6 +4,7 @@ local KeyValuePage = require("ui/widget/keyvaluepage") local Math = require("optmath") local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") +local datetime = require("datetime") local time = require("ui/time") local util = require("util") local _ = require("gettext") @@ -69,21 +70,21 @@ function SystemStat:appendCounters() standby = Device.total_standby_time end self:put({" " .. _("Up time"), - util.secondsToClockDuration("", time.to_s(uptime), false, true, true)}) + datetime.secondsToClockDuration("", time.to_s(uptime), false, true, true)}) if Device:canSuspend() or Device:canStandby() then local awake = uptime - suspend - standby self:put({" " .. _("Time spent awake"), - util.secondsToClockDuration("", time.to_s(awake), false, true, true) + datetime.secondsToClockDuration("", time.to_s(awake), false, true, true) .. " (" .. Math.round((awake / uptime) * 100) .. "%)"}) end if Device:canSuspend() then self:put({" " .. _("Time in suspend"), - util.secondsToClockDuration("", time.to_s(suspend), false, true, true) + datetime.secondsToClockDuration("", time.to_s(suspend), false, true, true) .. " (" .. Math.round((suspend / uptime) * 100) .. "%)"}) end if Device:canStandby() then self:put({" " .. _("Time in standby"), - util.secondsToClockDuration("", time.to_s(standby), false, true, true) + datetime.secondsToClockDuration("", time.to_s(standby), false, true, true) .. " (" .. Math.round((standby / uptime) * 100) .. "%)"}) end self:put({_("Counters"), ""}) diff --git a/spec/unit/datetime_spec.lua b/spec/unit/datetime_spec.lua new file mode 100644 index 000000000..cd915671c --- /dev/null +++ b/spec/unit/datetime_spec.lua @@ -0,0 +1,188 @@ +describe("datetime module", function() + local datetime + setup(function() + require("commonrequire") + datetime = require("datetime") + end) + + describe("secondsToClock()", function() + it("should convert seconds to 00:00 format", function() + assert.is_equal("00:00", + datetime.secondsToClock(0, true)) + assert.is_equal("00:01", + datetime.secondsToClock(60, true)) + end) + it("should round seconds to minutes in 00:00 format", function() + assert.is_equal("00:01", + datetime.secondsToClock(89, true)) + assert.is_equal("00:02", + datetime.secondsToClock(90, true)) + assert.is_equal("00:02", + datetime.secondsToClock(110, true)) + assert.is_equal("00:02", + datetime.secondsToClock(120, true)) + assert.is_equal("01:00", + datetime.secondsToClock(3600, true)) + assert.is_equal("01:00", + datetime.secondsToClock(3599, true)) + assert.is_equal("01:00", + datetime.secondsToClock(3570, true)) + assert.is_equal("00:59", + datetime.secondsToClock(3569, true)) + end) + it("should convert seconds to 00:00:00 format", function() + assert.is_equal("00:00:00", + datetime.secondsToClock(0)) + assert.is_equal("00:01:00", + datetime.secondsToClock(60)) + assert.is_equal("00:01:29", + datetime.secondsToClock(89)) + assert.is_equal("00:01:30", + datetime.secondsToClock(90)) + assert.is_equal("00:01:50", + datetime.secondsToClock(110)) + assert.is_equal("00:02:00", + datetime.secondsToClock(120)) + end) + end) + + describe("secondsToHClock()", function() + it("should convert seconds to 0'00'' format", function() + assert.is_equal("0'", + datetime.secondsToHClock(0, true)) + assert.is_equal("0'", + datetime.secondsToHClock(29, true)) + assert.is_equal("1'", + datetime.secondsToHClock(60, true)) + end) + it("should round seconds to minutes in 0h00' format", function() + assert.is_equal("1'", + datetime.secondsToHClock(89, true)) + assert.is_equal("2'", + datetime.secondsToHClock(90, true)) + assert.is_equal("2'", + datetime.secondsToHClock(110, true)) + assert.is_equal("2'", + datetime.secondsToHClock(120, true)) + assert.is_equal("1h00'", + datetime.secondsToHClock(3600, true)) + assert.is_equal("1h00'", + datetime.secondsToHClock(3599, true)) + assert.is_equal("1h00'", + datetime.secondsToHClock(3570, true)) + assert.is_equal("59'", + datetime.secondsToHClock(3569, true)) + assert.is_equal("10h01'", + datetime.secondsToHClock(36060, true)) + end) + it("should round seconds to minutes in 0h00m format", function() + assert.is_equal("1m", + datetime.secondsToHClock(89, true, true)) + assert.is_equal("2m", + datetime.secondsToHClock(90, true, true)) + assert.is_equal("2m", + datetime.secondsToHClock(110, true, true)) + assert.is_equal("1h00m", + datetime.secondsToHClock(3600, true, true)) + assert.is_equal("1h00m", + datetime.secondsToHClock(3599, true, true)) + assert.is_equal("59m", + datetime.secondsToHClock(3569, true, true)) + assert.is_equal("10h01m", + datetime.secondsToHClock(36060, true, true)) + end) + it("should convert seconds to 0h00'00'' format", function() + assert.is_equal("0\"", + datetime.secondsToHClock(0)) + assert.is_equal("1'00\"", + datetime.secondsToHClock(60)) + assert.is_equal("1'29\"", + datetime.secondsToHClock(89)) + assert.is_equal("1'30\"", + datetime.secondsToHClock(90)) + assert.is_equal("1'50\"", + datetime.secondsToHClock(110)) + assert.is_equal("2'00\"", + datetime.secondsToHClock(120)) + end) + end) + + describe("secondsToClockDuration()", function() + it("should change type based on format", function() + assert.is_equal("10h01m30s", + datetime.secondsToClockDuration("modern", 36090, false, true)) + assert.is_equal("10:01:30", + datetime.secondsToClockDuration("classic", 36090, false)) + assert.is_equal("10:01:30", + datetime.secondsToClockDuration("unknown", 36090, false)) + assert.is_equal("10:01:30", + datetime.secondsToClockDuration(nil, 36090, false)) + end) + it("should pass along withoutSeconds", function() + assert.is_equal("10h01m30s", + datetime.secondsToClockDuration("modern", 36090, false, true)) + assert.is_equal("10h02m", + datetime.secondsToClockDuration("modern", 36090, true, true)) + assert.is_equal("10:01:30", + datetime.secondsToClockDuration("classic", 36090, false)) + assert.is_equal("10:02", + datetime.secondsToClockDuration("classic", 36090, true)) + end) + it("should pass along hmsFormat for modern format", function() + assert.is_equal("10h01'30\"", + datetime.secondsToClockDuration("modern", 36090)) + assert.is_equal("10h01m30s", + datetime.secondsToClockDuration("modern", 36090, false, true)) + assert.is_equal("10h02m", + datetime.secondsToClockDuration("modern", 36090, true, true)) + assert.is_equal("10h02'", + datetime.secondsToClockDuration("modern", 36090, true, false)) + assert.is_equal("10:01:30", + datetime.secondsToClockDuration("classic", 36090, false, true)) + assert.is_equal("10:01:30", + datetime.secondsToClockDuration("classic", 36090, false, false)) + end) + end) + + describe("secondsToDate()", function() + it("should deliver a date string", function() + local time = { year=2022, month=12, day=6, hour=13, min=30, sec=35 } + local time_s = os.time(time) + + assert.is_equal("2022-12-06", + datetime.secondsToDate(time_s)) + assert.is_equal("2022-12-07", + datetime.secondsToDate(time_s + 86400)) -- one day later + assert.is_equal("Tue Dec 06 2022", + datetime.secondsToDate(time_s, true)) + assert.is_equal("Wed Dec 07 2022", + datetime.secondsToDate(time_s + 86400, true)) + end) + end) + describe("secondsToDateTime()", function() + it("should should deliver a date and time string", function() + local time = { year=2022, month=11, day=20, hour=9, min=57, sec=39 } + local time_s = os.time(time) + + assert.is_equal("2022-11-20 9:57", + datetime.secondsToDateTime(time_s)) + assert.is_equal("2022-11-21 9:57", + datetime.secondsToDateTime(time_s + 86400)) + + assert.is_equal("2022-11-20 9:57 AM", + datetime.secondsToDateTime(time_s, true)) + assert.is_equal("2022-11-21 9:57 AM", + datetime.secondsToDateTime(time_s + 86400, true)) + + assert.is_equal("Sun Nov 20 2022 9:57", + datetime.secondsToDateTime(time_s, false, true)) + assert.is_equal("Mon Nov 21 2022 9:57", + datetime.secondsToDateTime(time_s + 86400, false, true)) + + assert.is_equal("Sun Nov 20 2022 9:57 AM", + datetime.secondsToDateTime(time_s, true, true)) + assert.is_equal("Mon Nov 21 2022 9:57 AM", + datetime.secondsToDateTime(time_s + 86400, true, true)) + end) + end) +end) diff --git a/spec/unit/util_spec.lua b/spec/unit/util_spec.lua index 66d10bcda..9432bddcb 100644 --- a/spec/unit/util_spec.lua +++ b/spec/unit/util_spec.lua @@ -461,145 +461,6 @@ describe("util module", function() end) end) - describe("secondsToClock()", function() - it("should convert seconds to 00:00 format", function() - assert.is_equal("00:00", - util.secondsToClock(0, true)) - assert.is_equal("00:01", - util.secondsToClock(60, true)) - end) - it("should round seconds to minutes in 00:00 format", function() - assert.is_equal("00:01", - util.secondsToClock(89, true)) - assert.is_equal("00:02", - util.secondsToClock(90, true)) - assert.is_equal("00:02", - util.secondsToClock(110, true)) - assert.is_equal("00:02", - util.secondsToClock(120, true)) - assert.is_equal("01:00", - util.secondsToClock(3600, true)) - assert.is_equal("01:00", - util.secondsToClock(3599, true)) - assert.is_equal("01:00", - util.secondsToClock(3570, true)) - assert.is_equal("00:59", - util.secondsToClock(3569, true)) - end) - it("should convert seconds to 00:00:00 format", function() - assert.is_equal("00:00:00", - util.secondsToClock(0)) - assert.is_equal("00:01:00", - util.secondsToClock(60)) - assert.is_equal("00:01:29", - util.secondsToClock(89)) - assert.is_equal("00:01:30", - util.secondsToClock(90)) - assert.is_equal("00:01:50", - util.secondsToClock(110)) - assert.is_equal("00:02:00", - util.secondsToClock(120)) - end) - end) - - describe("secondsToHClock()", function() - it("should convert seconds to 0'00'' format", function() - assert.is_equal("0'", - util.secondsToHClock(0, true)) - assert.is_equal("0'", - util.secondsToHClock(29, true)) - assert.is_equal("1'", - util.secondsToHClock(60, true)) - end) - it("should round seconds to minutes in 0h00' format", function() - assert.is_equal("1'", - util.secondsToHClock(89, true)) - assert.is_equal("2'", - util.secondsToHClock(90, true)) - assert.is_equal("2'", - util.secondsToHClock(110, true)) - assert.is_equal("2'", - util.secondsToHClock(120, true)) - assert.is_equal("1h00'", - util.secondsToHClock(3600, true)) - assert.is_equal("1h00'", - util.secondsToHClock(3599, true)) - assert.is_equal("1h00'", - util.secondsToHClock(3570, true)) - assert.is_equal("59'", - util.secondsToHClock(3569, true)) - assert.is_equal("10h01'", - util.secondsToHClock(36060, true)) - end) - it("should round seconds to minutes in 0h00m format", function() - assert.is_equal("1m", - util.secondsToHClock(89, true, true)) - assert.is_equal("2m", - util.secondsToHClock(90, true, true)) - assert.is_equal("2m", - util.secondsToHClock(110, true, true)) - assert.is_equal("1h00m", - util.secondsToHClock(3600, true, true)) - assert.is_equal("1h00m", - util.secondsToHClock(3599, true, true)) - assert.is_equal("59m", - util.secondsToHClock(3569, true, true)) - assert.is_equal("10h01m", - util.secondsToHClock(36060, true, true)) - end) - it("should convert seconds to 0h00'00'' format", function() - assert.is_equal("0\"", - util.secondsToHClock(0)) - assert.is_equal("1'00\"", - util.secondsToHClock(60)) - assert.is_equal("1'29\"", - util.secondsToHClock(89)) - assert.is_equal("1'30\"", - util.secondsToHClock(90)) - assert.is_equal("1'50\"", - util.secondsToHClock(110)) - assert.is_equal("2'00\"", - util.secondsToHClock(120)) - end) - end) - - describe("secondsToClockDuration()", function() - it("should change type based on format", function() - assert.is_equal("10h01m30s", - util.secondsToClockDuration("modern", 36090, false, true)) - assert.is_equal("10:01:30", - util.secondsToClockDuration("classic", 36090, false)) - assert.is_equal("10:01:30", - util.secondsToClockDuration("unknown", 36090, false)) - assert.is_equal("10:01:30", - util.secondsToClockDuration(nil, 36090, false)) - end) - it("should pass along withoutSeconds", function() - assert.is_equal("10h01m30s", - util.secondsToClockDuration("modern", 36090, false, true)) - assert.is_equal("10h02m", - util.secondsToClockDuration("modern", 36090, true, true)) - assert.is_equal("10:01:30", - util.secondsToClockDuration("classic", 36090, false)) - assert.is_equal("10:02", - util.secondsToClockDuration("classic", 36090, true)) - end) - it("should pass along hmsFormat for modern format", function() - assert.is_equal("10h01'30\"", - util.secondsToClockDuration("modern", 36090)) - assert.is_equal("10h01m30s", - util.secondsToClockDuration("modern", 36090, false, true)) - assert.is_equal("10h02m", - util.secondsToClockDuration("modern", 36090, true, true)) - assert.is_equal("10h02'", - util.secondsToClockDuration("modern", 36090, true, false)) - assert.is_equal("10:01:30", - util.secondsToClockDuration("classic", 36090, false, true)) - assert.is_equal("10:01:30", - util.secondsToClockDuration("classic", 36090, false, false)) - end) - end) -- end my changes - describe("urlEncode() and urlDecode", function() it("should encode string", function() assert.is_equal("Secret_Password123", util.urlEncode("Secret_Password123"))