Linux C的localtime函式問題

  
剛好前陣子工作時遇到一個讓我找了一整天的BUG,在這裡跟大家分享一下,免得以後還有其他受到這個問題的殘害。在C語言的library中,在 linux 下的時區的設定是利用TZ這個環境變數來指定,如果TZ環境變數沒有設定,則會以/etc/localtime為主。
/etc/localtime檔案可能是個符號連結到一個時區設定檔,或是一個真實檔案。大部份linux系統會將環境變數指向
/etc/localtime,“TZ=/etc/localtime”,再藉由/etc/localtime來指向時區檔案。在C語言中,如果時區發現變化,會使用 tzset() 涵數讓系統重新讀取到新的時間,但這個動作並無法發揮作用,導致/etc/localtime產生變化後,而 localtime() 仍然會使用舊的時區來讀取時間。tzset() 會 reset 三個C的內部變數,分別為 tzname丶timezone丶daylight。在此提供一個方法避開這個問題,因為tzset()無法成功的 reset 這三個變數,使得 localtime() 察覺不到時區的變化。
範例如下:
    時間12:00
    環境變數:
    TZ=”/etc/localtime”
    時區檔案:
    /etc/localtime -> /mnt/html/zoneinfo/GMT+08:00
    透過date指令得到的時間為12:00
    透過localtime()涵數讀取到的時間為12:00

    現將/etc/localtime指向新的時區檔案:
    /etc/localtime -> /mnt/html/zoneinfo/GMT+06:00
    透過date指令得到的時間為10:00
    透過localtime()涵數讀取到的時間仍然為12:00
因為導致使用 localtime() 涵數的功能發生時間上的錯誤。解決方法如下:
 
 可以透過TZ環境變數的強制改變,來使 tzname丶timezone丶daylight 這三個內部變數更新到新的時區。 
 在/etc/localtime指到新的時區之後,先將TZ環境變數設為NULL。讓 localtime() 先讀取一次時間,此時會因為TZ設定
NULL而使得 localtime() 以UTC時區讀取時間,接著再一次將TZ環境變數指回 /etc/localtime,再一次使用 localtime() 涵數便可以得到正確的時間。範例如下:
    時間12:00
    環境變數:
    TZ=”/etc/localtime”
    時區檔案:
    /etc/localtime -> /mnt/html/zoneinfo/GMT+08:00
    透過date指令得到的時間為12:00
    透過localtime()涵數讀取到的時間為12:00

    時間12:00
    環境變數將TZ改為NULL:
    TZ=”NULL”
    時區檔案:
    /etc/localtime -> /mnt/html/zoneinfo/GMT+08:00
    透過date指令得到的時間為4:00
    透過localtime()涵數讀取到的時間為4:00

    再一步將環境變數將TZ改回/etc/localtime:
TZ=”/etc/localtime”
    透過date指令得到的時間為12:00
    透過localtime()涵數讀取到的時間為12:00

備註:
發生環境:Ubuntu丶Fedora
gcc version: 4.1.3
glibc: 2.3.6


沒有留言:

張貼留言

dnf upgrade fails with Error: GPG check FAILED

 OS: Fedora 36 今天在做  dnf  更新的時候,突然有很多 package 都出現簽章問題無法更新。類似如下的錯誤訊息 .... is not signed. ...... is not signed. ......... is not...