#date-time #date #time-parser #time #dsl #language #parser

timelang

A DSL (Domain Specific Language) and grammar for parsing and rendering human-readable date/time and duration values

4个版本

0.1.3 2023年12月5日
0.1.2 2023年11月30日
0.1.1 2023年11月26日
0.1.0 2023年11月26日

Rust模式中排名第377

MIT许可证

75KB
1.5K SLoC

🕗 Timelang

Crates.io docs.rs Build Status MIT License

Timelang是一个简单的DSL(领域特定语言),用于表示人类可读的时间相关表达式,包括特定的日期时间、相对表达式(如“现在3小时后”)、时间范围和持续时间。

入门指南

要使用timelang,您应该查看[TimeExpression],它是AST的最高级入口点,或者一些更具体的类型,如[Duration]、[PointInTime]和[TimeRange]。

timelang中的所有节点都实现了[FromStr]以及syn::parse::Parse,后者用于内部解析逻辑。所有节点类型都使用标准[Display]实现作为将它们输出为字符串的首选方式。

请注意,目前timelang仅支持年、月、周、日、小时和分钟,但以后可能会添加秒等。对于timelang的许多常见用例,通常不需要比分钟分辨率更好的分辨率。

示例

以下都是timelang中有效的表达式示例

  • now
  • 明天
  • 下个星期二
  • 后天
  • 前天
  • 20/4/2021
  • 11:21 上午
  • 15/6/20223:58 下午
  • 2小时, 37分钟
  • 5, 2, 3周和11分钟
  • 7天前
  • 2年前和10现在起分钟
  • 5, 3, 6分钟后15/4/20259:27 上午
  • 2023年1月1日14:07到2023年1月15日
  • 2024年3月19日10:07 AM到2027年3月9日5:27 PM后的3个月2天
  • 2天和14后天后的天数和小时
  • 11前天前的天数
  • 5下个星期二后的天数

特定日期

use timelang::*;
assert_eq!(
    "20/4/2021".parse::<TimeExpression>().unwrap(),
    TimeExpression::Specific(PointInTime::Absolute(AbsoluteTime::Date(Date(
        Month::April,
        DayOfMonth(20),
        Year(2021)
    ))))
);

特定日期时间

use timelang::*;
assert_eq!(
    "15/6/2022 at 14:00".parse::<AbsoluteTime>().unwrap(),
    AbsoluteTime::DateTime(DateTime(
        Date(Month::June, DayOfMonth(15), Year(2022)),
        Time(Hour::Hour24(14), Minute(0))
    ))
);

时间范围

use timelang::*;
assert_eq!(
    "from 1/1/2023 to 15/1/2023"
        .parse::<TimeExpression>()
        .unwrap(),
    TimeExpression::Range(TimeRange(
        PointInTime::Absolute(AbsoluteTime::Date(Date(
            Month::January,
            DayOfMonth(1),
            Year(2023)
        ))),
        PointInTime::Absolute(AbsoluteTime::Date(Date(
            Month::January,
            DayOfMonth(15),
            Year(2023)
        )))
    ))
);

持续时间(多个单位用逗号分隔)

use timelang::*;
assert_eq!(
    "2 hours, 30 minutes".parse::<TimeExpression>().unwrap(),
    TimeExpression::Duration(Duration {
        hours: Number(2),
        minutes: Number(30),
        days: Number(0),
        weeks: Number(0),
        months: Number(0),
        years: Number(0)
    })
);

持续时间(多个单位用and

use timelang::*;
assert_eq!(
    "1 year and 6 months".parse::<TimeExpression>().unwrap(),
    TimeExpression::Duration(Duration {
        years: Number(1),
        months: Number(6),
        days: Number(0),
        weeks: Number(0),
        hours: Number(0),
        minutes: Number(0)
    })
);

相对时间(使用ago

use timelang::*;
assert_eq!(
    "3 days ago".parse::<TimeExpression>().unwrap(),
    TimeExpression::Specific(PointInTime::Relative(RelativeTime::Directional {
        duration: Duration {
            days: Number(3),
            minutes: Number(0),
            hours: Number(0),
            weeks: Number(0),
            months: Number(0),
            years: Number(0)
        },
        dir: TimeDirection::Ago
    }))
);

相对时间(使用from now

use timelang::*;
assert_eq!(
    "5 days, 10 hours, and 35 minutes from now"
        .parse::<TimeExpression>()
        .unwrap(),
    TimeExpression::Specific(PointInTime::Relative(RelativeTime::Directional {
        duration: Duration {
            minutes: Number(35),
            hours: Number(10),
            days: Number(5),
            weeks: Number(0),
            months: Number(0),
            years: Number(0)
        },
        dir: TimeDirection::FromNow
    }))
);

相对时间(after特定日期)

use timelang::*;
assert_eq!(
    "2 hours, 3 minutes after 10/10/2022"
        .parse::<TimeExpression>()
        .unwrap(),
    TimeExpression::Specific(PointInTime::Relative(RelativeTime::Directional {
        duration: Duration {
            hours: Number(2),
            minutes: Number(3),
            days: Number(0),
            weeks: Number(0),
            months: Number(0),
            years: Number(0)
        },
        dir: TimeDirection::AfterAbsolute(AbsoluteTime::Date(Date(
            Month::October,
            DayOfMonth(10),
            Year(2022)
        )))
    }))
);

相对时间(before特定日期时间)

use timelang::*;
assert_eq!(
    "1 day before 31/12/2023 at 11:13 PM"
        .parse::<TimeExpression>()
        .unwrap(),
    TimeExpression::Specific(PointInTime::Relative(RelativeTime::Directional {
        duration: Duration {
            days: Number(1),
            minutes: Number(0),
            hours: Number(0),
            weeks: Number(0),
            months: Number(0),
            years: Number(0)
        },
        dir: TimeDirection::BeforeAbsolute(AbsoluteTime::DateTime(DateTime(
            Date(Month::December, DayOfMonth(31), Year(2023)),
            Time(Hour::Hour12(11, AmPm::PM), Minute(13))
        )))
    }))
);

时间范围(带有特定日期时间)

use timelang::*;
assert_eq!(
    "from 1/1/2024 at 10:00 to 2/1/2024 at 15:30"
        .parse::<TimeExpression>()
        .unwrap(),
    TimeExpression::Range(TimeRange(
        PointInTime::Absolute(AbsoluteTime::DateTime(DateTime(
            Date(Month::January, DayOfMonth(1), Year(2024)),
            Time(Hour::Hour24(10), Minute(0))
        ))),
        PointInTime::Absolute(AbsoluteTime::DateTime(DateTime(
            Date(Month::January, DayOfMonth(2), Year(2024)),
            Time(Hour::Hour24(15), Minute(30))
        )))
    ))
);

相对时间

use timelang::*;
assert_eq!("now".parse::<RelativeTime>().unwrap(), RelativeTime::Named(NamedRelativeTime::Now));
assert_eq!(
    "tomorrow".parse::<RelativeTime>().unwrap(),
    RelativeTime::Named(NamedRelativeTime::Tomorrow)
);
assert_eq!(
    "yesterday".parse::<RelativeTime>().unwrap(),
    RelativeTime::Named(NamedRelativeTime::Yesterday)
);
assert_eq!(
    "day before yesterday".parse::<RelativeTime>().unwrap(),
    RelativeTime::Named(NamedRelativeTime::DayBeforeYesterday)
);
// note the optional `the`
assert_eq!(
    "the day after tomorrow".parse::<RelativeTime>().unwrap(),
    RelativeTime::Named(NamedRelativeTime::DayAfterTomorrow)
);
assert_eq!(
    "next tuesday".parse::<RelativeTime>().unwrap(),
    RelativeTime::Next(RelativeTimeUnit::Tuesday)
);
assert_eq!(
    "last wednesday".parse::<RelativeTime>().unwrap(),
    RelativeTime::Last(RelativeTimeUnit::Wednesday)
);
assert_eq!(
    "3 days before yesterday".parse::<RelativeTime>().unwrap(),
    RelativeTime::Directional {
        duration: Duration {
            minutes: Number(0),
            hours: Number(0),
            days: Number(3),
            weeks: Number(0),
            months: Number(0),
            years: Number(0)
        },
        dir: TimeDirection::BeforeNamed(NamedRelativeTime::Yesterday)
    }
);
assert_eq!(
    "2 days and 14 hours after the day after tomorrow".parse::<RelativeTime>().unwrap(),
    RelativeTime::Directional {
        duration: Duration {
            minutes: Number(0),
            hours: Number(14),
            days: Number(2),
            weeks: Number(0),
            months: Number(0),
            years: Number(0)
        },
        dir: TimeDirection::AfterNamed(NamedRelativeTime::DayAfterTomorrow)
    }
);
assert_eq!(
    "2 weeks before last sunday".parse::<RelativeTime>().unwrap(),
    RelativeTime::Directional {
        duration: Duration {
            minutes: Number(0),
            hours: Number(0),
            days: Number(0),
            weeks: Number(2),
            months: Number(0),
            years: Number(0)
        },
        dir: TimeDirection::BeforeLast(RelativeTimeUnit::Sunday)
    }
);
assert_eq!(
    "3 years, 2 weeks after next thursday".parse::<RelativeTime>().unwrap(),
    RelativeTime::Directional {
        duration: Duration {
            minutes: Number(0),
            hours: Number(0),
            days: Number(0),
            weeks: Number(2),
            months: Number(0),
            years: Number(3)
        },
        dir: TimeDirection::AfterNext(RelativeTimeUnit::Thursday)
    }
);

备注

  • 目前使用syn进行解析。这可能会被替换为待定的解析crate,但使用syn可以快速启动。无论我们使用哪个新的crate,都希望它能让我们让timelang与no std兼容。
  • Timelang是明确的,这意味着所有可能的timelang句子都有且只有一个树表示。如果您能想出一个歧义句子,请通过提交GitHub问题告诉我们!

依赖项

~300–740KB
~18K SLoC