mkdocs-document-dates 3.7.4__tar.gz → 3.8.0__tar.gz

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.
Files changed (43) hide show
  1. {mkdocs_document_dates-3.7.4/mkdocs_document_dates.egg-info → mkdocs_document_dates-3.8.0}/PKG-INFO +3 -3
  2. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/README.md +2 -2
  3. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/cache_manager.py +3 -3
  4. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/plugin.py +45 -63
  5. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/utils.py +72 -35
  6. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0/mkdocs_document_dates.egg-info}/PKG-INFO +3 -3
  7. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/pyproject.toml +1 -1
  8. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/setup.py +1 -1
  9. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/LICENSE +0 -0
  10. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/MANIFEST.in +0 -0
  11. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/__init__.py +0 -0
  12. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/hooks/pre-commit +0 -0
  13. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/hooks_installer.py +0 -0
  14. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/.DS_Store +0 -0
  15. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/config/user.config.css +0 -0
  16. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/config/user.config.js +0 -0
  17. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/core/core.css +0 -0
  18. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/core/core.js +0 -0
  19. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/core/default.config.js +0 -0
  20. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/core/md5.min.js +0 -0
  21. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/core/timeago.full.min.js +0 -0
  22. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/core/timeago.min.js +0 -0
  23. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/core/utils.js +0 -0
  24. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/fonts/material-icons.css +0 -0
  25. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/fonts/materialicons.woff2 +0 -0
  26. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/templates/recently_updated_detail.html +0 -0
  27. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/templates/recently_updated_grid.html +0 -0
  28. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/templates/recently_updated_group.html +0 -0
  29. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/templates/recently_updated_list.html +0 -0
  30. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/tippy/backdrop.css +0 -0
  31. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/tippy/light.css +0 -0
  32. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/tippy/material.css +0 -0
  33. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/tippy/popper.min.js +0 -0
  34. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/tippy/scale.css +0 -0
  35. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/tippy/shift-away.css +0 -0
  36. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/tippy/tippy.css +0 -0
  37. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates/static/tippy/tippy.umd.min.js +0 -0
  38. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates.egg-info/SOURCES.txt +0 -0
  39. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates.egg-info/dependency_links.txt +0 -0
  40. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates.egg-info/entry_points.txt +0 -0
  41. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates.egg-info/requires.txt +0 -0
  42. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/mkdocs_document_dates.egg-info/top_level.txt +0 -0
  43. {mkdocs_document_dates-3.7.4 → mkdocs_document_dates-3.8.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-document-dates
3
- Version: 3.7.4
3
+ Version: 3.8.0
4
4
  Summary: A new generation MkDocs plugin for displaying exact creation date, last updated date, authors, email of documents
5
5
  Author-email: Aaron Wang <aaronwqt@gmail.com>
6
6
  License-Expression: MIT
@@ -82,8 +82,8 @@ In addition to the above basic configuration, the plug-in also provides a wealth
82
82
  - [Structure and Style](https://jaywhj.netlify.app/document-dates-en#Structure-and-Style): You can freely configure the plugin's display structure in mkdocs.yml or Front Matter. You can quickly set the plugin styles through preset entrances, such as icons, themes, colors, fonts, animations, dividing line and so on
83
83
  - [Template Variables](https://jaywhj.netlify.app/document-dates-en#Template-Variables): Can be used to optimize `sitemap.xml` for site SEO
84
84
  - [Recently Updated Module](https://jaywhj.netlify.app/document-dates-en#Recently-Updated-Module): Enable list of recently updated documents (in descending order of update date), this is ideal for sites with a large number of documents, so that readers can quickly see what's new
85
- - [Localization Language](https://jaywhj.netlify.app/document-dates-en#Localization-Language): More localization languages for `timeago` and `tooltip`
86
- - [Development Stories](https://jaywhj.netlify.app/document-dates-en#Development-Stories): Describes the origin of the plug-in, the difficulties and solutions encountered in development, and the principles and directions of product design
85
+ - [Localization Language](https://jaywhj.netlify.app/document-dates-en#Localization-Language): More localization languages for `timeago` and `tooltip`
86
+ - [Developer API](https://jaywhj.netlify.app/document-dates-en#Developer-API): Provides a date data API for developers, making it easy to retrieve exact dates in other plugins or hooks
87
87
 
88
88
  See the documentation for details: https://jaywhj.netlify.app/document-dates-en
89
89
 
@@ -64,8 +64,8 @@ In addition to the above basic configuration, the plug-in also provides a wealth
64
64
  - [Structure and Style](https://jaywhj.netlify.app/document-dates-en#Structure-and-Style): You can freely configure the plugin's display structure in mkdocs.yml or Front Matter. You can quickly set the plugin styles through preset entrances, such as icons, themes, colors, fonts, animations, dividing line and so on
65
65
  - [Template Variables](https://jaywhj.netlify.app/document-dates-en#Template-Variables): Can be used to optimize `sitemap.xml` for site SEO
66
66
  - [Recently Updated Module](https://jaywhj.netlify.app/document-dates-en#Recently-Updated-Module): Enable list of recently updated documents (in descending order of update date), this is ideal for sites with a large number of documents, so that readers can quickly see what's new
67
- - [Localization Language](https://jaywhj.netlify.app/document-dates-en#Localization-Language): More localization languages for `timeago` and `tooltip`
68
- - [Development Stories](https://jaywhj.netlify.app/document-dates-en#Development-Stories): Describes the origin of the plug-in, the difficulties and solutions encountered in development, and the principles and directions of product design
67
+ - [Localization Language](https://jaywhj.netlify.app/document-dates-en#Localization-Language): More localization languages for `timeago` and `tooltip`
68
+ - [Developer API](https://jaywhj.netlify.app/document-dates-en#Developer-API): Provides a date data API for developers, making it easy to retrieve exact dates in other plugins or hooks
69
69
 
70
70
  See the documentation for details: https://jaywhj.netlify.app/document-dates-en
71
71
 
@@ -164,7 +164,7 @@ def update_cache():
164
164
  global_updated = setup_gitattributes(docs_dir)
165
165
 
166
166
  # 获取docs目录下已跟踪(tracked)的markdown文件
167
- cmd = ["git", "ls-files", "*.md"]
167
+ cmd = ['git', '-c', 'core.quotepath=false', 'ls-files', '*.md']
168
168
  result = subprocess.run(cmd, cwd=docs_dir, env=_clean_git_env(), capture_output=True, encoding='utf-8')
169
169
  tracked_files = result.stdout.splitlines() if result.stdout else []
170
170
 
@@ -185,13 +185,13 @@ def update_cache():
185
185
 
186
186
  full_path = docs_dir / rel_path
187
187
  if full_path.exists():
188
- created_time = load_file_creation_date(full_path).astimezone()
188
+ created_time = load_file_creation_date(full_path)
189
189
  if not jsonl_cache_file.exists():
190
190
  git_time = load_git_first_commit_date(full_path)
191
191
  if git_time:
192
192
  created_time = min(created_time, git_time)
193
193
  jsonl_dates_cache[rel_path] = {
194
- "created": created_time.isoformat()
194
+ "created": int(created_time.timestamp())
195
195
  }
196
196
  project_updated = True
197
197
  except Exception as e:
@@ -1,16 +1,15 @@
1
- import os
2
1
  import yaml
3
2
  import shutil
4
3
  import logging
5
4
  from jinja2 import ChoiceLoader, FileSystemLoader
6
- from datetime import datetime
5
+ from datetime import datetime, timezone
7
6
  from pathlib import Path
8
7
  from mkdocs.plugins import BasePlugin, event_priority
9
8
  from mkdocs.config import config_options
10
9
  from mkdocs.structure.pages import Page
11
10
  from mkdocs.utils import get_relative_url
12
11
  from urllib.parse import urlparse
13
- from .utils import load_file_creation_date, load_git_metadata, load_git_last_updated_dates, read_jsonl_cache, compile_exclude_patterns, is_excluded, get_recently_updated_files
12
+ from .utils import compile_exclude_patterns, is_excluded, get_recently_updated_files, load_dates_and_authors
14
13
 
15
14
  logger = logging.getLogger("mkdocs.plugins.document_dates")
16
15
  logger.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, CRITICAL
@@ -45,14 +44,13 @@ class DocumentDatesPlugin(BasePlugin):
45
44
  super().__init__()
46
45
 
47
46
  self.data_cached = {}
48
- self.last_updated_dates = {}
49
47
  self.authors_yml = {}
50
48
  self.recent_docs_html = None
51
49
  self.recent_enable = False
52
50
  self._exclude_patterns = []
53
51
 
54
52
  def on_config(self, config):
55
- docs_dir_path = Path(config['docs_dir'])
53
+ docs_dir_path = Path(config.docs_dir)
56
54
 
57
55
  # 加载 author 配置
58
56
  authors_file = docs_dir_path / 'authors.yml'
@@ -65,20 +63,6 @@ class DocumentDatesPlugin(BasePlugin):
65
63
  pass
66
64
  self._load_authors_from_yaml(authors_file)
67
65
 
68
- # 加载文档 git 元数据(日期 & 作者)
69
- self.data_cached = load_git_metadata(docs_dir_path)
70
- # 加载 jsonl 缓存数据
71
- jsonl_cache_file = docs_dir_path / '.dates_cache.jsonl'
72
- if jsonl_cache_file.exists():
73
- jsonl_cache = read_jsonl_cache(jsonl_cache_file)
74
- for filename, new_info in jsonl_cache.items():
75
- if filename in self.data_cached:
76
- self.data_cached[filename].update(new_info)
77
-
78
- # 加载文档最近更新时间(日期)
79
- self.last_updated_dates = load_git_last_updated_dates(docs_dir_path)
80
-
81
-
82
66
  # 复制配置文件到用户目录(如果不存在)
83
67
  dest_dir = docs_dir_path / 'assets' / 'document_dates'
84
68
  dest_dir.mkdir(parents=True, exist_ok=True)
@@ -151,32 +135,41 @@ class DocumentDatesPlugin(BasePlugin):
151
135
 
152
136
  return config
153
137
 
138
+ @event_priority(50)
139
+ def on_files(self, files, config):
140
+ self.data_cached = load_dates_and_authors(Path(config.docs_dir), files)
141
+ return files
142
+
154
143
  @event_priority(50)
155
144
  def on_page_markdown(self, markdown, page: Page, config, files):
156
145
  # 获取相对路径,src_uri 总是以"/"分隔
157
- rel_path = getattr(page.file, 'src_uri', page.file.src_path)
158
- if os.sep != '/':
159
- rel_path = rel_path.replace(os.sep, '/')
160
- file_path = page.file.abs_src_path
161
-
146
+ rel_path = getattr(page.file, 'src_uri')
147
+
162
148
  # 优先获取 page.meta 中的数据
163
149
  created = self._load_meta_date(page.meta, self.config['created_field_names'])
164
150
  updated = self._load_meta_date(page.meta, self.config['updated_field_names'])
165
151
  authors = self._load_meta_author(page.meta, page.url)
166
152
 
167
- # 再获取缓存的数据
168
- if not created:
169
- created = self._load_created_cached(file_path, rel_path)
170
- if not updated:
171
- updated = self._load_updated_cached(file_path, rel_path)
153
+ # 如果 meta 数据存在,则存储
154
+ cache = self.data_cached.setdefault(rel_path, {})
155
+ if created:
156
+ cache['created'] = created
157
+ else:
158
+ created = cache.get('created')
159
+
160
+ if updated:
161
+ cache['updated'] = updated
162
+ else:
163
+ updated = cache.get('updated')
164
+
172
165
  if not authors:
173
166
  authors = self._load_author_cached(rel_path, page, config)
174
167
 
175
- # 注入数据
168
+ # 注入数据到模板 (utc datetime -> local datetime)
176
169
  page.meta["document_dates"] = {
177
170
  "dates": {
178
- "created": created.isoformat(),
179
- "updated": updated.isoformat(),
171
+ "created": created.astimezone().isoformat() if created else None,
172
+ "updated": updated.astimezone().isoformat() if updated else None,
180
173
  },
181
174
  "authors": authors
182
175
  }
@@ -212,7 +205,7 @@ class DocumentDatesPlugin(BasePlugin):
212
205
 
213
206
  # 获取最近更新的文档数据
214
207
  recent_exclude_patterns = compile_exclude_patterns(exclude_list)
215
- recently_updated_docs = get_recently_updated_files(self.last_updated_dates, files, recent_exclude_patterns, limit, self.recent_enable, prefix)
208
+ recently_updated_docs = get_recently_updated_files(self.data_cached, files, recent_exclude_patterns, limit, self.recent_enable, prefix)
216
209
 
217
210
  # 将数据注入到 config['extra'] 中供全局访问
218
211
  if not config.get('extra', {}).get("recently_updated_docs", {}):
@@ -290,42 +283,31 @@ class DocumentDatesPlugin(BasePlugin):
290
283
  for field in field_names:
291
284
  if field in meta:
292
285
  try:
293
- # 移除首尾可能存在的单双引号和时区信息
286
+ # 移除首尾可能存在的单双引号
294
287
  date_str = str(meta[field]).strip("'\"")
295
- return datetime.fromisoformat(date_str).replace(tzinfo=None)
288
+ dt = datetime.fromisoformat(date_str)
289
+ # 如果没时区,则当成本地时间,再转 UTC
290
+ if dt.tzinfo is None:
291
+ local_tz = datetime.now().astimezone().tzinfo
292
+ dt = dt.replace(tzinfo=local_tz)
293
+ return dt.astimezone(timezone.utc)
294
+ # return datetime.fromisoformat(date_str).astimezone()
296
295
  except Exception:
297
296
  continue
298
297
  return None
299
298
 
300
- def _load_created_cached(self, file_path, rel_path):
301
- # 优先从缓存中读取
302
- if rel_path in self.data_cached:
303
- return datetime.fromisoformat(self.data_cached[rel_path]['created'])
304
- # 从文件系统获取
305
- return load_file_creation_date(file_path).astimezone()
306
-
307
- def _load_updated_cached(self, file_path, rel_path):
308
- # 优先从缓存中读取
309
- if rel_path in self.last_updated_dates:
310
- return datetime.fromtimestamp(self.last_updated_dates[rel_path]).astimezone()
311
- # 从文件系统获取最后修改时间
312
- stat = os.stat(file_path)
313
- return datetime.fromtimestamp(stat.st_mtime).astimezone()
314
-
315
-
316
299
  def _load_author_cached(self, rel_path, page, config):
317
300
  # 1. git author
318
- if rel_path in self.data_cached:
319
- authors_list = self.data_cached[rel_path].get('authors')
320
- if authors_list:
321
- authors = []
322
- for data in authors_list:
323
- full_author = self.authors_yml.get(data['name'])
324
- if full_author:
325
- authors.append(self._repair_author(full_author, page.url))
326
- else:
327
- authors.append(Author(**data))
328
- return authors
301
+ authors_list = self.data_cached.get(rel_path, {}).get('authors', None)
302
+ if authors_list:
303
+ authors = []
304
+ for data in authors_list:
305
+ full_author = self.authors_yml.get(data['name'])
306
+ if full_author:
307
+ authors.append(self._repair_author(full_author, page.url))
308
+ else:
309
+ authors.append(Author(**data))
310
+ return authors
329
311
 
330
312
  # 2. site_author 或 PC username
331
313
  name = config.get('site_author') or Path.home().name
@@ -407,7 +389,7 @@ class DocumentDatesPlugin(BasePlugin):
407
389
  return (
408
390
  f"<span class='dd-item' data-tippy-content data-tippy-raw='{formatted}'>"
409
391
  f"<span class='material-icons' data-icon='{icon}'></span>"
410
- f"<time datetime='{time_obj.isoformat()}'>{self._formatting_date(time_obj)}</time>"
392
+ f"<time datetime='{time_obj.astimezone().isoformat()}'>{self._formatting_date(time_obj)}</time>"
411
393
  f"</span>"
412
394
  )
413
395
 
@@ -9,7 +9,7 @@ import re
9
9
  import math
10
10
  from pathlib import Path
11
11
  from operator import itemgetter
12
- from datetime import datetime
12
+ from datetime import datetime, timezone
13
13
  from collections import defaultdict
14
14
  from mkdocs.structure.files import Files
15
15
 
@@ -17,6 +17,44 @@ logger = logging.getLogger("mkdocs.plugins.document_dates")
17
17
  logger.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, CRITICAL
18
18
 
19
19
 
20
+ def load_dates_and_authors(docs_dir_path: Path, files: Files):
21
+
22
+ # git 创建日期
23
+ created_data = load_git_metadata(docs_dir_path)
24
+
25
+ # 覆盖 jsonl 创建日期
26
+ jsonl_cache_file = docs_dir_path / '.dates_cache.jsonl'
27
+ if jsonl_cache_file.exists():
28
+ jsonl_cache = read_jsonl_cache(jsonl_cache_file)
29
+ for filename, new_info in jsonl_cache.items():
30
+ if filename in created_data:
31
+ created_data[filename].update(new_info)
32
+
33
+ # git 更新日期
34
+ updated_data = load_git_last_updated_dates(docs_dir_path)
35
+
36
+ for file in files:
37
+ if file.inclusion.is_excluded():
38
+ continue
39
+ if not file.src_path.endswith('.md'):
40
+ continue
41
+ rel_path = getattr(file, 'src_uri')
42
+
43
+ # created: timestamp -> datetime
44
+ cache = created_data.setdefault(rel_path, {})
45
+ created_ts = cache.get('created')
46
+ cache['created'] = (
47
+ datetime.fromtimestamp(created_ts, tz=timezone.utc)
48
+ if created_ts is not None
49
+ else load_file_creation_date(file.abs_src_path)
50
+ )
51
+
52
+ # updated: timestamp -> datetime
53
+ mtime = updated_data.get(rel_path, os.path.getmtime(file.abs_src_path))
54
+ created_data[rel_path]['updated'] = datetime.fromtimestamp(mtime, tz=timezone.utc)
55
+
56
+ return created_data
57
+
20
58
  def compile_exclude_patterns(exclude_list):
21
59
  if not exclude_list:
22
60
  return []
@@ -36,34 +74,34 @@ def is_excluded(path, patterns):
36
74
  return True
37
75
  return False
38
76
 
39
- def load_file_creation_date(file_path):
77
+ def load_file_creation_date(file_path) -> datetime:
40
78
  try:
41
79
  stat = os.stat(file_path)
42
80
  system = platform.system().lower()
43
81
  if system.startswith('win'): # Windows
44
- return datetime.fromtimestamp(stat.st_ctime)
82
+ return datetime.fromtimestamp(stat.st_ctime, tz=timezone.utc)
45
83
  elif system == 'darwin': # macOS
46
84
  try:
47
- return datetime.fromtimestamp(stat.st_birthtime)
85
+ return datetime.fromtimestamp(stat.st_birthtime, tz=timezone.utc)
48
86
  except AttributeError:
49
- return datetime.fromtimestamp(stat.st_ctime)
87
+ return datetime.fromtimestamp(stat.st_ctime, tz=timezone.utc)
50
88
  else: # Linux, 没有创建时间,使用修改时间
51
- return datetime.fromtimestamp(stat.st_mtime)
89
+ return datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc)
52
90
  except (OSError, ValueError) as e:
53
91
  logger.error(f"Failed to load file creation date for {file_path}: {e}")
54
- return datetime.now()
92
+ return datetime.now(timezone.utc)
55
93
 
56
- def load_git_first_commit_date(file_path):
94
+ def load_git_first_commit_date(file_path) -> datetime:
57
95
  try:
58
96
  # git log --reverse --format="%aI" -- {file_path} | head -n 1
59
- cmd_list = ['git', 'log', '--reverse', '--format=%aI', '--', file_path]
97
+ cmd_list = ['git', 'log', '--reverse', '--format=%at', '--', file_path]
60
98
  process = subprocess.run(cmd_list, capture_output=True, encoding='utf-8')
61
99
  if process.returncode == 0 and process.stdout.strip():
62
- first_line = process.stdout.partition('\n')[0].strip()
63
- return datetime.fromisoformat(first_line)
100
+ first_line = int(process.stdout.partition('\n')[0].strip())
101
+ return datetime.fromtimestamp(first_line, tz=timezone.utc)
64
102
  except Exception as e:
65
103
  logger.info(f"Error load git first commit date for {file_path}: {e}")
66
- return None
104
+ return datetime.now(timezone.utc)
67
105
 
68
106
  def load_git_metadata(docs_dir_path: Path):
69
107
  dates_cache = {}
@@ -74,7 +112,7 @@ def load_git_metadata(docs_dir_path: Path):
74
112
  ).strip())
75
113
  rel_docs_path = docs_dir_path.relative_to(git_root).as_posix()
76
114
 
77
- cmd = ['git', 'log', '--reverse', '--no-merges', '--use-mailmap', '--name-only', '--format=%aN|%aE|%aI', f'--relative={rel_docs_path}', '--', '*.md']
115
+ cmd = ['git', '-c', 'core.quotepath=false', 'log', '--reverse', '--no-merges', '--use-mailmap', '--name-only', '--format=%aN|%aE|%at', f'--relative={rel_docs_path}', '--', '*.md']
78
116
  process = subprocess.run(cmd, cwd=docs_dir_path, capture_output=True, encoding='utf-8')
79
117
  if process.returncode == 0:
80
118
  authors_dict = defaultdict(dict)
@@ -93,7 +131,7 @@ def load_git_metadata(docs_dir_path: Path):
93
131
  # a.巧用 Python 字典的 setdefault 特性来去重(setdefault 为不存在的键提供初始值,不会覆盖已有值)
94
132
  # b.巧用 Python 字典的插入顺序特性来保留内容插入顺序(Python 3.7+ 字典会保持插入顺序)
95
133
  authors_dict[line].setdefault((name, email), None)
96
- first_commit.setdefault(line, created)
134
+ first_commit.setdefault(line, int(created))
97
135
 
98
136
  # 构建最终的缓存数据
99
137
  for file_path in first_commit:
@@ -118,11 +156,11 @@ def load_git_last_updated_dates(docs_dir_path: Path):
118
156
  ).strip())
119
157
  rel_docs_path = docs_dir_path.relative_to(git_root).as_posix()
120
158
 
121
- cmd = ['git', 'log', '--no-merges', '--use-mailmap', '--format=%aN|%aE|%at', '--name-only', f'--relative={rel_docs_path}', '--', '*.md']
159
+ cmd = ['git', '-c', 'core.quotepath=false', 'log', '--no-merges', '--use-mailmap', '--format=%aN|%aE|%at', '--name-only', f'--relative={rel_docs_path}', '--', '*.md']
122
160
  process = subprocess.run(cmd, cwd=docs_dir_path, capture_output=True, encoding='utf-8')
123
161
  if process.returncode == 0:
124
162
  result = subprocess.run(
125
- ["git", "ls-files", "*.md"],
163
+ ['git', '-c', 'core.quotepath=false', 'ls-files', '*.md'],
126
164
  cwd=docs_dir_path, capture_output=True, encoding='utf-8'
127
165
  )
128
166
  # 只记录已跟踪的文件(还有已删除、重命名、不再跟踪)
@@ -134,7 +172,7 @@ def load_git_last_updated_dates(docs_dir_path: Path):
134
172
  if not line:
135
173
  continue
136
174
  if '|' in line:
137
- ts = float(line.split('|')[2])
175
+ ts = int(line.split('|')[2])
138
176
  elif line.endswith('.md') and line in tracked_files and ts:
139
177
  # 只记录第一次出现的文件,即最近一次提交(setdefault 机制不会覆盖已有值)
140
178
  doc_mtime_map.setdefault(line, ts)
@@ -153,14 +191,13 @@ def get_recently_updated_files(existing_dates: dict, files: Files, exclude_list:
153
191
  continue
154
192
  if not file.src_path.endswith('.md'):
155
193
  continue
156
- rel_path = getattr(file, 'src_uri', file.src_path)
157
- if os.sep != '/':
158
- rel_path = rel_path.replace(os.sep, '/')
194
+ rel_path = getattr(file, 'src_uri')
159
195
  if is_excluded(rel_path, exclude_list):
160
196
  continue
161
197
 
162
198
  # 优先从现有数据获取 mtime,如果不存在则 fallback 到文件系统 mtime
163
- mtime = existing_dates.get(rel_path, os.path.getmtime(file.abs_src_path))
199
+ exist_updated: datetime = existing_dates.get(rel_path, {}).get('updated')
200
+ mtime = exist_updated.timestamp() if exist_updated else os.path.getmtime(file.abs_src_path)
164
201
 
165
202
  # 获取文档其它信息
166
203
  title = file.page.title if file.page and file.page.title else file.name
@@ -193,23 +230,17 @@ def get_recently_updated_files(existing_dates: dict, files: Files, exclude_list:
193
230
  "readtime": readtime,
194
231
  "tags": tags,
195
232
  })
196
- # existing_map[rel_path] = mtime
197
233
 
198
234
  # 构建最近更新列表
199
235
  if files_meta:
200
- fmt_full = "%Y-%m-%d %H:%M:%S"
201
- fmt_date = "%Y-%m-%d"
202
236
  # heapq 取 top limit
203
- top_results = heapq.nlargest(limit, files_meta, key=itemgetter("updated_ts"))
204
-
205
- for doc in top_results:
206
- dt = datetime.fromtimestamp(doc["updated_ts"])
237
+ recently_updated_results = heapq.nlargest(limit, files_meta, key=itemgetter("updated_ts"))
207
238
 
208
- recently_updated_results.append({
209
- **doc,
210
- "updated_dt": dt.strftime(fmt_full),
211
- "updated": dt.strftime(fmt_date),
212
- })
239
+ for doc in recently_updated_results:
240
+ # timestamp -> utc datetime -> local datetime
241
+ dt = datetime.fromtimestamp(doc["updated_ts"], tz=timezone.utc).astimezone()
242
+ doc["updated_dt"] = dt.isoformat()
243
+ doc["updated"] = dt.date().isoformat()
213
244
 
214
245
  return recently_updated_results
215
246
 
@@ -223,8 +254,11 @@ def read_jsonl_cache(jsonl_file: Path):
223
254
  entry = json.loads(line.strip())
224
255
  if entry and isinstance(entry, dict) and len(entry) == 1:
225
256
  file_path, file_info = next(iter(entry.items()))
257
+ if isinstance(file_info, dict):
258
+ if file_info.get('created'):
259
+ file_info['created'] = int(datetime.fromisoformat(file_info['created']).timestamp())
226
260
  dates_cache[file_path] = file_info
227
- except (json.JSONDecodeError, StopIteration) as e:
261
+ except (json.JSONDecodeError, StopIteration, ValueError) as e:
228
262
  logger.warning(f"Skipping invalid JSONL line: {e}")
229
263
  except IOError as e:
230
264
  logger.warning(f"Error reading from '.dates_cache.jsonl': {str(e)}")
@@ -237,6 +271,9 @@ def write_jsonl_cache(jsonl_file: Path, dates_cache, tracked_files):
237
271
  with open(temp_file, 'w', encoding='utf-8') as f:
238
272
  for file_path in tracked_files:
239
273
  if file_path in dates_cache:
274
+ file_info = dates_cache[file_path].copy()
275
+ if file_info.get('created') is not None:
276
+ file_info['created'] = datetime.fromtimestamp(file_info['created'], tz=timezone.utc).isoformat()
240
277
  entry = {file_path: dates_cache[file_path]}
241
278
  f.write(json.dumps(entry, ensure_ascii=False) + '\n')
242
279
 
@@ -247,7 +284,7 @@ def write_jsonl_cache(jsonl_file: Path, dates_cache, tracked_files):
247
284
  subprocess.run(["git", "add", str(jsonl_file)], check=True)
248
285
  logger.info(f"Successfully updated JSONL cache file: {jsonl_file}")
249
286
  return True
250
- except (IOError, json.JSONDecodeError) as e:
287
+ except IOError as e:
251
288
  logger.warning(f"Failed to write JSONL cache file {jsonl_file}: {e}")
252
289
  except Exception as e:
253
290
  logger.warning(f"Failed to add JSONL cache file to git: {e}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-document-dates
3
- Version: 3.7.4
3
+ Version: 3.8.0
4
4
  Summary: A new generation MkDocs plugin for displaying exact creation date, last updated date, authors, email of documents
5
5
  Author-email: Aaron Wang <aaronwqt@gmail.com>
6
6
  License-Expression: MIT
@@ -82,8 +82,8 @@ In addition to the above basic configuration, the plug-in also provides a wealth
82
82
  - [Structure and Style](https://jaywhj.netlify.app/document-dates-en#Structure-and-Style): You can freely configure the plugin's display structure in mkdocs.yml or Front Matter. You can quickly set the plugin styles through preset entrances, such as icons, themes, colors, fonts, animations, dividing line and so on
83
83
  - [Template Variables](https://jaywhj.netlify.app/document-dates-en#Template-Variables): Can be used to optimize `sitemap.xml` for site SEO
84
84
  - [Recently Updated Module](https://jaywhj.netlify.app/document-dates-en#Recently-Updated-Module): Enable list of recently updated documents (in descending order of update date), this is ideal for sites with a large number of documents, so that readers can quickly see what's new
85
- - [Localization Language](https://jaywhj.netlify.app/document-dates-en#Localization-Language): More localization languages for `timeago` and `tooltip`
86
- - [Development Stories](https://jaywhj.netlify.app/document-dates-en#Development-Stories): Describes the origin of the plug-in, the difficulties and solutions encountered in development, and the principles and directions of product design
85
+ - [Localization Language](https://jaywhj.netlify.app/document-dates-en#Localization-Language): More localization languages for `timeago` and `tooltip`
86
+ - [Developer API](https://jaywhj.netlify.app/document-dates-en#Developer-API): Provides a date data API for developers, making it easy to retrieve exact dates in other plugins or hooks
87
87
 
88
88
  See the documentation for details: https://jaywhj.netlify.app/document-dates-en
89
89
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mkdocs-document-dates"
7
- version = "3.7.4"
7
+ version = "3.8.0"
8
8
  description = "A new generation MkDocs plugin for displaying exact creation date, last updated date, authors, email of documents"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.7"
@@ -4,7 +4,7 @@ from setuptools import find_packages, setup
4
4
  def legacy_setup():
5
5
  setup(
6
6
  name="mkdocs-document-dates",
7
- version="3.7.4",
7
+ version="3.8.0",
8
8
  author="Aaron Wang",
9
9
  author_email="aaronwqt@gmail.com",
10
10
  license="MIT",