mkdocs-document-dates 0.4.0__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  """MkDocs Document Dates Plugin."""
2
2
 
3
- __version__ = '0.1.0'
3
+ __version__ = '1.0.0'
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env python3
2
+ import os
3
+ import sys
4
+ import json
5
+ import platform
6
+ import subprocess
7
+ import traceback
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+
11
+ def find_mkdocs_projects():
12
+ """查找当前 git 仓库中的所有 MkDocs 项目"""
13
+ try:
14
+ git_root = Path(subprocess.check_output(
15
+ ['git', 'rev-parse', '--show-toplevel'],
16
+ text=True
17
+ ).strip())
18
+
19
+ projects = []
20
+ for config_file in git_root.rglob('mkdocs.y*ml'):
21
+ if config_file.name in ('mkdocs.yml', 'mkdocs.yaml'):
22
+ projects.append(config_file.parent)
23
+ return projects
24
+ except subprocess.CalledProcessError as e:
25
+ print(f"Error finding git root: {e}")
26
+ return []
27
+
28
+ def get_file_dates(file_path):
29
+ """获取文件的创建和修改时间"""
30
+ try:
31
+ stat = os.stat(file_path)
32
+ modified = datetime.fromtimestamp(stat.st_mtime)
33
+
34
+ system = platform.system().lower()
35
+ if system == 'darwin': # macOS
36
+ try:
37
+ created = datetime.fromtimestamp(stat.st_birthtime)
38
+ except AttributeError:
39
+ created = datetime.fromtimestamp(stat.st_ctime)
40
+ elif system == 'windows': # Windows
41
+ created = datetime.fromtimestamp(stat.st_ctime)
42
+ else: # Linux 和其他系统
43
+ created = modified
44
+
45
+ return created.isoformat(), modified.isoformat()
46
+ except (OSError, ValueError):
47
+ current_time = datetime.now()
48
+ return current_time.isoformat(), current_time.isoformat()
49
+
50
+ def update_dates_cache():
51
+ """更新文档时间缓存"""
52
+ projects = find_mkdocs_projects()
53
+ if not projects:
54
+ print("No MkDocs projects found")
55
+ return
56
+
57
+ for project_dir in projects:
58
+ # print(f"Processing project: {project_dir}")
59
+ docs_dir = project_dir / 'docs'
60
+ if not docs_dir.exists():
61
+ print(f"No docs directory found in {project_dir}")
62
+ continue
63
+
64
+ # print(f"Processing docs in {docs_dir}")
65
+ cache_file = docs_dir / '.dates_cache.json'
66
+ dates_cache = _load_cache(cache_file)
67
+
68
+ _update_cache(docs_dir, dates_cache)
69
+ _save_cache(cache_file, dates_cache)
70
+ # print(f"Cache file updated: {cache_file}")
71
+
72
+ def _load_cache(cache_file):
73
+ """加载缓存文件"""
74
+ if cache_file.exists():
75
+ try:
76
+ with open(cache_file) as f:
77
+ return json.load(f)
78
+ except json.JSONDecodeError:
79
+ pass
80
+ return {}
81
+
82
+ def _update_cache(docs_dir, dates_cache):
83
+ """更新缓存内容"""
84
+ # 记录当前所有的 markdown 文件
85
+ current_files = set()
86
+
87
+ # 处理新增和更新的文件
88
+ for md_file in docs_dir.rglob("*.md"):
89
+ rel_path = str(md_file.relative_to(docs_dir))
90
+ current_files.add(rel_path)
91
+
92
+ # 文件不在缓存中(新增)或文件修改时间变化(更新)
93
+ if rel_path not in dates_cache or os.path.getmtime(md_file) != dates_cache[rel_path].get("mtime", 0):
94
+ created, modified = get_file_dates(md_file)
95
+ dates_cache[rel_path] = {
96
+ "created": created,
97
+ "modified": modified,
98
+ "mtime": os.path.getmtime(md_file)
99
+ }
100
+
101
+ # 处理删除的文件
102
+ for cached_path in list(dates_cache.keys()):
103
+ if cached_path not in current_files:
104
+ del dates_cache[cached_path]
105
+
106
+ def _save_cache(cache_file, dates_cache):
107
+ """保存缓存文件"""
108
+ with open(cache_file, "w") as f:
109
+ json.dump(dates_cache, f, indent=2, ensure_ascii=False)
110
+ subprocess.run(["git", "add", str(cache_file)], check=True)
111
+
112
+ # 添加主入口
113
+ if __name__ == "__main__":
114
+ try:
115
+ update_dates_cache()
116
+ except Exception as e:
117
+ traceback.print_exc()
118
+ sys.exit(1)
@@ -0,0 +1,23 @@
1
+ from pathlib import Path
2
+ import importlib.util
3
+
4
+ def load_translations():
5
+ """加载所有可用的翻译"""
6
+ translations = {}
7
+ translations_dir = Path(__file__).parent
8
+
9
+ for lang_file in translations_dir.glob('*.py'):
10
+ if lang_file.stem == '__init__':
11
+ continue
12
+
13
+ spec = importlib.util.spec_from_file_location(
14
+ f"translations.{lang_file.stem}",
15
+ lang_file
16
+ )
17
+ module = importlib.util.module_from_spec(spec)
18
+ spec.loader.exec_module(module)
19
+
20
+ if hasattr(module, 'translations'):
21
+ translations[lang_file.stem] = module.translations
22
+
23
+ return translations
@@ -0,0 +1,18 @@
1
+ translations = {
2
+ 'just_now': 'الآن',
3
+ 'seconds_ago': 'منذ {} ثانية',
4
+ 'minute_ago': 'منذ دقيقة',
5
+ 'minutes_ago': 'منذ {} دقيقة',
6
+ 'hour_ago': 'منذ ساعة',
7
+ 'hours_ago': 'منذ {} ساعة',
8
+ 'day_ago': 'منذ يوم',
9
+ 'days_ago': 'منذ {} يوم',
10
+ 'week_ago': 'منذ أسبوع',
11
+ 'weeks_ago': 'منذ {} أسبوع',
12
+ 'month_ago': 'منذ شهر',
13
+ 'months_ago': 'منذ {} شهر',
14
+ 'year_ago': 'منذ سنة',
15
+ 'years_ago': 'منذ {} سنة',
16
+ 'created_time': 'تاريخ الإنشاء',
17
+ 'modified_time': 'تاريخ التعديل',
18
+ }
@@ -0,0 +1,18 @@
1
+ translations = {
2
+ 'just_now': 'gerade jetzt',
3
+ 'seconds_ago': 'vor {} Sekunden',
4
+ 'minute_ago': 'vor einer Minute',
5
+ 'minutes_ago': 'vor {} Minuten',
6
+ 'hour_ago': 'vor einer Stunde',
7
+ 'hours_ago': 'vor {} Stunden',
8
+ 'day_ago': 'vor einem Tag',
9
+ 'days_ago': 'vor {} Tagen',
10
+ 'week_ago': 'vor einer Woche',
11
+ 'weeks_ago': 'vor {} Wochen',
12
+ 'month_ago': 'vor einem Monat',
13
+ 'months_ago': 'vor {} Monaten',
14
+ 'year_ago': 'vor einem Jahr',
15
+ 'years_ago': 'vor {} Jahren',
16
+ 'created_time': 'Erstellungszeit',
17
+ 'modified_time': 'Änderungszeit',
18
+ }
@@ -0,0 +1,18 @@
1
+ translations = {
2
+ 'just_now': 'just now',
3
+ 'seconds_ago': '{} seconds ago',
4
+ 'minute_ago': '1 minute ago',
5
+ 'minutes_ago': '{} minutes ago',
6
+ 'hour_ago': '1 hour ago',
7
+ 'hours_ago': '{} hours ago',
8
+ 'day_ago': '1 day ago',
9
+ 'days_ago': '{} days ago',
10
+ 'week_ago': '1 week ago',
11
+ 'weeks_ago': '{} weeks ago',
12
+ 'month_ago': '1 month ago',
13
+ 'months_ago': '{} months ago',
14
+ 'year_ago': '1 year ago',
15
+ 'years_ago': '{} years ago',
16
+ 'created_time': 'Created Time',
17
+ 'modified_time': 'Last Update',
18
+ }
@@ -0,0 +1,18 @@
1
+ translations = {
2
+ 'just_now': 'justo ahora',
3
+ 'seconds_ago': 'hace {} segundos',
4
+ 'minute_ago': 'hace un minuto',
5
+ 'minutes_ago': 'hace {} minutos',
6
+ 'hour_ago': 'hace una hora',
7
+ 'hours_ago': 'hace {} horas',
8
+ 'day_ago': 'hace un día',
9
+ 'days_ago': 'hace {} días',
10
+ 'week_ago': 'hace una semana',
11
+ 'weeks_ago': 'hace {} semanas',
12
+ 'month_ago': 'hace un mes',
13
+ 'months_ago': 'hace {} meses',
14
+ 'year_ago': 'hace un año',
15
+ 'years_ago': 'hace {} años',
16
+ 'created_time': 'Fecha de creación',
17
+ 'modified_time': 'Fecha de modificación',
18
+ }
@@ -0,0 +1,18 @@
1
+ translations = {
2
+ 'just_now': 'à l\'instant',
3
+ 'seconds_ago': 'il y a {} secondes',
4
+ 'minute_ago': 'il y a une minute',
5
+ 'minutes_ago': 'il y a {} minutes',
6
+ 'hour_ago': 'il y a une heure',
7
+ 'hours_ago': 'il y a {} heures',
8
+ 'day_ago': 'il y a un jour',
9
+ 'days_ago': 'il y a {} jours',
10
+ 'week_ago': 'il y a une semaine',
11
+ 'weeks_ago': 'il y a {} semaines',
12
+ 'month_ago': 'il y a un mois',
13
+ 'months_ago': 'il y a {} mois',
14
+ 'year_ago': 'il y a un an',
15
+ 'years_ago': 'il y a {} ans',
16
+ 'created_time': 'Date de création',
17
+ 'modified_time': 'Date de modification',
18
+ }
@@ -0,0 +1,18 @@
1
+ translations = {
2
+ 'just_now': 'たった今',
3
+ 'seconds_ago': '{} 秒前',
4
+ 'minute_ago': '1 分前',
5
+ 'minutes_ago': '{} 分前',
6
+ 'hour_ago': '1 時間前',
7
+ 'hours_ago': '{} 時間前',
8
+ 'day_ago': '1 日前',
9
+ 'days_ago': '{} 日前',
10
+ 'week_ago': '1 週間前',
11
+ 'weeks_ago': '{} 週間前',
12
+ 'month_ago': '1 ヶ月前',
13
+ 'months_ago': '{} ヶ月前',
14
+ 'year_ago': '1 年前',
15
+ 'years_ago': '{} 年前',
16
+ 'created_time': '作成日時',
17
+ 'modified_time': '更新日時',
18
+ }
@@ -0,0 +1,18 @@
1
+ translations = {
2
+ 'just_now': '방금',
3
+ 'seconds_ago': '{} 초 전',
4
+ 'minute_ago': '1 분 전',
5
+ 'minutes_ago': '{} 분 전',
6
+ 'hour_ago': '1 시간 전',
7
+ 'hours_ago': '{} 시간 전',
8
+ 'day_ago': '1 일 전',
9
+ 'days_ago': '{} 일 전',
10
+ 'week_ago': '1 주 전',
11
+ 'weeks_ago': '{} 주 전',
12
+ 'month_ago': '1 개월 전',
13
+ 'months_ago': '{} 개월 전',
14
+ 'year_ago': '1 년 전',
15
+ 'years_ago': '{} 년 전',
16
+ 'created_time': '작성일',
17
+ 'modified_time': '수정일',
18
+ }
@@ -0,0 +1,18 @@
1
+ translations = {
2
+ 'just_now': 'только что',
3
+ 'seconds_ago': '{} секунд назад',
4
+ 'minute_ago': 'минуту назад',
5
+ 'minutes_ago': '{} минут назад',
6
+ 'hour_ago': 'час назад',
7
+ 'hours_ago': '{} часов назад',
8
+ 'day_ago': 'день назад',
9
+ 'days_ago': '{} дней назад',
10
+ 'week_ago': 'неделю назад',
11
+ 'weeks_ago': '{} недель назад',
12
+ 'month_ago': 'месяц назад',
13
+ 'months_ago': '{} месяцев назад',
14
+ 'year_ago': 'год назад',
15
+ 'years_ago': '{} лет назад',
16
+ 'created_time': 'Дата создания',
17
+ 'modified_time': 'Дата изменения',
18
+ }
@@ -0,0 +1,18 @@
1
+ translations = {
2
+ 'just_now': '刚刚',
3
+ 'seconds_ago': '{} 秒前',
4
+ 'minute_ago': '1 分钟前',
5
+ 'minutes_ago': '{} 分钟前',
6
+ 'hour_ago': '1 小时前',
7
+ 'hours_ago': '{} 小时前',
8
+ 'day_ago': '1 天前',
9
+ 'days_ago': '{} 天前',
10
+ 'week_ago': '1 周前',
11
+ 'weeks_ago': '{} 周前',
12
+ 'month_ago': '1 个月前',
13
+ 'months_ago': '{} 个月前',
14
+ 'year_ago': '1 年前',
15
+ 'years_ago': '{} 年前',
16
+ 'created_time': '创建时间',
17
+ 'modified_time': '最后更新',
18
+ }
@@ -0,0 +1,18 @@
1
+ translations = {
2
+ 'just_now': '剛剛',
3
+ 'seconds_ago': '{} 秒前',
4
+ 'minute_ago': '1 分鐘前',
5
+ 'minutes_ago': '{} 分鐘前',
6
+ 'hour_ago': '1 小時前',
7
+ 'hours_ago': '{} 小時前',
8
+ 'day_ago': '1 天前',
9
+ 'days_ago': '{} 天前',
10
+ 'week_ago': '1 週前',
11
+ 'weeks_ago': '{} 週前',
12
+ 'month_ago': '1 個月前',
13
+ 'months_ago': '{} 個月前',
14
+ 'year_ago': '1 年前',
15
+ 'years_ago': '{} 年前',
16
+ 'created_time': '建立時間',
17
+ 'modified_time': '修改時間',
18
+ }
@@ -1,14 +1,18 @@
1
1
  import os
2
+ import json
2
3
  import platform
3
4
  from datetime import datetime
4
5
  from pathlib import Path
5
6
  from mkdocs.plugins import BasePlugin
6
7
  from mkdocs.config import config_options
8
+ from .lang import load_translations
9
+ from .styles import DOCUMENT_DATES_CSS
7
10
 
8
11
  class DocumentDatesPlugin(BasePlugin):
9
12
  config_scheme = (
13
+ ('type', config_options.Type(str, default='date')),
14
+ ('locale', config_options.Type(str, default='en')),
10
15
  ('date_format', config_options.Type(str, default='%Y-%m-%d')),
11
- ('show_time', config_options.Type(bool, default=False)),
12
16
  ('time_format', config_options.Type(str, default='%H:%M:%S')),
13
17
  ('position', config_options.Type(str, default='bottom')),
14
18
  ('exclude', config_options.Type(list, default=[])),
@@ -16,35 +20,11 @@ class DocumentDatesPlugin(BasePlugin):
16
20
 
17
21
  def __init__(self):
18
22
  super().__init__()
23
+ self.translations = load_translations()
19
24
 
20
- def get_css_content(self):
25
+ def _get_css_content(self):
21
26
  """返回插件的 CSS 样式"""
22
- return """
23
- .document-dates-plugin {
24
- color: #8e8e8e;
25
- font-size: 0.75rem;
26
- padding: 0.2rem 0;
27
- opacity: 0.8;
28
- display: flex;
29
- gap: 1.5rem;
30
- align-items: center;
31
- margin-bottom: 0.3rem;
32
- }
33
- .document-dates-plugin span {
34
- display: inline-flex;
35
- align-items: center;
36
- gap: 0.3rem;
37
- }
38
- .document-dates-plugin .material-icons {
39
- font-size: 0.9rem;
40
- opacity: 0.7;
41
- }
42
- .document-dates-plugin-wrapper {
43
- margin: 0.3rem 0 1rem 0;
44
- border-bottom: 1px solid rgba(0, 0, 0, 0.07);
45
- padding-bottom: 0.5rem;
46
- }
47
- """
27
+ return DOCUMENT_DATES_CSS
48
28
 
49
29
  def on_config(self, config):
50
30
  """配置插件并添加必要的 CSS"""
@@ -59,117 +39,195 @@ class DocumentDatesPlugin(BasePlugin):
59
39
  # 添加自定义 CSS
60
40
  css_file = Path(config['docs_dir']) / 'assets' / 'document_dates.css'
61
41
  css_file.parent.mkdir(parents=True, exist_ok=True)
62
- css_file.write_text(self.get_css_content())
42
+ css_file.write_text(self._get_css_content())
63
43
  config['extra_css'].append('assets/document_dates.css')
64
44
 
65
45
  return config
66
46
 
67
- def format_date_info(self, created, modified):
47
+ def _get_date_info(self, created, modified):
68
48
  """格式化日期信息的 HTML"""
49
+ # 获取翻译字典
50
+ locale = self.config['locale']
51
+ if locale not in self.translations:
52
+ locale = 'en'
53
+ t = self.translations[locale]
54
+
69
55
  return (
70
- f"\n\n"
71
56
  f"<div class='document-dates-plugin-wrapper'>"
72
57
  f"<div class='document-dates-plugin'>"
73
- f"<span><span class='material-icons'>add_circle</span>"
74
- f"{self.format_date(created)}</span>"
75
- f"<span><span class='material-icons'>update</span>"
76
- f"{self.format_date(modified)}</span>"
58
+ f"<span title='{t['created_time']}'><span class='material-icons'>add_circle</span>"
59
+ f"{self._get_formatted_date(created)}</span>"
60
+ f"<span title='{t['modified_time']}'><span class='material-icons'>update</span>"
61
+ f"{self._get_formatted_date(modified)}</span>"
62
+ f"</div>"
77
63
  f"</div>"
78
- f"</div>\n"
79
64
  )
80
65
 
81
- def insert_date_info(self, markdown, date_info):
66
+ def _insert_date_info(self, markdown, date_info):
82
67
  """根据配置将日期信息插入到合适的位置"""
68
+ if not markdown.strip():
69
+ return markdown
70
+
83
71
  if self.config['position'] == 'top':
84
- lines = markdown.split('\n')
72
+ lines = markdown.splitlines()
85
73
  for i, line in enumerate(lines):
86
74
  if line.startswith('#'):
87
75
  lines.insert(i + 1, date_info)
88
76
  return '\n'.join(lines)
89
- return date_info + markdown
90
- return markdown + "\n\n" + date_info
77
+ return f"{date_info}\n{markdown}"
78
+ return f"{markdown}\n\n{date_info}"
91
79
 
92
80
  def on_page_markdown(self, markdown, page, config, files):
93
81
  """处理页面内容,添加日期信息"""
94
82
  file_path = Path(page.file.abs_src_path)
95
- docs_dir = Path(config['docs_dir'])
96
-
97
- # 检查是否在排除列表中
98
- for exclude_pattern in self.config['exclude']:
99
- # 处理目录递归排除(如 private/*)
100
- if exclude_pattern.endswith('/*'):
101
- base_dir = exclude_pattern[:-2] # 移除 /*
102
- try:
103
- # 检查当前文件是否在指定目录或其子目录中
104
- rel_path = file_path.relative_to(docs_dir)
105
- if str(rel_path).startswith(f"{base_dir}/"):
106
- return markdown
107
- except ValueError:
108
- continue
109
-
110
- # 处理特定目录下的特定类型文件(如 drafts/*.md)
111
- elif '/*.' in exclude_pattern:
112
- dir_part, ext_part = exclude_pattern.split('/*.')
113
- if (file_path.parent.name == dir_part and
114
- file_path.suffix == f".{ext_part}"):
115
- return markdown
116
-
117
- # 处理特定后缀文件(如 *.tmp)
118
- elif exclude_pattern.startswith('*.'):
119
- if file_path.suffix == f".{exclude_pattern[2:]}":
120
- return markdown
121
-
122
- # 处理精确文件匹配(如 temp.md)
123
- elif file_path.name == exclude_pattern:
124
- return markdown
125
83
 
126
- # 直接获取日期信息
127
- created, modified = self.get_file_dates(file_path)
84
+ if self._is_excluded(file_path, Path(config['docs_dir'])):
85
+ return markdown
128
86
 
129
- # 检查 frontmatter 中的日期
130
- meta = getattr(page, 'meta', {})
131
- if 'created_date' in meta:
132
- try:
133
- date_str = str(meta['created_date']).strip("'\"") # 移除可能存在的引号
134
- created = datetime.fromisoformat(date_str)
135
- except (ValueError, TypeError):
136
- # 如果解析失败,保持原有的文件系统日期
137
- pass
138
-
139
- if 'modified_date' in meta:
140
- try:
141
- date_str = str(meta['modified_date']).strip("'\"") # 移除可能存在的引号
142
- modified = datetime.fromisoformat(date_str)
143
- except (ValueError, TypeError):
144
- # 如果解析失败,保持原有的文件系统日期
145
- pass
87
+ created, modified = self._get_file_dates(file_path)
88
+ created, modified = self._process_meta_dates(page.meta, created, modified)
146
89
 
147
- # 格式化并插入日期信息
148
- date_info = self.format_date_info(created, modified)
149
- return self.insert_date_info(markdown, date_info)
90
+ date_info = self._get_date_info(created, modified)
91
+ return self._insert_date_info(markdown, date_info)
92
+
93
+ def _is_excluded(self, file_path: Path, docs_dir: Path) -> bool:
94
+ """检查文件是否在排除列表中"""
95
+ for pattern in self.config['exclude']:
96
+ if self._matches_exclude_pattern(file_path, docs_dir, pattern):
97
+ return True
98
+ return False
99
+
100
+ def _matches_exclude_pattern(self, file_path: Path, docs_dir: Path, pattern: str) -> bool:
101
+ """检查文件是否匹配排除模式
102
+ 支持三种匹配模式:
103
+ 1. 具体文件路径:支持多级目录,如 'private/temp.md'
104
+ 2. 目录下所有文件:使用 '*' 通配符,如 'private/*',匹配包含子目录的所有文件
105
+ 3. 指定目录下特定类型文件:如 'private/*.md',仅匹配当前目录下的特定类型文件
106
+ """
107
+ try:
108
+ # 获取相对于 docs_dir 的路径
109
+ rel_path = file_path.relative_to(docs_dir)
110
+ pattern_path = Path(pattern)
111
+
112
+ # 情况1:匹配具体文件路径
113
+ if '*' not in pattern:
114
+ return str(rel_path) == pattern
115
+
116
+ # 情况2:匹配目录下所有文件(包含子目录)
117
+ if pattern.endswith('/*'):
118
+ base_dir = pattern[:-2]
119
+ return str(rel_path).startswith(f"{base_dir}/")
120
+
121
+ # 情况3:匹配指定目录下的特定类型文件(不包含子目录)
122
+ if '*.' in pattern:
123
+ pattern_dir = pattern_path.parent
124
+ pattern_suffix = pattern_path.name[1:] # 去掉 * 号
125
+ return (rel_path.parent == Path(pattern_dir) and
126
+ rel_path.name.endswith(pattern_suffix))
127
+
128
+ return False
129
+ except ValueError:
130
+ return False
150
131
 
151
- def get_file_dates(self, file_path):
132
+ def _process_meta_dates(self, meta: dict, created: datetime, modified: datetime) -> tuple[datetime, datetime]:
133
+ """处理 frontmatter 中的日期"""
134
+ result_created = self._parse_meta_date(meta.get('created'), created)
135
+ result_modified = self._parse_meta_date(meta.get('modified'), modified)
136
+ return result_created, result_modified
137
+
138
+ def _parse_meta_date(self, date_str: str | None, default_date: datetime) -> datetime:
139
+ """解析 meta 中的日期字符串"""
140
+ if not date_str:
141
+ return default_date
142
+
143
+ try:
144
+ return datetime.fromisoformat(str(date_str).strip("'\""))
145
+ except (ValueError, TypeError):
146
+ return default_date
147
+
148
+ def _get_file_dates(self, file_path):
152
149
  """获取文件的创建时间和修改时间"""
153
-
154
- stat = os.stat(file_path)
155
- modified = datetime.fromtimestamp(stat.st_mtime)
156
-
157
- system = platform.system().lower()
158
- if system == 'darwin': # macOS
159
- try:
160
- created = datetime.fromtimestamp(stat.st_birthtime)
161
- except AttributeError:
150
+ try:
151
+ # 尝试从缓存读取时间信息
152
+ docs_dir = Path(file_path).parent.parent
153
+ cache_file = docs_dir / '.dates_cache.json'
154
+ if cache_file.exists():
155
+ with open(cache_file) as f:
156
+ dates_cache = json.load(f)
157
+ rel_path = str(Path(file_path).relative_to(docs_dir))
158
+ if rel_path in dates_cache:
159
+ return (
160
+ datetime.fromisoformat(dates_cache[rel_path]['created']),
161
+ datetime.fromisoformat(dates_cache[rel_path]['modified'])
162
+ )
163
+
164
+ # 如果缓存不存在或文件不在缓存中,使用文件系统时间
165
+ stat = os.stat(file_path)
166
+ modified = datetime.fromtimestamp(stat.st_mtime)
167
+
168
+ system = platform.system().lower()
169
+ if system == 'darwin': # macOS
170
+ try:
171
+ created = datetime.fromtimestamp(stat.st_birthtime)
172
+ except AttributeError:
173
+ created = datetime.fromtimestamp(stat.st_ctime)
174
+ elif system == 'windows': # Windows
162
175
  created = datetime.fromtimestamp(stat.st_ctime)
163
- elif system == 'windows': # Windows
164
- created = datetime.fromtimestamp(stat.st_ctime)
165
- else: # Linux 和其他系统
166
- # Linux 没有可靠的创建时间,使用修改时间作为创建时间
167
- created = modified
176
+ else: # Linux 和其他系统
177
+ created = modified
178
+
179
+ return created, modified
180
+
181
+ except (OSError, ValueError, json.JSONDecodeError) as e:
182
+ current_time = datetime.now()
183
+ return current_time, current_time
184
+
185
+ def _get_timeago(self, date):
186
+ """将日期格式化为 timeago 格式"""
187
+ now = datetime.now()
188
+ diff = now - date
189
+ seconds = diff.total_seconds()
190
+
191
+ # 获取翻译字典
192
+ locale = self.config['locale']
193
+ if locale not in self.translations:
194
+ locale = 'en'
195
+ t = self.translations[locale]
168
196
 
169
- return created, modified
197
+ # 时间间隔判断
198
+ if seconds < 10:
199
+ return t['just_now']
200
+ elif seconds < 60:
201
+ return t['seconds_ago'].format(int(seconds))
202
+ elif seconds < 120:
203
+ return t['minute_ago']
204
+ elif seconds < 3600:
205
+ return t['minutes_ago'].format(int(seconds / 60))
206
+ elif seconds < 7200:
207
+ return t['hour_ago']
208
+ elif seconds < 86400:
209
+ return t['hours_ago'].format(int(seconds / 3600))
210
+ elif seconds < 172800:
211
+ return t['day_ago']
212
+ elif seconds < 604800:
213
+ return t['days_ago'].format(int(seconds / 86400))
214
+ elif seconds < 1209600:
215
+ return t['week_ago']
216
+ elif seconds < 2592000:
217
+ return t['weeks_ago'].format(int(seconds / 604800))
218
+ elif seconds < 5184000:
219
+ return t['month_ago']
220
+ elif seconds < 31536000:
221
+ return t['months_ago'].format(int(seconds / 2592000))
222
+ elif seconds < 63072000:
223
+ return t['year_ago']
224
+ else:
225
+ return t['years_ago'].format(int(seconds / 31536000))
170
226
 
171
- def format_date(self, date):
172
- if self.config['show_time']:
227
+ def _get_formatted_date(self, date):
228
+ """格式化日期,支持 timeago、date 和 datetime 格式"""
229
+ if self.config['type'] == 'timeago':
230
+ return self._get_timeago(date)
231
+ elif self.config['type'] == 'datetime':
173
232
  return date.strftime(f"{self.config['date_format']} {self.config['time_format']}")
174
- return date.strftime(self.config['date_format'])
175
-
233
+ return date.strftime(self.config['date_format'])
@@ -0,0 +1,28 @@
1
+ """CSS 样式定义"""
2
+
3
+ DOCUMENT_DATES_CSS = """
4
+ .document-dates-plugin {
5
+ color: #8e8e8e;
6
+ font-size: 0.75rem;
7
+ padding: 0.2rem 0;
8
+ opacity: 0.8;
9
+ display: flex;
10
+ gap: 1.5rem;
11
+ align-items: center;
12
+ margin-bottom: 0.3rem;
13
+ }
14
+ .document-dates-plugin span {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ gap: 0.3rem;
18
+ }
19
+ .document-dates-plugin .material-icons {
20
+ font-size: 0.9rem;
21
+ opacity: 0.7;
22
+ }
23
+ .document-dates-plugin-wrapper {
24
+ margin: 0.3rem 0 1rem 0;
25
+ border-bottom: 1px solid rgba(0, 0, 0, 0.07);
26
+ padding-bottom: 0.5rem;
27
+ }
28
+ """
@@ -0,0 +1,114 @@
1
+ Metadata-Version: 2.2
2
+ Name: mkdocs-document-dates
3
+ Version: 0.6.0
4
+ Summary: A MkDocs plugin for displaying accurate document creation and last modification dates.
5
+ Home-page: https://github.com/jaywhj/mkdocs-document-dates
6
+ Author: Aaron Wang
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.6
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: mkdocs>=1.0.0
14
+ Dynamic: author
15
+ Dynamic: classifier
16
+ Dynamic: description
17
+ Dynamic: description-content-type
18
+ Dynamic: home-page
19
+ Dynamic: requires-dist
20
+ Dynamic: requires-python
21
+ Dynamic: summary
22
+
23
+ # mkdocs-document-dates
24
+
25
+ English | [简体中文](README_zh.md)
26
+
27
+
28
+
29
+ A MkDocs plugin for displaying **accurate** document creation and last modification dates.
30
+
31
+ ## Features
32
+
33
+ - Automatically displays document creation and last modification times
34
+ - No Git dependency, uses filesystem timestamps directly
35
+ - Supports manual date specification in `Front Matter`
36
+ - Cross-platform support (Windows, macOS, Linux)
37
+ - Support for multiple time formats (date, datetime, timeago)
38
+ - Flexible display position (top or bottom)
39
+ - Support for document exclusion
40
+ - Material Design icons, Elegant styling
41
+ - Lightweight with no extra dependencies
42
+ - Multi-language support
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ pip install mkdocs-document-dates
48
+ ```
49
+
50
+ ## Configuration
51
+
52
+ Just add the plugin to your mkdocs.yml:
53
+
54
+ ```yaml
55
+ plugins:
56
+ - document-dates
57
+ ```
58
+
59
+ Or, customize the configuration:
60
+
61
+ ```yaml
62
+ plugins:
63
+ - document-dates:
64
+ type: date # Date type: date | datetime | timeago, default: date
65
+ locale: en # Localization: zh zh_tw en es fr de ar ja ko ru, default: en
66
+ date_format: '%Y-%m-%d' # Date format
67
+ time_format: '%H:%M:%S' # Time format (valid only if type=datetime)
68
+ position: bottom # Display position: top (after title) | bottom (end of document), default: bottom
69
+ exclude: # List of file patterns to exclude
70
+ - temp.md # Exclude specific file
71
+ - private/* # Exclude all files in private directory, including subdirectories
72
+ - drafts/*.md # Exclude all markdown files in the current directory drafts, but not subdirectories
73
+ ```
74
+
75
+ ## Manual Date Specification
76
+
77
+ You can also manually specify the date of a Markdown document in its `Front Matter` :
78
+
79
+ ```yaml
80
+ ---
81
+ created: 2023-01-01
82
+ modified: 2023-12-31
83
+ ---
84
+
85
+ # Document Title
86
+ ```
87
+
88
+
89
+
90
+ ## Configuration Options
91
+
92
+ - `type` : Date type (default: `date` )
93
+ - `date` : Display date only
94
+ - `datetime` : Display date and time
95
+ - `timeago` : Display relative time (e.g., 2 minutes ago)
96
+ - `locale` : Localization (default: `en` )
97
+ - Supports: `zh zh_tw en es fr de ar ja ko ru`
98
+ - `date_format` : Date format (default: `%Y-%m-%d`)
99
+ - Supports all Python datetime format strings, e.g., %Y-%m-%d, %b %d, %Y, etc.
100
+ - `time_format` : Time format (default: `%H:%M:%S`)
101
+ - `position` : Display position (default: `bottom`)
102
+ - `top` : Display after the first heading
103
+ - `bottom` : Display at the end of the document
104
+ - `exclude` : File exclusion list (default: [] )
105
+ - Supports glob patterns, e.g., ["private/\*", "temp.md", "drafts/\*.md"]
106
+
107
+ ## Notes
108
+
109
+ - Creation time behavior varies across operating systems:
110
+ - Windows: Uses file creation time
111
+ - macOS: Uses file creation time (birthtime)
112
+ - Linux: Uses modification time as creation time due to system limitations, if you need the exact creation time, you can manually specify it in Front Matter
113
+ - It still works when using CI/CD build systems (e.g. Github Actions)
114
+ - Used a cache file `.dates_cache.json` to solve this problem
@@ -0,0 +1,21 @@
1
+ mkdocs_document_dates/__init__.py,sha256=CZ37u8DC6kbkupKFk-VKyfKZZFAJ4rOD0aPzOzVoATc,58
2
+ mkdocs_document_dates/plugin.py,sha256=a8YPvWlENsU2vTkPVFg_KKjQhzBf3n_pMGFS9-Os6iQ,9504
3
+ mkdocs_document_dates/styles.py,sha256=ujhlKgDWsOo5sHiYtf3XekuGg4C5Yjol8RzP3W9Wzjo,578
4
+ mkdocs_document_dates/hooks/pre-commit,sha256=sXLpuap_p3nw7x5-346GMS8MsG2rEtA3zVyuGlOJWJs,3862
5
+ mkdocs_document_dates/lang/__init__.py,sha256=M1BLjCOA3HHKeAitk45YAgzxxNpnxFUvVAk6-FO_QSA,690
6
+ mkdocs_document_dates/lang/ar.py,sha256=BsZlxz54U_spOZ5SBiKt73ywoLKbR54cZNkKAs86OxM,632
7
+ mkdocs_document_dates/lang/de.py,sha256=B0Ffrn4lVSvcxFpGho7SiMm16GXYEmpjcVAR-k4UgSI,585
8
+ mkdocs_document_dates/lang/en.py,sha256=V6WYglXO56Vn9o96Fjny-LQ2BPOPwplxrNQBI4l6dq4,541
9
+ mkdocs_document_dates/lang/es.py,sha256=BwussbHS_Lnok6YY64JAsurksdl9GfamV8YEyaecPQY,586
10
+ mkdocs_document_dates/lang/fr.py,sha256=QRHqbX2qE7yy1v4UrZcOKv8gMbm4v1b32sKVejJmf4g,612
11
+ mkdocs_document_dates/lang/ja.py,sha256=Jqd3bcmyXCv-w-5eq3MSelOqImdJT_mCfCpyfeG3jiw,525
12
+ mkdocs_document_dates/lang/ko.py,sha256=GwY6yrKYAOj6S6feq9yqNxr2XpyGHC0XeenO541vmdM,520
13
+ mkdocs_document_dates/lang/ru.py,sha256=fK5s4mQKCoP6KI3jf6eqqmqB3YVn39q81U7Cw1QWNNg,721
14
+ mkdocs_document_dates/lang/zh.py,sha256=OrLElrSTZhHSwxBzuUtUj7NpQb7wm0s83viimpk2ynM,519
15
+ mkdocs_document_dates/lang/zh_tw.py,sha256=t3qu-a7UOzgcmYDkLFiosJZCcpfMU4xiKTJfu1ZHoHA,519
16
+ mkdocs_document_dates-0.6.0.dist-info/LICENSE,sha256=1YKfCs5WKSk-bON8a68WZE5to1B2klCrHBYiuaVCThM,514
17
+ mkdocs_document_dates-0.6.0.dist-info/METADATA,sha256=xl1Wmw2MZFR_TwU5-rb1V8f9N1gw1FA9OqqKpTVcIwk,3701
18
+ mkdocs_document_dates-0.6.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
19
+ mkdocs_document_dates-0.6.0.dist-info/entry_points.txt,sha256=gI-OFLGjDG6-oLEfyevl3Gwwj2GcqVFQeX3bvL1IF8o,83
20
+ mkdocs_document_dates-0.6.0.dist-info/top_level.txt,sha256=yWkKQdNuAJJVqUQ9uLa5xD4x_Gux4IfOUpy8Ryagdwc,22
21
+ mkdocs_document_dates-0.6.0.dist-info/RECORD,,
@@ -1,110 +0,0 @@
1
- Metadata-Version: 2.2
2
- Name: mkdocs-document-dates
3
- Version: 0.4.0
4
- Summary: A MkDocs plugin for displaying accurate document creation and last modification dates.
5
- Home-page: https://github.com/jaywhj/mkdocs-document-dates
6
- Author: Aaron Wang
7
- Classifier: Programming Language :: Python :: 3
8
- Classifier: License :: OSI Approved :: MIT License
9
- Classifier: Operating System :: OS Independent
10
- Requires-Python: >=3.6
11
- Description-Content-Type: text/markdown
12
- License-File: LICENSE
13
- Requires-Dist: mkdocs>=1.0.0
14
- Dynamic: author
15
- Dynamic: classifier
16
- Dynamic: description
17
- Dynamic: description-content-type
18
- Dynamic: home-page
19
- Dynamic: requires-dist
20
- Dynamic: requires-python
21
- Dynamic: summary
22
-
23
- # mkdocs-document-dates
24
-
25
- English | [简体中文](README_zh.md)
26
-
27
-
28
-
29
- A MkDocs plugin for displaying **accurate** document creation and last modification dates.
30
-
31
- ## Features
32
-
33
- - Automatically displays document creation and last modification times
34
- - Supports manual date specification in `Front Matter`
35
- - No Git dependency, uses filesystem timestamps directly
36
- - Cross-platform support (Windows, macOS, Linux)
37
- - Configurable date and time formats
38
- - Flexible display position (top or bottom)
39
- - File exclusion rules support
40
- - Material Design icons
41
- - Elegant styling
42
- - Lightweight with no extra dependencies
43
-
44
- ## Installation
45
-
46
- ```bash
47
- pip install mkdocs-document-dates
48
- ```
49
-
50
- ## Configuration
51
-
52
- Add the plugin to your mkdocs.yml:
53
-
54
- ```yaml
55
- plugins:
56
- - document-dates
57
- ```
58
-
59
- Or, customize the configuration:
60
-
61
- ```yaml
62
- plugins:
63
- - document-dates:
64
- date_format: '%Y-%m-%d' # Date format
65
- show_time: false # Whether to show time
66
- time_format: '%H:%M:%S' # Time format
67
- position: bottom # Display position: top (after title) or bottom (end of document)
68
- exclude: # List of file patterns to exclude
69
- - "private/*" # Exclude all files in private directory, including subdirectories
70
- - "drafts/*.md" # Exclude all markdown files in drafts directory
71
- - "temp.md" # Exclude specific file
72
- - "*.tmp" # Exclude all files with .tmp extension
73
- ```
74
-
75
- ## Manual Date Specification
76
-
77
- You can also manually specify the date of a Markdown document in its `Front Matter` :
78
-
79
- ```yaml
80
- ---
81
- created_date: 2023-01-01
82
- modified_date: 2023-12-31
83
- ---
84
-
85
- # Document Title
86
- ```
87
-
88
- ## Configuration Options
89
-
90
- - `date_format`: Date format (default: %Y-%m-%d)
91
- - Supports all Python datetime format strings, examples: %Y-%m-%d, %b %d, %Y, etc.
92
- - `show_time`: Whether to show time (default: false)
93
- - true: Show both date and time
94
- - false: Show date only
95
- - `time_format`: Time format (default: %H:%M:%S)
96
- - Only effective when show_time is true
97
- - `position`: Display position (default: bottom)
98
- - top: Display after the first heading
99
- - bottom: Display at the end of document
100
- - `exclude`: List of files to exclude (default: [])
101
- - Supports glob patterns, example: ["private/*", "temp.md"]
102
-
103
- ## Notes
104
-
105
- - Creation time behavior varies across operating systems:
106
- - Windows: Uses file creation time
107
- - macOS: Uses file creation time (birthtime)
108
- - Linux: Uses modification time as creation time due to system limitations
109
- - For accurate creation times, it's recommended to use Front Matter for manual specification
110
-
@@ -1,8 +0,0 @@
1
- mkdocs_document_dates/__init__.py,sha256=yom7psmObebsZY0AwCN1PjlGUwPkny2r6NyzoO0cudg,58
2
- mkdocs_document_dates/plugin.py,sha256=zn9BfgqR3peyH33uY8Jwx1BDFT9fuMQgPsT5Y-0xo-Y,6584
3
- mkdocs_document_dates-0.4.0.dist-info/LICENSE,sha256=1YKfCs5WKSk-bON8a68WZE5to1B2klCrHBYiuaVCThM,514
4
- mkdocs_document_dates-0.4.0.dist-info/METADATA,sha256=4_lWCetVk8oxW6Acp7JEi7WG5GVTI7AffuHzCBgPNhw,3278
5
- mkdocs_document_dates-0.4.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
6
- mkdocs_document_dates-0.4.0.dist-info/entry_points.txt,sha256=gI-OFLGjDG6-oLEfyevl3Gwwj2GcqVFQeX3bvL1IF8o,83
7
- mkdocs_document_dates-0.4.0.dist-info/top_level.txt,sha256=yWkKQdNuAJJVqUQ9uLa5xD4x_Gux4IfOUpy8Ryagdwc,22
8
- mkdocs_document_dates-0.4.0.dist-info/RECORD,,