From 15026228e85fe7502ddb37170ce36a476562612f Mon Sep 17 00:00:00 2001 From: Mirage Lyu Date: Thu, 12 Sep 2024 16:41:06 +0800 Subject: [PATCH] Support ohos tzdata since oh35 --- src/offset/local/tz_info/parser.rs | 15 ++++++ src/offset/local/tz_info/timezone.rs | 72 ++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/src/offset/local/tz_info/parser.rs b/src/offset/local/tz_info/parser.rs index 47cc0377e4..cfd7cf44ed 100644 --- a/src/offset/local/tz_info/parser.rs +++ b/src/offset/local/tz_info/parser.rs @@ -247,6 +247,21 @@ impl<'a> Cursor<'a> { Ok(u32::from_be_bytes(buf)) } + #[cfg(target_env = "ohos")] + pub(crate) fn seek_after(&mut self, offset: usize) -> Result { + if offset < self.read_count { + return Err(io::Error::from(ErrorKind::UnexpectedEof)); + } + match self.remaining.get((offset - self.read_count)..) { + Some(remaining) => { + self.remaining = remaining; + self.read_count = offset; + Ok(offset) + }, + _ => Err(io::Error::from(ErrorKind::UnexpectedEof)), + } + } + /// Read exactly `count` bytes, reducing remaining data and incrementing read count pub(crate) fn read_exact(&mut self, count: usize) -> Result<&'a [u8], io::Error> { match (self.remaining.get(..count), self.remaining.get(count..)) { diff --git a/src/offset/local/tz_info/timezone.rs b/src/offset/local/tz_info/timezone.rs index 1b089902ed..d209d59315 100644 --- a/src/offset/local/tz_info/timezone.rs +++ b/src/offset/local/tz_info/timezone.rs @@ -8,6 +8,9 @@ use std::{cmp::Ordering, fmt, str}; use super::rule::{AlternateTime, TransitionRule}; use super::{parser, Error, DAYS_PER_WEEK, SECONDS_PER_DAY}; +#[cfg(target_env = "ohos")] +use crate::offset::local::tz_info::parser::Cursor; + /// Time zone #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) struct TimeZone { @@ -50,6 +53,14 @@ impl TimeZone { } } + // ohos merge all file into tzdata since ver35 + #[cfg(target_env = "ohos")] + { + if let Ok(bytes) = find_ohos_tz_data(tz_string) { + return Self::from_tz_data(&bytes); + } + } + let mut chars = tz_string.chars(); if chars.next() == Some(':') { return Self::from_file(&mut find_tz_file(chars.as_str())?); @@ -629,6 +640,67 @@ fn find_tz_file(path: impl AsRef) -> Result { } } +#[cfg(target_env = "ohos")] +const TZDATA_PATH: &str = "/system/etc/zoneinfo/tzdata"; + +#[cfg(target_env = "ohos")] +fn from_tzdata_bytes(bytes: &mut Vec, tz_string: &str) -> Result, Error> { + const VERSION_SIZE: usize = 12; + const OFFSET_SIZE: usize = 4; + const INDEX_CHUNK_SIZE: usize = 48; + const ZONENAME_SIZE: usize = 40; + + let mut cursor = Cursor::new(&bytes); + // version head + let _ = cursor.read_exact(VERSION_SIZE)?; + let index_offset_offset = cursor.read_be_u32()?; + let data_offset_offset = cursor.read_be_u32()?; + // final offset + let _ = cursor.read_be_u32()?; + + if cursor.seek_after(index_offset_offset as usize).is_err() { + return Err(Error::Io(io::ErrorKind::NotFound.into())); + } + let mut idx = index_offset_offset; + while idx < data_offset_offset { + let index_buf = cursor.read_exact(ZONENAME_SIZE)?; + let offset = cursor.read_be_u32()?; + let length = cursor.read_be_u32()?; + let zone_name = str::from_utf8(index_buf)?.trim_end_matches('\0'); + if zone_name == tz_string { + if cursor.seek_after((data_offset_offset + offset) as usize).is_err() { + return Err(Error::Io(io::ErrorKind::NotFound.into())); + } + return match cursor.read_exact(length as usize) { + Ok(result) => { + Ok(result.to_vec()) + }, + _ => { + Err(Error::Io(io::ErrorKind::NotFound.into())) + } + } + } + idx += INDEX_CHUNK_SIZE as u32; + } + + Err(Error::Io(io::ErrorKind::NotFound.into())) +} + +#[cfg(target_env = "ohos")] +fn from_tzdata_file(file: &mut File, tz_string: &str) -> Result, Error> { + let mut bytes: Vec = Vec::new(); + file.read_to_end(&mut bytes)?; + from_tzdata_bytes(&mut bytes, tz_string) +} + +#[cfg(target_env = "ohos")] +fn find_ohos_tz_data(tz_string: &str) -> Result, Error> { + if let Ok(mut file) = File::open(TZDATA_PATH) { + return from_tzdata_file(&mut file, tz_string); + } + Err(Error::Io(io::ErrorKind::NotFound.into())) +} + // Possible system timezone directories #[cfg(unix)] const ZONE_INFO_DIRECTORIES: [&str; 4] =