Compare commits
2 Commits
6f20341c85
...
e6447fdd43
Author | SHA1 | Date | |
---|---|---|---|
|
e6447fdd43 | ||
|
3f3191fcea |
|
@ -13,6 +13,7 @@ aes = "0.8.3"
|
|||
base64 = "0.21.2"
|
||||
blockhash = "0.5.0"
|
||||
cbc = { version = "0.1.2", features = ["std"] }
|
||||
chrono = "0.4.26"
|
||||
cipher = "0.4.4"
|
||||
des = "0.8.1"
|
||||
hex = "0.4.3"
|
||||
|
@ -20,6 +21,7 @@ hmac-sha256 = "1.1.7"
|
|||
hmac-sha512 = "1.1.5"
|
||||
image = "0.24.6"
|
||||
md-5 = "0.10.5"
|
||||
once_cell = "1.18.0"
|
||||
rand = "0.8.5"
|
||||
rsa = { version = "0.9.2", features = ["sha2"] }
|
||||
sha1 = "0.10.5"
|
||||
|
|
|
@ -26,7 +26,7 @@ Rust 中可以使用的常用辅助功能工具箱。主要配备以下功能:
|
|||
- [x] MD5 散列算法
|
||||
- [x] 图像感知散列算法
|
||||
- 唯一序列号生成器
|
||||
- [ ] 改进版雪花 ID 生成器(短主机精简日期版)
|
||||
- [x] 冰雹 ID 生成器(短主机精简日期版雪花 ID)
|
||||
- [x] UUID 生成器
|
||||
- [x] short UUID 生成器
|
||||
- 签名算法
|
||||
|
@ -36,5 +36,7 @@ Rust 中可以使用的常用辅助功能工具箱。主要配备以下功能:
|
|||
- 序列化算法
|
||||
- [x] Base64 算法
|
||||
- [x] Hex 直转
|
||||
- 常用工具函数
|
||||
- [x] 日期时间函数
|
||||
|
||||
本工具箱仅可支持于 Rust 程序中使用,可以编译为`rlib`或者`dylib`。
|
||||
|
|
|
@ -5,4 +5,5 @@ pub mod hash;
|
|||
pub mod serial_code;
|
||||
pub mod serialize;
|
||||
pub mod signature;
|
||||
pub mod time;
|
||||
pub mod verifiy_code;
|
||||
|
|
121
src/serial_code/hail.rs
Normal file
121
src/serial_code/hail.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
use core::time;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use chrono::NaiveDateTime;
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use thiserror::Error;
|
||||
|
||||
const HAIL_PERIOD_START: Lazy<i64> = Lazy::new(|| {
|
||||
crate::time::date(2022, 2, 22)
|
||||
.map(|d| d.and_hms_opt(22, 22, 22))
|
||||
.flatten()
|
||||
.map(|dt| crate::time::attach_asia_shanghai(dt))
|
||||
.map(|dt| dt.timestamp())
|
||||
.unwrap_or_else(|| NaiveDateTime::MIN.timestamp())
|
||||
});
|
||||
type TimestampValidator = fn(i64) -> bool;
|
||||
type TimestampGenerator = fn() -> i64;
|
||||
|
||||
static INSTANCE: OnceCell<HailSerialCodeAlgorithm> = OnceCell::new();
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum HailSerialCodeAlgorithmError {
|
||||
#[error("Algorithm is already initialized")]
|
||||
AlgorithmAlreadyInitialized,
|
||||
}
|
||||
|
||||
/// 冰雹序列ID算法。
|
||||
/// 缩减了时间戳的位数,相比雪花算法可以额外支持近40年。
|
||||
pub struct HailSerialCodeAlgorithm {
|
||||
validator: Option<TimestampValidator>,
|
||||
generator: Option<TimestampGenerator>,
|
||||
host_id: i64,
|
||||
last_timestamp: Arc<Mutex<i64>>,
|
||||
counter: Arc<Mutex<i64>>,
|
||||
}
|
||||
|
||||
impl HailSerialCodeAlgorithm {
|
||||
/// 获取一个算法实例用于获取序列ID。
|
||||
pub fn get() -> &'static Self {
|
||||
INSTANCE.get().unwrap()
|
||||
}
|
||||
|
||||
/// 初始化整个序列ID算法。
|
||||
/// ! 注意,如果选择使用内置的主机独立时间戳生成器和验证器,那么将不能保证多主机状态下的序列ID一致性。可能会存在个别主机时间回拨现象。
|
||||
///
|
||||
/// - `host_id`:主机ID,取值范围为0~65535。
|
||||
/// - `timestamp_generator`:时间戳生成器,用于生成时间戳。如果不提供,则使用算法内置的主机独立时间戳生成器。
|
||||
/// - `timestamp_validatoe`:时间戳验证器,用于验证时间戳是否有效。如果不提供,则使用算法内置的主机独立时间戳验证器。
|
||||
pub fn initialize_algorithm(
|
||||
host_id: i64,
|
||||
timestamp_generator: Option<TimestampGenerator>,
|
||||
timestamp_validatoe: Option<TimestampValidator>,
|
||||
) -> Result<(), HailSerialCodeAlgorithmError> {
|
||||
let algorithm = HailSerialCodeAlgorithm {
|
||||
validator: timestamp_validatoe,
|
||||
generator: timestamp_generator,
|
||||
host_id,
|
||||
last_timestamp: Arc::new(Mutex::new(0)),
|
||||
counter: Arc::new(Mutex::new(0)),
|
||||
};
|
||||
INSTANCE
|
||||
.set(algorithm)
|
||||
.map_err(|_| HailSerialCodeAlgorithmError::AlgorithmAlreadyInitialized)
|
||||
}
|
||||
|
||||
/// 生成一个自计时起点以来的时间戳。
|
||||
fn generate_timestamp(&self) -> i64 {
|
||||
let current_time = crate::time::now_asia_shanghai().timestamp();
|
||||
current_time - *HAIL_PERIOD_START
|
||||
}
|
||||
|
||||
/// 判断指定时间戳是否比已经存储的最后一次使用的时间戳要大。否则时间发生了回拨。
|
||||
fn validate_timestamp(&self, timestamp: i64) -> bool {
|
||||
let last_timestamp = self.last_timestamp.clone();
|
||||
let last_timestamp = last_timestamp.lock().unwrap();
|
||||
timestamp >= *last_timestamp
|
||||
}
|
||||
|
||||
/// 生成一个64位长整型序列ID。
|
||||
pub fn generate_serial(&self) -> i64 {
|
||||
let last_timestamp = self.last_timestamp.clone();
|
||||
let mut last_timestamp = last_timestamp.lock().unwrap();
|
||||
let counter = self.counter.clone();
|
||||
let mut counter = counter.lock().unwrap();
|
||||
loop {
|
||||
let timestamp = if let Some(generator) = self.generator {
|
||||
generator()
|
||||
} else {
|
||||
self.generate_timestamp()
|
||||
};
|
||||
if let Some(validator) = self.validator {
|
||||
if !validator(timestamp) {
|
||||
std::thread::sleep(time::Duration::from_secs(1));
|
||||
continue;
|
||||
}
|
||||
} else if !self.validate_timestamp(timestamp) {
|
||||
std::thread::sleep(time::Duration::from_secs(1));
|
||||
continue;
|
||||
}
|
||||
if *last_timestamp < timestamp {
|
||||
// 对齐时间戳并重置序列计数器
|
||||
*last_timestamp = timestamp;
|
||||
*counter = 0;
|
||||
}
|
||||
*counter += 1;
|
||||
return (timestamp << 20) | ((self.host_id & 0xFFFF) << 16) | (*counter & 0xFFFF_FFFF);
|
||||
}
|
||||
}
|
||||
|
||||
/// 生成一个17位长前补零的序列ID字符串。
|
||||
pub fn generate_string_serial(&self) -> String {
|
||||
let serial = self.generate_serial();
|
||||
format!("{:017}", serial)
|
||||
}
|
||||
|
||||
/// 生成一个带字符串前缀17位长前补零的序列ID字符串。
|
||||
pub fn generate_prefixed_string_serial(&self, prefix: &str) -> String {
|
||||
let serial = self.generate_serial();
|
||||
format!("{}{:017}", prefix, serial)
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
pub mod hail;
|
||||
|
||||
pub mod uuid {
|
||||
pub fn new() -> Box<String> {
|
||||
Box::from(uuid::Uuid::new_v4().to_string())
|
||||
|
|
147
src/time/mod.rs
Normal file
147
src/time/mod.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
use chrono::{DateTime, Datelike, Duration, FixedOffset, NaiveDate, NaiveDateTime, Utc};
|
||||
|
||||
/// 获取一个类型为`chrono::DateTime<chrono::FixedOffset>`类型的当前日期时间的实例。时间时区将自动被设置为东八区。
|
||||
pub fn now_asia_shanghai() -> DateTime<FixedOffset> {
|
||||
let utc_now = Utc::now();
|
||||
shift_to_asia_shanghai(utc_now)
|
||||
}
|
||||
|
||||
/// 将一个类型为`chrono::DateTime<chrono::Utc>`类型的日期时间转换到指定时区的时间实例。
|
||||
pub fn shift_tz(datetime: DateTime<Utc>, zone: i64) -> DateTime<FixedOffset> {
|
||||
if zone.is_positive() {
|
||||
datetime.with_timezone(
|
||||
&FixedOffset::east_opt(Duration::hours(zone.abs()).num_seconds() as i32).unwrap(),
|
||||
)
|
||||
} else {
|
||||
datetime.with_timezone(
|
||||
&FixedOffset::west_opt(Duration::hours(zone.abs()).num_seconds() as i32).unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// 将一个类型为`chrono::DateTime<chrono::Utc>`类型的日期时间转换到东八区的时间实例。
|
||||
pub fn shift_to_asia_shanghai(datetime: DateTime<Utc>) -> DateTime<FixedOffset> {
|
||||
shift_tz(datetime, 8)
|
||||
}
|
||||
|
||||
/// 直接给一个原生日期时间附加东八区的时区信息。
|
||||
pub fn attach_asia_shanghai(datetime: NaiveDateTime) -> DateTime<FixedOffset> {
|
||||
DateTime::<FixedOffset>::from_local(
|
||||
datetime,
|
||||
FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
/// 从一个64位时间戳生成东八区的时间实例。这个函数主要用于处理使用`timestamp`方法直接返回的时间戳。
|
||||
///
|
||||
/// - `timestamp`:64位时间戳。
|
||||
pub fn from_utc_timestamp(timestamp: i64) -> DateTime<FixedOffset> {
|
||||
let request_time = NaiveDateTime::from_timestamp_micros(timestamp).unwrap();
|
||||
DateTime::<FixedOffset>::from_utc(
|
||||
request_time,
|
||||
FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
/// 根据指定的日期生成一个时间对象,如果给定的日期不合法将返回空白内容。
|
||||
///
|
||||
/// - `year`:日期的年份。
|
||||
/// - `month`:日期的月份,从`1`开始。
|
||||
/// - `day`:日期的天数。
|
||||
pub fn date(year: i32, month: u32, day: u32) -> Option<NaiveDate> {
|
||||
NaiveDate::from_ymd_opt(year, month, day)
|
||||
}
|
||||
|
||||
/// 根据指定日期生成一个指定日期最开始时间的时间,精度为毫秒。
|
||||
///
|
||||
/// - `year`:指定日期的年份。
|
||||
/// - `month`:指定日期的月份,从`1`开始。
|
||||
/// - `day`:指定日期的天数。
|
||||
pub fn date_beginning(year: i32, month: u32, day: u32) -> Option<DateTime<FixedOffset>> {
|
||||
let timezone = FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap();
|
||||
NaiveDate::from_ymd_opt(year, month, day)
|
||||
.map(|d| d.and_hms_micro_opt(0, 0, 0, 0).unwrap())
|
||||
.map(|dt| DateTime::<FixedOffset>::from_local(dt, timezone))
|
||||
}
|
||||
|
||||
/// 根据给定的日期,返回其当天最开始的时间,精度为毫秒。
|
||||
///
|
||||
/// - `date`:给定的原始日期,注意:原始日期将被消耗掉。
|
||||
pub fn begin_of_date(date: NaiveDate) -> Option<DateTime<FixedOffset>> {
|
||||
let timezone = FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap();
|
||||
date.and_hms_micro_opt(0, 0, 0, 0)
|
||||
.map(|dt| DateTime::<FixedOffset>::from_local(dt, timezone))
|
||||
}
|
||||
|
||||
/// 根据给定的日期,返回其当天即将结束的时间,精度为毫秒。
|
||||
///
|
||||
/// - `date`:给定的原始日期,注意:原始日期将被消耗掉。
|
||||
pub fn end_of_date(date: NaiveDate) -> Option<DateTime<FixedOffset>> {
|
||||
let timezone = FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap();
|
||||
date.and_hms_micro_opt(23, 59, 59, 999_999)
|
||||
.map(|dt| DateTime::<FixedOffset>::from_local(dt, timezone))
|
||||
}
|
||||
|
||||
/// 根据指定日期生成一个指定日期结束时间的时间,精度为毫秒。
|
||||
///
|
||||
/// - `year`:指定日期的年份。
|
||||
/// - `month`:指定日期的月份,从`1`开始。
|
||||
/// - `day`:指定日期的天数。
|
||||
pub fn date_ending(year: i32, month: u32, day: u32) -> Option<DateTime<FixedOffset>> {
|
||||
let timezone = FixedOffset::east_opt(Duration::hours(8).num_seconds() as i32).unwrap();
|
||||
NaiveDate::from_ymd_opt(year, month, day)
|
||||
.map(|d| d.and_hms_micro_opt(23, 59, 59, 999_999).unwrap())
|
||||
.map(|dt| DateTime::<FixedOffset>::from_local(dt, timezone))
|
||||
}
|
||||
|
||||
/// 返回两个日期之间的月份差值。
|
||||
///
|
||||
/// - `control`:基准月份。
|
||||
/// - `test`:测试月份。
|
||||
pub fn difference_month(control: NaiveDate, test: NaiveDate) -> i32 {
|
||||
let difference_year = test.year() - control.year();
|
||||
let difference_month = (test.month() - control.month()) as i32;
|
||||
difference_year * 12 + difference_month
|
||||
}
|
||||
|
||||
/// 测试指定月份是否是基准月份的前一个月份。
|
||||
///
|
||||
/// - `control`:基准月份。
|
||||
/// - `test`:待测试的指定月份。
|
||||
pub fn is_previous_month(control: NaiveDate, test: NaiveDate) -> bool {
|
||||
difference_month(control, test) == 1
|
||||
}
|
||||
|
||||
/// 测试指定月份是否是基准月份的下一个月份。
|
||||
///
|
||||
/// - `control`:基准月份。
|
||||
/// - `test`:待测试的指定月份。
|
||||
pub fn is_next_month(control: NaiveDate, test: NaiveDate) -> bool {
|
||||
difference_month(control, test) == -1
|
||||
}
|
||||
|
||||
/// 生成符合Postgresql中日期类型最小值的日期。
|
||||
pub fn min_date() -> NaiveDate {
|
||||
NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()
|
||||
}
|
||||
|
||||
/// 生成符合Postgresql中日期类型最小值的日期时间。
|
||||
pub fn min_datetime() -> DateTime<FixedOffset> {
|
||||
NaiveDate::from_ymd_opt(1970, 1, 1)
|
||||
.map(begin_of_date)
|
||||
.flatten()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// 生成符合Postgresql中日期类型最大值的日期。
|
||||
pub fn max_date() -> NaiveDate {
|
||||
NaiveDate::from_ymd_opt(2099, 12, 31).unwrap()
|
||||
}
|
||||
|
||||
/// 生成符合Postgresql中日期类型最大值的日期时间。
|
||||
pub fn max_datetime() -> DateTime<FixedOffset> {
|
||||
NaiveDate::from_ymd_opt(2099, 12, 31)
|
||||
.map(end_of_date)
|
||||
.flatten()
|
||||
.unwrap()
|
||||
}
|
Loading…
Reference in New Issue
Block a user