Skip to content

Commit

Permalink
Support ohos tzdata since oh35
Browse files Browse the repository at this point in the history
  • Loading branch information
MirageLyu authored and djc committed Oct 11, 2024
1 parent 05a6ce6 commit d8a177e
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 0 deletions.
15 changes: 15 additions & 0 deletions src/offset/local/tz_info/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize, io::Error> {
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..)) {
Expand Down
61 changes: 61 additions & 0 deletions src/offset/local/tz_info/timezone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -50,6 +53,12 @@ impl TimeZone {
}
}

// ohos merge all file into tzdata since ver35
#[cfg(target_env = "ohos")]
{
return Self::from_tz_data(&find_ohos_tz_data(tz_string)?);
}

let mut chars = tz_string.chars();
if chars.next() == Some(':') {
return Self::from_file(&mut find_tz_file(chars.as_str())?);
Expand Down Expand Up @@ -629,6 +638,58 @@ fn find_tz_file(path: impl AsRef<Path>) -> Result<File, Error> {
}
}

#[cfg(target_env = "ohos")]
fn from_tzdata_bytes(bytes: &mut Vec<u8>, tz_string: &str) -> Result<Vec<u8>, 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()?;

cursor.seek_after(index_offset_offset as usize)?;
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 {
idx += INDEX_CHUNK_SIZE as u32;
continue;
}
cursor.seek_after((data_offset_offset + offset) as usize)?;
return match cursor.read_exact(length as usize) {
Ok(result) => Ok(result.to_vec()),
Err(_err) => Err(Error::InvalidTzFile("invalid ohos tzdata chunk")),
};
}

Err(Error::InvalidTzString("cannot find tz string within tzdata"))
}

#[cfg(target_env = "ohos")]
fn from_tzdata_file(file: &mut File, tz_string: &str) -> Result<Vec<u8>, Error> {
let mut bytes = 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<Vec<u8>, Error> {
const TZDATA_PATH: &str = "/system/etc/zoneinfo/tzdata";
match File::open(TZDATA_PATH) {
Ok(mut file) => from_tzdata_file(&mut file, tz_string),
Err(err) => Err(err.into()),
}
}

// Possible system timezone directories
#[cfg(unix)]
const ZONE_INFO_DIRECTORIES: [&str; 4] =
Expand Down

0 comments on commit d8a177e

Please sign in to comment.