@date/@datetime gain :lang (German: "16. Juni 1910") over a document-wide Language state variable, and :number for the numeric form (en 6/16/1910, de 16.06.1910 per DIN 5008). @eval finds Python modules next to the file that names them regardless of the cwd, and the new :cwd option runs an eval in a chosen working directory (@source_file uses it to resolve against the document). Two new test suites ship in tst/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
77 lines
3.0 KiB
Python
77 lines
3.0 KiB
Python
# Implementation of the @date and @datetime klammers (date.k). Each takes
|
|
# the current date and time, offset by the :days argument (positive is
|
|
# future, negative is past), and formats it in the requested language.
|
|
#
|
|
# Language selection is two-level: the klammer's :lang argument overrides
|
|
# the document-wide Language state variable (declared in sks/kutil/kutil.k,
|
|
# default en; set it for a whole document with @@@state Language :value de).
|
|
#
|
|
# :number selects the purely numeric form instead — which is language-
|
|
# specific in order, separator, and padding: en 5/15/1955 (month first,
|
|
# unpadded), de 15.05.1955 (day first, zero-padded per DIN 5008).
|
|
#
|
|
# Languages are OWN TABLES, deliberately not locales: strftime's %B follows
|
|
# the process-wide C locale, which must be generated on the host (the
|
|
# containers ship minimal locale support) and is global mutable state in the
|
|
# embedded interpreter — the same reason @table's :decimal is implemented by
|
|
# character translation. Adding a language is adding one LANGUAGES entry:
|
|
# twelve month names and the date pattern (day/month/year; the day carries
|
|
# no leading zero). The time suffix ", HH:MM" is language-independent.
|
|
|
|
import datetime as dt
|
|
|
|
LANGUAGES = {
|
|
'en': {
|
|
'months': ['January', 'February', 'March', 'April', 'May', 'June',
|
|
'July', 'August', 'September', 'October', 'November',
|
|
'December'],
|
|
'date': '{day} {month} {year}', # 16 June 1910
|
|
'number': '{m}/{day}/{year}', # 6/16/1910
|
|
},
|
|
'de': {
|
|
'months': ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
|
|
'Juli', 'August', 'September', 'Oktober', 'November',
|
|
'Dezember'],
|
|
'date': '{day}. {month} {year}', # 16. Juni 1910
|
|
'number': '{dd}.{mm}.{year}', # 16.06.1910 (DIN 5008)
|
|
},
|
|
}
|
|
|
|
|
|
def offset(days):
|
|
return dt.datetime.now() + dt.timedelta(days=days or 0)
|
|
|
|
|
|
def language(K):
|
|
"""The language table for K: :lang argument, else the Language state
|
|
variable, else English. Returns (table, error_text)."""
|
|
code = getattr(K, 'lang', '') or getattr(K, 'Language', '') or 'en'
|
|
if code not in LANGUAGES:
|
|
return None, ('ERROR: @date/@datetime: unknown language "%s" '
|
|
'(available: %s)'
|
|
% (code, ', '.join(sorted(LANGUAGES))))
|
|
return LANGUAGES[code], None
|
|
|
|
|
|
def format_date(t, lang, number=False):
|
|
pattern = lang['number'] if number else lang['date']
|
|
return pattern.format(day=t.day, m=t.month,
|
|
dd='%02d' % t.day, mm='%02d' % t.month,
|
|
month=lang['months'][t.month - 1],
|
|
year=t.year)
|
|
|
|
|
|
def date(K):
|
|
lang, error = language(K)
|
|
if error:
|
|
return error
|
|
return format_date(offset(K.days), lang, K.number)
|
|
|
|
|
|
def datetime(K):
|
|
lang, error = language(K)
|
|
if error:
|
|
return error
|
|
t = offset(K.days)
|
|
return format_date(t, lang, K.number) + ', %02d:%02d' % (t.hour, t.minute)
|