mirror of
https://github.com/typst/typst
synced 2025-08-15 23:48:32 +08:00
MoveTimestamp
into metadata module
This commit is contained in:
parent
6c84f392b2
commit
c741b532d9
@ -12,12 +12,14 @@ mod shape;
|
||||
mod text;
|
||||
mod util;
|
||||
|
||||
pub use self::metadata::{Timestamp, Timezone};
|
||||
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use ecow::eco_format;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typst_library::diag::{bail, SourceResult, StrResult};
|
||||
use typst_library::foundations::{Datetime, Smart};
|
||||
use typst_library::foundations::Smart;
|
||||
use typst_library::layout::{PageRanges, PagedDocument};
|
||||
|
||||
/// Export a document into a PDF file.
|
||||
@ -186,86 +188,3 @@ pub enum PdfStandard {
|
||||
#[serde(rename = "a-4e")]
|
||||
A_4e,
|
||||
}
|
||||
|
||||
/// A timestamp with timezone information.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Timestamp {
|
||||
/// The datetime of the timestamp.
|
||||
pub(crate) datetime: Datetime,
|
||||
/// The timezone of the timestamp.
|
||||
pub(crate) timezone: Timezone,
|
||||
}
|
||||
|
||||
impl Timestamp {
|
||||
/// Create a new timestamp with a given datetime and UTC suffix.
|
||||
pub fn new_utc(datetime: Datetime) -> Self {
|
||||
Self { datetime, timezone: Timezone::UTC }
|
||||
}
|
||||
|
||||
/// Create a new timestamp with a given datetime, and a local timezone offset.
|
||||
pub fn new_local(datetime: Datetime, whole_minute_offset: i32) -> Option<Self> {
|
||||
let hour_offset = (whole_minute_offset / 60).try_into().ok()?;
|
||||
// Note: the `%` operator in Rust is the remainder operator, not the
|
||||
// modulo operator. The remainder operator can return negative results.
|
||||
// We can simply apply `abs` here because we assume the `minute_offset`
|
||||
// will have the same sign as `hour_offset`.
|
||||
let minute_offset = (whole_minute_offset % 60).abs().try_into().ok()?;
|
||||
match (hour_offset, minute_offset) {
|
||||
// Only accept valid timezone offsets with `-23 <= hours <= 23`,
|
||||
// and `0 <= minutes <= 59`.
|
||||
(-23..=23, 0..=59) => Some(Self {
|
||||
datetime,
|
||||
timezone: Timezone::Local { hour_offset, minute_offset },
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A timezone.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Timezone {
|
||||
/// The UTC timezone.
|
||||
UTC,
|
||||
/// The local timezone offset from UTC. And the `minute_offset` will have
|
||||
/// same sign as `hour_offset`.
|
||||
Local { hour_offset: i8, minute_offset: u8 },
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_timestamp_new_local() {
|
||||
let dummy_datetime = Datetime::from_ymd_hms(2024, 12, 17, 10, 10, 10).unwrap();
|
||||
let test = |whole_minute_offset, expect_timezone| {
|
||||
assert_eq!(
|
||||
Timestamp::new_local(dummy_datetime, whole_minute_offset)
|
||||
.unwrap()
|
||||
.timezone,
|
||||
expect_timezone
|
||||
);
|
||||
};
|
||||
|
||||
// Valid timezone offsets
|
||||
test(0, Timezone::Local { hour_offset: 0, minute_offset: 0 });
|
||||
test(480, Timezone::Local { hour_offset: 8, minute_offset: 0 });
|
||||
test(-480, Timezone::Local { hour_offset: -8, minute_offset: 0 });
|
||||
test(330, Timezone::Local { hour_offset: 5, minute_offset: 30 });
|
||||
test(-210, Timezone::Local { hour_offset: -3, minute_offset: 30 });
|
||||
test(-720, Timezone::Local { hour_offset: -12, minute_offset: 0 }); // AoE
|
||||
|
||||
// Corner cases
|
||||
test(315, Timezone::Local { hour_offset: 5, minute_offset: 15 });
|
||||
test(-225, Timezone::Local { hour_offset: -3, minute_offset: 45 });
|
||||
test(1439, Timezone::Local { hour_offset: 23, minute_offset: 59 });
|
||||
test(-1439, Timezone::Local { hour_offset: -23, minute_offset: 59 });
|
||||
|
||||
// Invalid timezone offsets
|
||||
assert!(Timestamp::new_local(dummy_datetime, 1440).is_none());
|
||||
assert!(Timestamp::new_local(dummy_datetime, -1440).is_none());
|
||||
assert!(Timestamp::new_local(dummy_datetime, i32::MAX).is_none());
|
||||
assert!(Timestamp::new_local(dummy_datetime, i32::MIN).is_none());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::convert::GlobalContext;
|
||||
use crate::Timezone;
|
||||
use ecow::EcoString;
|
||||
use krilla::metadata::{Metadata, TextDirection};
|
||||
use typst_library::foundations::{Datetime, Smart};
|
||||
@ -99,3 +98,86 @@ fn convert_date(
|
||||
|
||||
Some(kd)
|
||||
}
|
||||
|
||||
/// A timestamp with timezone information.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Timestamp {
|
||||
/// The datetime of the timestamp.
|
||||
pub(crate) datetime: Datetime,
|
||||
/// The timezone of the timestamp.
|
||||
pub(crate) timezone: Timezone,
|
||||
}
|
||||
|
||||
impl Timestamp {
|
||||
/// Create a new timestamp with a given datetime and UTC suffix.
|
||||
pub fn new_utc(datetime: Datetime) -> Self {
|
||||
Self { datetime, timezone: Timezone::UTC }
|
||||
}
|
||||
|
||||
/// Create a new timestamp with a given datetime, and a local timezone offset.
|
||||
pub fn new_local(datetime: Datetime, whole_minute_offset: i32) -> Option<Self> {
|
||||
let hour_offset = (whole_minute_offset / 60).try_into().ok()?;
|
||||
// Note: the `%` operator in Rust is the remainder operator, not the
|
||||
// modulo operator. The remainder operator can return negative results.
|
||||
// We can simply apply `abs` here because we assume the `minute_offset`
|
||||
// will have the same sign as `hour_offset`.
|
||||
let minute_offset = (whole_minute_offset % 60).abs().try_into().ok()?;
|
||||
match (hour_offset, minute_offset) {
|
||||
// Only accept valid timezone offsets with `-23 <= hours <= 23`,
|
||||
// and `0 <= minutes <= 59`.
|
||||
(-23..=23, 0..=59) => Some(Self {
|
||||
datetime,
|
||||
timezone: Timezone::Local { hour_offset, minute_offset },
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A timezone.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Timezone {
|
||||
/// The UTC timezone.
|
||||
UTC,
|
||||
/// The local timezone offset from UTC. And the `minute_offset` will have
|
||||
/// same sign as `hour_offset`.
|
||||
Local { hour_offset: i8, minute_offset: u8 },
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_timestamp_new_local() {
|
||||
let dummy_datetime = Datetime::from_ymd_hms(2024, 12, 17, 10, 10, 10).unwrap();
|
||||
let test = |whole_minute_offset, expect_timezone| {
|
||||
assert_eq!(
|
||||
Timestamp::new_local(dummy_datetime, whole_minute_offset)
|
||||
.unwrap()
|
||||
.timezone,
|
||||
expect_timezone
|
||||
);
|
||||
};
|
||||
|
||||
// Valid timezone offsets
|
||||
test(0, Timezone::Local { hour_offset: 0, minute_offset: 0 });
|
||||
test(480, Timezone::Local { hour_offset: 8, minute_offset: 0 });
|
||||
test(-480, Timezone::Local { hour_offset: -8, minute_offset: 0 });
|
||||
test(330, Timezone::Local { hour_offset: 5, minute_offset: 30 });
|
||||
test(-210, Timezone::Local { hour_offset: -3, minute_offset: 30 });
|
||||
test(-720, Timezone::Local { hour_offset: -12, minute_offset: 0 }); // AoE
|
||||
|
||||
// Corner cases
|
||||
test(315, Timezone::Local { hour_offset: 5, minute_offset: 15 });
|
||||
test(-225, Timezone::Local { hour_offset: -3, minute_offset: 45 });
|
||||
test(1439, Timezone::Local { hour_offset: 23, minute_offset: 59 });
|
||||
test(-1439, Timezone::Local { hour_offset: -23, minute_offset: 59 });
|
||||
|
||||
// Invalid timezone offsets
|
||||
assert!(Timestamp::new_local(dummy_datetime, 1440).is_none());
|
||||
assert!(Timestamp::new_local(dummy_datetime, -1440).is_none());
|
||||
assert!(Timestamp::new_local(dummy_datetime, i32::MAX).is_none());
|
||||
assert!(Timestamp::new_local(dummy_datetime, i32::MIN).is_none());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user