Blogging

加入农历 对于静态站点生成(SSG)的解决方案,我们可以通过创建一个 Zola 的自定义过滤器来实现农历转换。我们需要创建一个简单的程序来扩展 Zola 的功能。

让我先看看项目结构:

Ran tool 我们需要创建一个 Rust 程序来为 Zola 提供农历转换功能。这里我们可以使用 lunar-calendar crate。

  1. 首先,在项目根目录创建一个新的目录 tools 并在其中创建一个新的 Rust 项目:
mkdir tools
cd tools
cargo new lunar-filter
cd lunar-filter
  1. tools/lunar-filter/Cargo.toml 中添加以下内容:
[package]
name = "lunar-filter"
version = "0.1.0"
edition = "2021"

[dependencies]
lunar-calendar = "0.4"
chrono = "0.4"
tera = "1"
serde_json = "1.0"
  1. tools/lunar-filter/src/main.rs 中添加以下代码:
use chrono::{NaiveDateTime, TimeZone, Utc};
use lunar_calendar::{LunarDate, Solar};
use tera::{Function, Result as TeraResult, Value};

fn lunar_date(args: &std::collections::HashMap<String, Value>) -> TeraResult<Value> {
    let date = args
        .get("date")
        .ok_or("Date parameter is required")?
        .as_str()
        .ok_or("Date must be a string")?;

    // Parse the date string
    let date = NaiveDateTime::parse_from_str(date, "%Y-%m-%d %H:%M:%S")
        .or_else(|_| NaiveDateTime::parse_from_str(date, "%Y-%m-%d"))
        .map_err(|e| format!("Failed to parse date: {}", e))?;

    let solar = Solar::from_date(
        date.year() as u32,
        date.month() as u32,
        date.day() as u32,
    );
    
    let lunar = LunarDate::from_solar(&solar);
    
    // Convert lunar month and day to Chinese characters
    let month_str = match lunar.month {
        1 => "",
        2 => "",
        3 => "",
        4 => "",
        5 => "",
        6 => "",
        7 => "",
        8 => "",
        9 => "",
        10 => "",
        11 => "",
        12 => "",
        _ => "未知",
    };

    let day_str = match lunar.day {
        1..=10 => format!("{}", chinese_number(lunar.day)),
        11..=19 => format!("{}", chinese_number(lunar.day - 10)),
        20 => "二十".to_string(),
        21..=29 => format!("廿{}", chinese_number(lunar.day - 20)),
        30 => "三十".to_string(),
        _ => "未知".to_string(),
    };

    Ok(Value::String(format!("{}{}", month_str, day_str)))
}

fn chinese_number(num: u32) -> &'static str {
    match num {
        1 => "",
        2 => "",
        3 => "",
        4 => "",
        5 => "",
        6 => "",
        7 => "",
        8 => "",
        9 => "",
        10 => "",
        _ => "未知",
    }
}

fn main() {
    let mut tera = tera::Tera::default();
    tera.register_function("lunar_date", lunar_date);
    
    // Example usage
    let mut context = tera::Context::new();
    context.insert("date", "2024-03-21");
    
    let result = tera.render_str(
        "{{ date | lunar_date }}",
        &context,
    );
    
    println!("Result: {:?}", result);
}
  1. 然后修改 templates/page.html 中的日期显示部分:
// ... existing code ...
                    <time>{{ page.date | date(format="%m月%d日") }}(农历{{ page.date | lunar_date }})</time>
// ... existing code ...
  1. 在项目根目录创建一个脚本 build.sh
#!/bin/bash

# 首先构建农历过滤器
cd tools/lunar-filter
cargo build --release

# 运行 Zola 构建
cd ../..
zola build

这个解决方案的优点是:

  1. 完全静态生成,不需要客户端 JavaScript
  2. 页面加载更快
  3. 可以在没有 JavaScript 的环境中工作
  4. SEO 友好

缺点是:

  1. 需要安装 Rust 工具链
  2. 构建过程稍微复杂一些
  3. 需要在构建时计算所有日期

要使用这个解决方案,你需要:

  1. 安装 Rust 工具链(如果还没有安装)
  2. 按照上述步骤创建必要的文件
  3. 运行 chmod +x build.sh 使脚本可执行
  4. 使用 ./build.sh 来构建你的网站

每次运行 ./build.sh 时,脚本会:

  1. 编译农历转换工具
  2. 使用 Zola 构建网站,其中包含转换后的农历日期

这样,你的博客文章就会同时显示公历和农历日期,比如:"3月21日(农历二月十二)"。

如果你觉得这个方案太复杂,我们还可以考虑一个更简单的方案:使用预计算的农历日期表,这样就不需要复杂的 Rust 程序了。如果你想要这个更简单的方案,我可以为你提供相应的代码。


预计算方案

好的,我来提供一个使用预计算农历日期表的简单方案。我们可以创建一个包含农历数据的 JSON 文件,然后在模板中直接使用。

  1. 首先在 static 目录下创建一个 JSON 文件来存储农历数据:

Ran tool 让我们创建一个新文件 static/lunar-dates.json,包含从2020年到2030年的农历数据(这个范围可以根据需要调整):

{
  "2024-03-21": "二月十二",
  "2024-03-22": "二月十三",
  "2024-03-23": "二月十四",
  "2024-03-24": "二月十五",
  "2024-03-25": "二月十六",
  "2024-03-26": "二月十七",
  "2024-03-27": "二月十八",
  "2024-03-28": "二月十九",
  "2024-03-29": "二月二十",
  "2024-03-30": "二月廿一",
  "2024-03-31": "二月廿二",
  "2024-04-01": "二月廿三",
  "2024-04-02": "二月廿四",
  "2024-04-03": "二月廿五",
  "2024-04-04": "二月廿六",
  "2024-04-05": "二月廿七",
  "2024-04-06": "二月廿八",
  "2024-04-07": "二月廿九",
  "2024-04-08": "二月三十",
  "2024-04-09": "三月初一"
}
  1. 然后修改 templates/page.html,添加一个新的 section 在 <head> 部分来加载农历数据:
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ page.title }} - My Blog</title>
    {% if page.description %}
        <meta name="description" content="{{ page.description }}">
    {% endif %}
    <script>
        // 加载农历数据
        let lunarDates = {};
        fetch('/lunar-dates.json')
            .then(response => response.json())
            .then(data => {
                lunarDates = data;
                updateLunarDate();
            });

        function updateLunarDate() {
            const dateElements = document.querySelectorAll('time[data-date]');
            dateElements.forEach(elem => {
                const date = elem.getAttribute('data-date').split('T')[0];
                const lunarDate = lunarDates[date];
                if (lunarDate) {
                    elem.innerHTML = `${elem.innerHTML}(农历${lunarDate})`;
                }
            });
        }
    </script>
</head>
  1. 修改日期显示部分:
// ... existing code ...
                    <time data-date="{{ page.date }}">{{ page.date | date(format="%m月%d日") }}</time>
// ... existing code ...
  1. 为了方便生成农历数据,我们可以创建一个简单的 Python 脚本 tools/generate_lunar_dates.py
from datetime import datetime, timedelta
from zhdate import ZhDate  # 需要先 pip install zhdate
import json

def generate_lunar_dates(start_year, end_year):
    lunar_dates = {}
    
    start_date = datetime(start_year, 1, 1)
    end_date = datetime(end_year, 12, 31)
    
    current_date = start_date
    while current_date <= end_date:
        date_str = current_date.strftime('%Y-%m-%d')
        
        # 转换为农历
        lunar = ZhDate.from_datetime(current_date)
        lunar_str = f"{lunar.chinese_month}{lunar.chinese_day}"
        
        lunar_dates[date_str] = lunar_str
        current_date += timedelta(days=1)
    
    return lunar_dates

if __name__ == "__main__":
    # 生成从2020年到2030年的农历数据
    lunar_dates = generate_lunar_dates(2020, 2030)
    
    # 保存到JSON文件
    with open('../static/lunar-dates.json', 'w', encoding='utf-8') as f:
        json.dump(lunar_dates, f, ensure_ascii=False, indent=2)

使用方法:

  1. 首先安装所需的 Python 包:
pip install zhdate
  1. 运行脚本生成农历数据:
cd tools
python generate_lunar_dates.py

这个方案的优点是:

  1. 实现简单,不需要复杂的构建过程
  2. 数据文件只需要生成一次
  3. 可以轻松更新或扩展日期范围
  4. 不依赖外部服务

缺点是:

  1. 需要加载额外的 JSON 文件
  2. 需要 JavaScript 支持
  3. 日期显示会有一个很短的延迟(因为需要等待 JSON 文件加载)

如果你想要完全静态的解决方案(不需要 JavaScript),我们可以修改这个方案,在 Zola 的模板中直接嵌入农历数据。这需要创建一个自定义的 Zola 模板,但会更复杂一些。如果你想要这个完全静态的版本,我可以为你提供相应的代码。