chronotope / chrono

Date and time library for Rust
Other
3.34k stars 533 forks source link

Local time unsupport openwrt #1129

Open cppcoffee opened 1 year ago

cppcoffee commented 1 year ago

Rust example code:

use chrono::{Utc, Local};

fn main() {
    println!("{}", Local::now());
    println!("{}", Utc::now());
}

On ubuntu output:

2023-06-06 11:37:45.784123237 +08:00
2023-06-06 03:37:45.784374037 UTC

On openwrt output:

2023-06-06 03:39:08.963993795 +00:00
2023-06-06 03:39:08.964326294 UTC

Openwrt timezone config:

$ cat /etc/config/system
config system
        option timezone 'CST-8'
        option zonename 'Asia/Shanghai'
...

Expected openwrt output +08:00

Is it possible that chrono::Local does not support openwrt systems? It prints out as utc。

djc commented 1 year ago

Yes -- there are two parts to this that are platform specific: (1) how to find out which timezone has been configured, (2) the timezone offsets to use for that timezone. (1) seems to be the issue here -- chrono relies on the iana-time-zone crate for this, please file an issue there.

pitdicker commented 1 year ago

@cppcoffee Is there a not-too-difficult way for me to test chrono on openwrt?

cppcoffee commented 1 year ago

Hi @pitdicker , OpenWrt has virtualization image that you can try.

https://openwrt.org/docs/guide-user/virtualization/start

pitdicker commented 1 year ago

Okay, I can reproduce it.

docker import http://downloads.openwrt.org/attitude_adjustment/12.09/x86/generic/openwrt-x86-generic-rootfs.tar.gz openwrt-x86-generic-rootfs
rustup target add x86_64-unknown-linux-musl
docker run -v $(pwd)/target/x86_64-unknown-linux-musl:/target -i openwrt-x86-generic-rootfs /target/debug/deps/chrono-f094327353343c38

Our test suite fails with:

failures:

---- offset::local::tz_info::timezone::tests::test_time_zone_from_posix_tz stdout ----
Error: ParseInt(ParseIntError { kind: Empty })

failures:
    offset::local::tz_info::timezone::tests::test_time_zone_from_posix_tz
pitdicker commented 1 year ago

The docker image has no timezone files and /etc/TZ is a dangling pointer. Maybe timezone support has to be installed first?

cppcoffee commented 1 year ago

On my openwrt environment, the /etc/TZ file content as follows:

# cat /etc/TZ
CST-8
cppcoffee commented 1 year ago

OpenWrt commands for setting the time:

$ uci get system.@system[0].timezone
$ uci set system.@system[0].timezone='Asia/Shanghai'
$ uci commit system
$ /etc/init.d/system reload
$ date
cppcoffee commented 1 year ago

The iana-time-zone crate support OpenWrt now:

https://github.com/strawlab/iana-time-zone/pull/109

Use chrono + iana-time-zone new version, chrono::Local prints out UTC time.

https://github.com/strawlab/iana-time-zone/pull/109#issuecomment-1579975176

djc commented 1 year ago

I suppose that would be because the time zone info files aren't found in the places where they are stored on conventional Linux systems.

cppcoffee commented 1 year ago

Where chrono to find the time zone info files?

djc commented 1 year ago

https://github.com/chronotope/chrono/blob/main/src/offset/local/unix.rs#L78

cppcoffee commented 1 year ago

On OpenWrt print chrono::Local::now(), without TZ env, output UTC time.

$ ./chrono-demo-uclibc
2023-06-17T03:44:47.615484252+00:00

If use TZ=CST-8 env, output local time.

$ cat /etc/TZ
CST-8
$ TZ=CST-8 ./chrono-demo-uclibc
2023-06-17T11:44:12.281978327+08:00
cppcoffee commented 1 year ago

Maybe I should try installing the timezone package.

https://openwrt.org/packages/pkgdata_owrt18_6/zoneinfo-asia

cppcoffee commented 1 year ago

In my scenario, it was solved by manually setting the TZ environment:

fn main() {
    let tz = std::fs::read_to_string("/etc/TZ").unwrap_or("CST-8".to_owned());
    std::env::set_var("TZ", tz);

    // do something
}
ArisAachen commented 1 year ago

I got the same issue, and solve by set TZ env I hve tried golang for test, golang seems get right output time

pitdicker commented 1 year ago

To solve this we need to know what OpenWRT does that is different from regular Linux distributions. Can someone provide the following information?

cppcoffee commented 1 year ago

The OpenWrt os-release content:

root@OpenWrt:/# cat /etc/os-release
NAME="OpenWrt"
VERSION="22.03.4"
ID="openwrt"
ID_LIKE="lede openwrt"
PRETTY_NAME="OpenWrt 22.03.4"
VERSION_ID="22.03.4"
HOME_URL="https://openwrt.org/"
BUG_URL="https://bugs.openwrt.org/"
SUPPORT_URL="https://forum.openwrt.org/"
BUILD_ID="20231016-1458-g9191cfc-dirty"
OPENWRT_BOARD="ramips/mt7621"
OPENWRT_ARCH="mipsel_24kc"
OPENWRT_TAINTS="no-all busybox"
OPENWRT_DEVICE_MANUFACTURER="OpenWrt"
OPENWRT_DEVICE_MANUFACTURER_URL="https://openwrt.org/"
OPENWRT_DEVICE_PRODUCT="Generic"
OPENWRT_DEVICE_REVISION="v0"
OPENWRT_RELEASE="OpenWrt 22.03.4 20231016-1458-g9191cfc-dirty"

The OpenWrt TZ file location /tmp/TZ:

root@OpenWrt:/# ll /tmp/TZ
-rw-r--r--    1 root     root             4 Apr  9 12:30 /tmp/TZ

root@OpenWrt:/# cat /tmp/TZ
UTC

The /tmp/TZ build on:

/etc/init.d/system:24:  echo "$timezone" > /tmp/TZ

The /etc/init.d/system content:

root@OpenWrt:/# cat /etc/init.d/system
#!/bin/sh /etc/rc.common
# Copyright (C) 2014 OpenWrt.org

START=10
USE_PROCD=1

validate_system_section() {
        uci_load_validate system system "$1" "$2" \
                'hostname:string:OpenWrt' \
                'conloglevel:uinteger' \
                'buffersize:uinteger' \
                'timezone:string:UTC' \  # load TZ variable to timezone
                'zonename:string'
}

system_config() {
        [ "$2" = 0 ] || {
                echo "validation failed"
                return 1
        }

        echo "$hostname" > /proc/sys/kernel/hostname
        [ -z "$conloglevel" -a -z "$buffersize" ] || dmesg ${conloglevel:+-n $conloglevel} ${buffersize:+-s $buffersize}
        echo "$timezone" > /tmp/TZ
        [ -n "$zonename" ] && [ -f "/usr/share/zoneinfo/${zonename// /_}" ] \
                && ln -sf "/usr/share/zoneinfo/${zonename// /_}" /tmp/localtime \
                && rm -f /tmp/TZ

        # apply timezone to kernel
        hwclock -u --systz
}

reload_service() {
        config_load system
        config_foreach validate_system_section system system_config
}

service_triggers() {
        procd_add_reload_trigger "system"
        procd_add_validation validate_system_section
}

start_service() {
        reload_service
}

system config:

root@OpenWrt:/# cat /etc/config/system

config system
        option hostname 'OpenWrt'
        option timezone 'UTC'     # the origin timezone
        option ttylogin '0'
        option log_size '64'
        option urandom_seed '0'
        option compat_version '1.1'

config timeserver 'ntp'
        option enabled '1'
        option enable_server '0'
        list server '0.openwrt.pool.ntp.org'
        list server '1.openwrt.pool.ntp.org'
        list server '2.openwrt.pool.ntp.org'
        list server '3.openwrt.pool.ntp.org'

System configuration /etc/config/system:

https://openwrt.org/docs/guide-user/base-system/system_configuration