Solution 1: use timestamp(as string/tuple format) as key to hash, linear look for recorded logs

class LogSystem:

    def __init__(self):
        self.d = {}
        #self.gr = {'Year':1,'Month':2,'Day':3,'Hour':4,'Minute':5,'Second':6}
        self.gr = {'Year':5,'Month':8,'Day':11,'Hour':14,'Minute':17,'Second':20}
    def put(self, id: int, timestamp: str) -> None:
        """ time O(W) W:length of timestamp space O(1) for each operation 
        main idea: take timestamp as key, id as value, store it into dictionary.  Note, compare tuple v.s. compare substring when you use str.split() or not 
        """
        #tup = tuple(timestamp.split(':'))
        #self.d[tup] = id
        self.d[timestamp] = id
    def retrieve(self, start: str, end: str, granularity: str) -> List[int]:
        res = []
        idx = self.gr[granularity]
        s = start[:idx]
        e = end[:idx]
        #s = tuple(start.split(':')[:idx])
        #e = tuple(end.split(':')[:idx])
        for k,v in self.d.items():
            if s<=k[:idx]<=e:
                res.append(v)
        return res 
# Your LogSystem object will be instantiated and called as such:
# obj = LogSystem()
# obj.put(id,timestamp)
# param_2 = obj.retrieve(start,end,granularity)

Optimization: inseatd of searching in lineaer, using TreeMap data structure and subMap to look for target range in O(lgN) time; use