Python convert seconds to days, hours, minutes

Patching as well Ralph Bolton's answer. Moving to a class and moving tulp of tulp [intervals] to dictionary. Adding an optional rounded function depending of granularity [enable by default]. Ready to translation using gettext [default is disable]. This is intend to be load from an module. This is for python3 [tested 3.6 - 3.8]

import gettext
import locale
from itertools import chain

mylocale = locale.getdefaultlocale[]
# see --> //stackoverflow.com/a/10174657/11869956 thx 
#localedir = os.path.join[os.path.dirname[__file__], 'locales']
# or python > 3.4:
try:
    localedir = pathlib.Path[__file__].parent/'locales'
    lang_translations = gettext.translation['utils', localedir, 
                                            languages=[mylocale[0]]]
    lang_translations.install[]
    _ = lang_translations.gettext
except Exception as exc:
    print['Error: unexcept error while initializing translation:', file=sys.stderr]
    print[f'Error: {exc}', file=sys.stderr]
    print[f'Error: localedir={localedir}, languages={mylocale[0]}', file=sys.stderr]
    print['Error: translation has been disabled.', file=sys.stderr]
    _ = gettext.gettext

Here is the class:

class FormatTimestamp:
    """Convert seconds to, optional rounded, time depending of granularity's degrees.
        inspired by //stackoverflow.com/a/24542445/11869956"""
    def __init__[self]:
        # For now i haven't found a way to do it better
        # TODO: optimize ?!? ;]
        self.intervals = {
            # 'years'     :   31556952,  # //www.calculateme.com/time/years/to-seconds/
            # //www.calculateme.com/time/months/to-seconds/ -> 2629746 seconds
            # But it's outputing some strange result :
            # So 3 seconds less [2629743] : 4 weeks, 2 days, 10 hours, 29 minutes and 3 seconds
            # than after 3 more seconds : 1 month ?!?
            # Google give me 2628000 seconds
            # So 3 seconds less [2627997]: 4 weeks, 2 days, 9 hours, 59 minutes and 57 seconds
            # Strange as well 
            # So for the moment latest is week ...
            #'months'    :   2419200, # 60 * 60 * 24 * 7 * 4 
            'weeks'     :   604800,  # 60 * 60 * 24 * 7
            'days'      :   86400,    # 60 * 60 * 24
            'hours'     :   3600,    # 60 * 60
            'minutes'   :   60,
            'seconds'  :   1
            }
        self.nextkey = {
            'seconds'   :   'minutes',
            'minutes'   :   'hours',
            'hours'     :   'days',
            'days'      :   'weeks',
            'weeks'     :   'weeks',
            #'months'    :   'months',
            #'years'     :   'years' # stop here
            }
        self.translate = {
            'weeks'     :   _['weeks'],
            'days'      :   _['days'],
            'hours'     :   _['hours'],
            'minutes'   :   _['minutes'],
            'seconds'   :   _['seconds'],
            ## Single
            'week'      :   _['week'],
            'day'       :   _['day'],
            'hour'      :   _['hour'],
            'minute'    :   _['minute'],
            'second'    :   _['second'],
            ' and'      :   _['and'],
            ','         :   _[','],     # This is for compatibility
            ''          :   '\0'        # same here BUT we CANNOT pass empty string to gettext 
                                        # or we get : warning: Empty msgid.  It is reserved by GNU gettext:
                                        # gettext[""] returns the header entry with
                                        # meta information, not the empty string.
                                        # Thx to --> //stackoverflow.com/a/30852705/11869956 - saved my day
            }

    def convert[self, seconds, granularity=2, rounded=True, translate=False]:
        """Proceed the conversion"""

        def _format[result]:
            """Return the formatted result
            TODO : numpy / google docstrings"""
            start = 1 
            length = len[result]
            none = 0
            next_item = False
            for item in reversed[result[:]]:
                if item['value']:
                    # if we have more than one item
                    if length - none > 1:
                        # This is the first 'real' item 
                        if start == 1:
                            item['punctuation'] = ''
                            next_item = True
                        elif next_item:
                            # This is the second 'real' item
                            # Happened 'and' to key name
                            item['punctuation'] = ' and'
                            next_item = False
                        # If there is more than two 'real' item
                        # than happened ','
                        elif 2 < start:
                            item['punctuation'] = ','
                        else:
                            item['punctuation'] = ''
                    else:
                        item['punctuation'] = ''
                    start += 1
                else:
                    none += 1
            return [ { 'value'        :   mydict['value'], 
                       'name'         :   mydict['name_strip'],
                       'punctuation'  :   mydict['punctuation'] } for mydict in result \
                                                                  if mydict['value'] is not None ]


        def _rstrip[value, name]:
            """Rstrip 's' name depending of value"""
            if value == 1:
                name = name.rstrip['s']
            return name


        # Make sure granularity is an integer
        if not isinstance[granularity, int]:
            raise ValueError[f'Granularity should be an integer: {granularity}']

        # For seconds only don't need to compute
        if seconds < 0:
            return 'any time now.'
        elif seconds < 60:
            return 'less than a minute.'

        result = []
        for name, count in self.intervals.items[]:
            value = seconds // count
            if value:
                seconds -= value * count
                name_strip = _rstrip[value, name]
                # save as dict: value, name_strip [eventually strip], name [for reference], value in seconds
                # and count [for reference]
                result.append[{ 
                        'value'        :   value,
                        'name_strip'   :   name_strip,
                        'name'         :   name, 
                        'seconds'      :   value * count,
                        'count'        :   count
                                 }]
            else:
                if len[result] > 0:
                    # We strip the name as second == 0
                    name_strip = name.rstrip['s']
                    # adding None to key 'value' but keep other value
                    # in case when need to add seconds when we will 
                    # recompute every thing
                    result.append[{ 
                        'value'        :   None,
                        'name_strip'   :   name_strip,
                        'name'         :   name, 
                        'seconds'      :   0,
                        'count'        :   count
                                 }]

        # Get the length of the list
        length = len[result]
        # Don't need to compute everything / every time
        if length < granularity or not rounded:
            if translate:
                return ' '.join['{0} {1}{2}'.format[item['value'], _[self.translate[item['name']]], 
                                                _[self.translate[item['punctuation']]]] \
                                                for item in _format[result]]
            else:
                return ' '.join['{0} {1}{2}'.format[item['value'], item['name'], item['punctuation']] \
                                                for item in _format[result]]

        start = length - 1
        # Reverse list so the firsts elements 
        # could be not selected depending on granularity.
        # And we can delete item after we had his seconds to next
        # item in the current list [result]
        for item in reversed[result[:]]:
            if granularity 

Chủ Đề