2024年10月3日 星期四

MicroPython 學習筆記 : 手刻 strftime() 與 strptime() 函式

在 CPython 中處理日期時間最常用的內建模組是 datetime, 特別是要將 datetime 物件格式化成日期時間字串時的 strftime() 與將日期時間字串轉回 datetime 物件的 strptime() 這兩個方法/函式最常用 (兩者是反運算). 

在 CPython 中, 要將日期時間字串轉成 datetime 物件可以直接呼叫 datetime 類別的靜態方法 strptime() 例如 : 

>>> from datetime import datetime  
>>> datetime.strptime('2024-10-03 09:04:35.123456', '%Y-%m-%d %H:%M:%S.%f')    
datetime.datetime(2024, 10, 3, 9, 4, 35, 123456)    

也可以呼叫 datetime 物件的 strptime() 方法, 例如 : 

>>> from datetime import datetime  
>>> today=datetime.today()   # 用 now() 也可以
>>> today    
datetime.datetime(2024, 10, 3, 9, 11, 55, 129274)   
>>> today.strptime('2024-10-03 09:04:35.123456', '%Y-%m-%d %H:%M:%S.%f')    
datetime.datetime(2024, 10, 3, 9, 4, 35, 123456)

此處我們只是借用 today 這個 datetime 物件的 strptime() 方法而已, strptime() 實際要轉換的日期時間字串內容與 today 物件自己的日期時間內容完全無關, 而是由傳入的第一參數 (日期時間字串). 

但是將 datetime 物件轉成日期時間字串的 strftime() 則必須使用物件方法, 因為它的傳入參數只有格式化串一個而已 (datetime 類別的 strftime() 靜態方法也是), 所以轉換的對象就是 datetime 物件本身 : 

>>> today.strftime('%Y-%m-%d %H:%M:%S.%f')    
'2024-10-03 09:11:55.129274'   

總之, 在 CPython 環境中, strptime() 既可以當類別的靜態方法用, 也可以當物件方法用; 但 strftime() 則只能當物件方法用, 參考 : 

但是很可惜, MicroPython 並未實作 datetime 模組, 不論是匯入 datetime 或 udatetime 都會出錯 :

>>> import datetime   
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: no module named 'datetime'
>>> import udatetime    
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: no module named 'udatetime'

所以在 MicroPython 中如果要用到 strftime() 與 strptime() 的功能必須手刻, 但不需要完全實作 CPython 的 strftime() 與 strptime() 之完整功能, 只要針對物聯網最常用的 "%Y-%m-%d %H:%M:%S" 格式進行轉換即可. 

strftime() 須用到 time 模組來產生日期時間物件, 其 localtime() 函式會傳回年月日時分秒的日期時間 tuple 物件, 透過字串的 replace() 方法將格式化字串中的相對應子字串用 tuple 中的元素取代. 傳入參數為格式化字串 format_str 與日期時間元組 dt (預設為 None 表示目前的日期時間), 傳回值為替換後的格式化字串 :  

import time

def strftime(dt=None, format_str="%Y-%m-%d %H:%M:%S"):
    if dt is None:
        dt=time.localtime()
    return format_str.replace("%Y", str(dt[0])) \
                        .replace("%m", "{:02d}".format(dt[1])) \
                        .replace("%d", "{:02d}".format(dt[2])) \
                        .replace("%H", "{:02d}".format(dt[3])) \
                        .replace("%M", "{:02d}".format(dt[4])) \
                        .replace("%S", "{:02d}".format(dt[5]))

此函式兩個參數皆有預設值, 未傳入參數時就傳回目前時間的格式化字串, 傳入年月日時分秒 tuple 時小於 10 也會補 0, 測試如下 :

>>> now=time.localtime()    
>>> now   
(2024, 10, 3, 9, 28, 40, 3, 277)
>>> strftime(now)      
'2024-10-03 09:28:40'
>>> strftime()                # 未傳參數 : 現在時間
'2024-10-03 11:59:48'
>>> strftime((2024, 1 , 1, 11, 2, 3))     # 有傳參數 : 指定時間
'2024-01-01 11:02:03'

而 strptime() 函式則是傳入日期時間字串 dt_str 與格式化字串 format_str, 經過字串剖析後取出年月日時分秒各元素後組成 tuple 物件傳回 : 

def strptime(dt_str, format_str="%Y-%m-%d %H:%M:%S"):
    if format_str=="%Y-%m-%d %H:%M:%S":
        year=int(dt_str[0:4])
        month=int(dt_str[5:7])
        day=int(dt_str[8:10])
        hour=int(dt_str[11:13])
        minute=int(dt_str[14:16])
        second=int(dt_str[17:19])
        return (year, month, day, hour, minute, second, 0, 0, 0)
    else:
        raise ValueError("Unsupported format string")

測試如下 :

>>> dt=strptime('2024-10-03 09:28:40')   
>>> dt   
(2024, 10, 3, 9, 28, 40, 0, 0, 0)

檢視 xtools 函式庫已有一個類似 strftime() 功能的函式 format_datetime() : 

def format_datetime(local_time):
    Y,M,D,H,m,S,W,ds = local_time
    t = str(Y) + '-'
    t += pad_zero(M)
    t += '-'
    t += pad_zero(D)
    t += ' '
    t += pad_zero(H)
    t += ':'
    t += pad_zero(m)
    t += ':'
    t += pad_zero(S)
    return t

它也能將傳入的日期時間元組轉成日期時間字串傳回 : 

>>> import xtools   
>>> import time 
>>> now=time.localtime()   
>>> now     
(2024, 10, 3, 12, 23, 58, 3, 277) 
>>> xtools.format_datetime(now)         
'2024-10-03 12:23:58'

不過要注意的是如果要自行傳入日期時間元組給 xtools.format_datetime() 時必須傳入 8 個元素 (後面兩個設為 0 即可), 不能只傳年月日時分秒 6 個:

>>> xtools.format_datetime((2024, 10, 3, 12, 34, 56))    
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "xtools.py", line 191, in format_datetime
ValueError: need more than 6 values to unpack   
>>> xtools.format_datetime((2024, 10, 3, 12, 34, 56, 0, 0))     
'2024-10-03 12:34:56'

我已將此兩函式加入 xtools 函式庫中, 因為我覺得函式名稱較習慣 :

>>> import time  
>>> now=time.localtime()     
>>> xtools.strftime(now)     
'2024-10-03 10:24:18'
>>> xtools.strftime((2024, 10, 3, 12, 34, 56))    
'2024-10-03 12:34:56'
>>> dt=xtools.strptime('2024-10-03 10:24:18')   
>>> dt   
(2024, 10, 3, 10, 24, 18, 0, 0, 0)

沒有留言 :