Files
klammertext/sks/date/date.py
Andy Kopra 61b21d1397 Document language for dates; @eval pathname resolution and :cwd (from dev bc7cd62b6f68)
@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>
2026-07-27 20:25:49 +02:00

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)