ts
— это библиотека даты и времени для Emacs. Он призван быть более удобным, чем такие шаблоны, как (string-to-number (format-time-string "%Y"))
предоставляя простые средства доступа, такие как (ts-year (ts-now))
.
Чтобы повысить производительность (значительно), отформатированные части даты вычисляются лениво, а не при создании экземпляра объекта временной метки, а вычисленные части затем кэшируются для последующего доступа без повторных вычислений. За кулисами это позволяет избежать ненужных вызовов (string-to-number (format-time-string...
, которые на удивление дороги.
Получить части текущей даты:
; ; When the current date is 2018-12-08 23:09:14 -0600:
(ts-year (ts-now)) ; => 2018
(ts-month (ts-now)) ; => 12
(ts-day (ts-now)) ; => 8
(ts-hour (ts-now)) ; => 23
(ts-minute (ts-now)) ; => 9
(ts-second (ts-now)) ; => 14
(ts-tz-offset (ts-now)) ; => "-0600"
(ts-dow (ts-now)) ; => 6
(ts-day-abbr (ts-now)) ; => "Sat"
(ts-day-name (ts-now)) ; => "Saturday"
(ts-month-abbr (ts-now)) ; => "Dec"
(ts-month-name (ts-now)) ; => "December"
(ts-tz-abbr (ts-now)) ; => "CST"
Увеличьте текущую дату:
; ; By 10 years:
( list :now (ts-format)
:future (ts-format (ts-adjust 'year 10 (ts-now))))
; ;=> ( :now "2018-12-15 22:00:34 -0600"
; ; :future "2028-12-15 22:00:34 -0600")
; ; By 10 years, 2 months, 3 days, 5 hours, and 4 seconds:
( list :now (ts-format)
:future (ts-format
(ts-adjust 'year 10 'month 2 'day 3
'hour 5 'second 4
(ts-now))))
; ;=> ( :now "2018-12-15 22:02:31 -0600"
; ; :future "2029-02-19 03:02:35 -0600")
Какой день недели был 2 дня назад?
(ts-day-name (ts-dec 'day 2 (ts-now))) ; => "Thursday"
; ; Or, with threading macros:
( thread-last (ts-now) (ts-dec 'day 2 ) ts-day-name) ; => "Thursday"
(-> > (ts-now) (ts-dec 'day 2 ) ts-day-name) ; => "Thursday"
Получить временную метку для этого времени на прошлой неделе:
(ts-unix (ts-adjust 'day -7 (ts-now)))
; ;=> 1543728398.0
; ; To confirm that the difference really is 7 days:
( / ( - (ts-unix (ts-now))
(ts-unix (ts-adjust 'day -7 (ts-now))))
86400 )
; ;=> 7.000000567521762
; ; Or human-friendly as a list:
(ts-human-duration
(ts-difference (ts-now)
(ts-dec 'day 7 (ts-now))))
; ;=> (:years 0 :days 7 :hours 0 :minutes 0 :seconds 0)
; ; Or as a string:
(ts-human-format-duration
(ts-difference (ts-now)
(ts-dec 'day 7 (ts-now))))
; ;=> "7 days"
; ; Or confirm by formatting:
( list :now (ts-format)
:last-week (ts-format (ts-dec 'day 7 (ts-now))))
; ;=> ( :now "2018-12-08 23:31:37 -0600"
; ; :last-week "2018-12-01 23:31:37 -0600")
Некоторые методы доступа имеют псевдонимы, похожие на конструкторы format-time-string
:
(ts-hour (ts-now)) ; => 0
(ts-H (ts-now)) ; => 0
(ts-minute (ts-now)) ; => 56
(ts-min (ts-now)) ; => 56
(ts-M (ts-now)) ; => 56
(ts-second (ts-now)) ; => 38
(ts-sec (ts-now)) ; => 38
(ts-S (ts-now)) ; => 38
(ts-year (ts-now)) ; => 2018
(ts-Y (ts-now)) ; => 2018
(ts-month (ts-now)) ; => 12
(ts-m (ts-now)) ; => 12
(ts-day (ts-now)) ; => 9
(ts-d (ts-now)) ; => 9
Разберите строку в объект временной метки и переформатируйте ее:
(ts-format (ts-parse " sat dec 8 2018 12:12:12 " )) ; => "2018-12-08 12:12:12 -0600"
; ; With a threading macro:
(-> > " sat dec 8 2018 12:12:12 "
ts-parse
ts-format) ; ;=> "2018-12-08 12:12:12 -0600"
Отформатируйте разницу между двумя метками времени:
(ts-human-format-duration
(ts-difference (ts-now)
(ts-adjust 'day -400
'hour -2 'minute -1 'second -5
(ts-now))))
; ; => "1 years, 35 days, 2 hours, 1 minutes, 5 seconds"
; ; Abbreviated:
(ts-human-format-duration
(ts-difference (ts-now)
(ts-adjust 'day -400
'hour -2 'minute -1 'second -5
(ts-now)))
'abbr )
; ; => "1y35d2h1m5s"
Проанализируйте элемент временной метки организации непосредственно из org-element-context
и найдите разницу между ним и текущим моментом:
( with-temp-buffer
( org-mode )
( save-excursion
( insert " <2015-09-24 Thu .+1d> " ))
(ts-human-format-duration
(ts-difference (ts-now)
(ts-parse-org-element ( org-element-context )))))
; ;=> "3 years, 308 days, 2 hours, 24 minutes, 21 seconds"
Проанализируйте строку временной метки организации (которая имеет повторитель) и отформатируйте год и месяц:
; ; Note the use of `format' rather than `concat' , because `ts-year'
; ; returns the year as a number rather than a string.
( let* ((ts (ts-parse-org " <2015-09-24 Thu .+1d> " )))
( format " %s , %s " (ts-month-name ts) (ts-year ts)))
; ;=> "September, 2015"
; ; Or, using dash.el:
(-- > (ts-parse-org " <2015-09-24 Thu .+1d> " )
( format " %s , %s " (ts-month-name it) (ts-year it)))
; ;=> "September, 2015"
; ; Or, if you remember the format specifiers:
(ts-format " %B, %Y " (ts-parse-org " <2015-09-24 Thu .+1d> " ))
; ;=> "September, 2015"
Как давно была эта дата в 1970 году?
( let* ((now (ts-now))
(then (ts-apply :year 1970 now)))
( list (ts-format then)
(ts-human-format-duration
(ts-difference now then))))
; ;=> ("1970-08-04 07:07:10 -0500"
; ; "49 years, 12 days")
Как давно началась эпоха?
(ts-human-format-duration
(ts-diff (ts-now) (make-ts :unix 0 )))
; ;=> "49 years, 227 days, 12 hours, 12 minutes, 30 seconds"
В каком из последних 100 лет Рождество приходилось на субботу?
( let ((ts (ts-parse " 2019-12-25 " ))
(limit ( - (ts-year (ts-now)) 100 )))
( cl-loop while ( >= (ts-year ts) limit)
when ( string= " Saturday " (ts-day-name ts))
collect (ts-year ts)
do (ts-decf (ts-year ts))))
; ;=> (2010 2004 1999 1993 1982 1976 1971 1965 1954 1948 1943 1937 1926 1920)
Более интересный пример: относится ли временная метка к предыдущей календарной неделе?
; ; First, define a function to return the range of the previous calendar week.
( defun last-week-range ()
" Return timestamps (BEG . END) spanning the previous calendar week. "
( let* ( ; ; Bind `now' to the current timestamp to ensure all calculations
; ; begin from the same timestamp. (In the unlikely event that
; ; the execution of this code spanned from one day into the next,
; ; that would cause a wrong result.)
(now (ts-now))
; ; We start by calculating the offsets for the beginning and
; ; ending timestamps using the current day of the week. Note
; ; that the `ts-dow' slot uses the "%w" format specifier, which
; ; counts from Sunday to Saturday as a number from 0 to 6.
(adjust-beg-day ( - ( + 7 (ts-dow now))))
(adjust-end-day ( - ( - 7 ( - 6 (ts-dow now)))))
; ; Make beginning/end timestamps based on `now' , with adjusted
; ; day and hour/minute/second values. These functions return
; ; new timestamps, so `now' is unchanged.
(beg ( thread-last now
; ; `ts-adjust' makes relative adjustments to timestamps.
(ts-adjust 'day adjust-beg-day)
; ; `ts-apply' applies absolute values to timestamps.
(ts-apply :hour 0 :minute 0 :second 0 )))
(end ( thread-last now
(ts-adjust 'day adjust-end-day)
(ts-apply :hour 23 :minute 59 :second 59 ))))
( cons beg end)))
(-let* ( ; ; Bind the default format string for `ts-format' , so the
; ; results are easy to understand.
(ts-default-format " %a, %Y-%m-%d %H:%M:%S %z " )
; ; Get the timestamp for 3 days before now.
(check-ts (ts-adjust 'day -3 (ts-now)))
; ; Get the range for the previous week from the function we defined.
((beg . end) (last-week-range)))
( list :last-week-beg (ts-format beg)
:check-ts (ts-format check-ts)
:last-week-end (ts-format end)
:in-range-p (ts-in beg end check-ts)))
; ;=> (:last-week-beg "Sun, 2019-08-04 00:00:00 -0500"
; ; :check-ts "Fri, 2019-08-09 10:00:34 -0500"
; ; :last-week-end "Sat, 2019-08-10 23:59:59 -0500"
; ; :in-range-p t)
ts-B (STRUCT)
ts
struct STRUCT
.ts-H (STRUCT)
ts
struct STRUCT
.ts-M (STRUCT)
ts
struct STRUCT
.ts-S (STRUCT)
ts
struct STRUCT
.ts-Y (STRUCT)
ts
STRUCT
.ts-b (STRUCT)
ts
STRUCT
.ts-d (STRUCT)
ts
struct STRUCT
.ts-day (STRUCT)
ts
struct STRUCT
.ts-day-abbr (STRUCT)
ts
STRUCT
.ts-day-name (STRUCT)
ts
struct STRUCT
.ts-day-of-month-num (STRUCT)
ts
struct STRUCT
.ts-day-of-week-abbr (STRUCT)
ts
STRUCT
.ts-day-of-week-name (STRUCT)
ts
struct STRUCT
.ts-day-of-week-num (STRUCT)
ts
STRUCT
.ts-day-of-year (STRUCT)
ts
STRUCT
.ts-dom (STRUCT)
ts
struct STRUCT
.ts-dow (STRUCT)
ts
STRUCT
.ts-doy (STRUCT)
ts
STRUCT
.ts-hour (STRUCT)
ts
struct STRUCT
.ts-internal (STRUCT)
ts
struct STRUCT
. Slot представляет собой внутреннее значение времени Emacs (например, возвращаемое current-time
).ts-m (STRUCT)
ts
struct STRUCT
.ts-min (STRUCT)
ts
struct STRUCT
.ts-minute (STRUCT)
ts
struct STRUCT
.ts-month (STRUCT)
ts
struct STRUCT
.ts-month-abbr (STRUCT)
ts
STRUCT
.ts-month-name (STRUCT)
ts
struct STRUCT
.ts-month-num (STRUCT)
ts
struct STRUCT
.ts-moy (STRUCT)
ts
struct STRUCT
.ts-sec (STRUCT)
ts
struct STRUCT
.ts-second (STRUCT)
ts
struct STRUCT
.ts-tz-abbr (STRUCT)
ts
STRUCT
.ts-tz-offset (STRUCT)
ts
STRUCT
.ts-unix (STRUCT)
ts
STRUCT
.ts-week (STRUCT)
ts
struct STRUCT
.ts-week-of-year (STRUCT)
ts
struct STRUCT
.ts-woy (STRUCT)
ts
struct STRUCT
.ts-year (STRUCT)
ts
STRUCT
. ts-apply (&rest SLOTS TS)
TS
с новыми значениями слотов. Заполните слоты временных меток, перезапишите заданные значения слотов и верните новую временную метку со значением временной метки Unix, полученным из новых значений слотов. SLOTS
— это список чередующихся пар ключ-значение, подобный тому, который передается в make-ts
.ts-adjust (&rest ADJUSTMENTS)
ADJUSTMENTS
к TS
. ADJUSTMENTS
должны представлять собой серию чередующихся SLOTS
и VALUES
с помощью которых можно их корректировать. Например, эта форма возвращает новую временную метку, которая будет на 47 часов вперед: (ts-adjust 'hour -1 'day +2 (ts-now))
Поскольку аргумент отметки времени является последним, его можно использовать в макросе потоков.
ts-dec (SLOT VALUE TS)
TS
со значением SLOT
уменьшенным на VALUE
. SLOT
следует указывать как простой символ, а не как ключевое слово.ts-inc (SLOT VALUE TS)
TS
со значением SLOT
, увеличенным на VALUE
. SLOT
следует указывать как простой символ, а не как ключевое слово.ts-update (TS)
TS
после обновления временной метки Unix из других слотов. Неразрушающий. Используется после настройки слотов, например, ts-fill
.Разрушительный
ts-adjustf (TS &rest ADJUSTMENTS)
TS
после применения ADJUSTMENTS
. Эта функция является деструктивной, поскольку она вызывает setf
для TS
. ADJUSTMENTS
должны представлять собой серию чередующихся SLOTS
и VALUES
с помощью которых можно их корректировать. Например, в этой форме временная метка изменяется на 47 часов вперед:
(let ((ts (ts-now))) (ts-adjustf ts 'hour -1 'day +2))
ts-decf (PLACE &optional (VALUE 1))
PLACE
на VALUE
(по умолчанию 1), обновите временную метку Unix и верните новое значение PLACE
.ts-incf (PLACE &optional (VALUE 1))
PLACE
на VALUE
(по умолчанию 1), обновите временную метку Unix и верните новое значение PLACE
. ts-in (BEG END TS)
TS
находится в диапазоне BEG
до END
включительно. Все аргументы должны быть структурами ts
.ts< (AB)
A
меньше временной метки B
ts<= (AB)
A
<= метка времени B
.ts= (AB)
A
совпадает с временной меткой B
Сравнивает только слоты unix
временных меток. Обратите внимание, что слот Unix временной метки является плавающим и может отличаться менее чем на одну секунду, что приводит к их неравным значениям, даже если все отформатированные части временной метки одинаковы.ts> (AB)
A
больше, чем временная метка B
ts>= (AB)
A
>= метка времени B
. ts-human-duration (SECONDS)
SECONDS
в годах, днях, часах, минутах и секундах. Это простой расчет, не учитывающий високосные годы, високосные секунды и т. д.ts-human-format-duration (SECONDS &optional ABBREVIATE)
SECONDS
. Если SECONDS
меньше 1, возвращается "0 seconds"
. Если ABBREVIATE
не равно нулю, верните более короткую версию без пробелов. Это простой расчет, не учитывающий високосные годы, високосные секунды и т. д. ts-format (&optional TS-OR-FORMAT-STRING TS)
format-time-string
. Если TS-OR-FORMAT-STRING
— это временная метка или ноль, используйте значение ts-default-format
. Если оба TS-OR-FORMAT-STRING
и TS
равны нулю, используйте текущее время. ts-parse (STRING)
ts
, анализируя STRING
с помощью parse-time-string
.ts-parse-fill (FILL STRING)
ts
, анализируя STRING
с помощью parse-time-string
. Пустые значения часов/минут/секунд заполняются в соответствии с FILL
: если begin
, то с 0; если end
, час заполняется цифрой 23, а минута/секунда — цифрой 59; если ноль, может быть сигнализирована ошибка, когда значения времени пусты. Обратите внимание, что когда FILL
имеет значение end
, значение времени типа «12:12» заполняется до «12:12:00», а не до «12:12:59».ts-parse-org (ORG-TS-STRING)
ORG-TS-STRING
. Обратите внимание, что вызывается функция org-parse-time-string
, которую следует загрузить перед вызовом этой функции.ts-parse-org-fill (FILL ORG-TS-STRING)
ORG-TS-STRING
. Обратите внимание, что вызывается функция org-parse-time-string
, которую следует загрузить перед вызовом этой функции. Значения часов/минут/секунд заполняются в соответствии с FILL
: если begin
, то с 0; если end
, час заполняется значением 23, а минута/секунда — значением 59. Обратите внимание, что org-parse-time-string
не поддерживает временные метки, содержащие секунды.ts-parse-org-element (ELEMENT)
ELEMENT
. Элемент должен быть похож на элемент, проанализированный org-element
, первым элементом которого является timestamp
. Предполагается, что временная метка не является диапазоном. copy-ts (TS)
TS
.ts-difference (AB)
A
и B
ts-diff
ts-difference
.ts-fill (TS &optional ZONE)
TS
заполнив все слоты из своей временной метки Unix. Это неразрушающее действие. ZONE
передается в format-time-string
, который см.ts-now
ts
установленную сейчас.ts-p (STRUCT)
ts-reset (TS)
TS
со всеми очищенными слотами, кроме unix
. Неразрушающий. То же, что: (make-ts :unix (ts-unix ts))
ts-defstruct (&rest ARGS)
cl-defstruct
, но с дополнительными опциями слота.Дополнительные параметры и значения слота:
:accessor-init
: сексп, который инициализирует слот в средстве доступа, если слот равен нулю. struct
символа будет привязана к текущей структуре. Аксессор определяется после того, как структура полностью определена, поэтому он может ссылаться на определение структуры (например, с помощью макроса cl-struct
pcase
).
:aliases
: A
символов, которые будут иметь псевдоним для средства доступа к слоту, с добавлением имени структуры (например, структура ts
с year
слота и псевдонимом y
создаст псевдоним ts-y
).
ts-human-format-duration
против format-seconds
В Emacs есть встроенная функция format-seconds
, которая выдает выходные данные, аналогичные выводам ts-human-format-duration
. Его вывод также можно контролировать с помощью строки формата. Однако, если вывод ts-human-format-duration
достаточен, он работает намного лучше, чем format-seconds
. Этот простой тест, выполненный 100 000 раз, показывает, что он работает намного быстрее и генерирует меньше мусора:
(bench-multi-lexical :times 100000
:forms (( " ts-human-format-duration " (ts-human-format-duration 15780.910933971405 t ))
( " format-seconds " ( format-seconds " %yy%dd%hh%mm%ss%z " 15780.910933971405 ))))
Форма | x быстрее, чем следующий | Общее время работы | Количество GC | Общее время работы сборщика мусора |
---|---|---|---|---|
ts-человеческий-формат-длительность | 5,82 | 0,832945 | 3 | 0,574929 |
формат секунд | самый медленный | 4,848253 | 17 | 3,288799 |
(См. «Руководство разработчика пакетов Emacs» для ознакомления с многолексическим макросом bench-multi-lexical
.)
Добавлен
ts-fill
принимает аргумент ZONE
, подобный тому, который передается в format-time-string
, который см. Зафиксированный
ts-human-format-duration
возвращал пустую строку длительностью менее 1 секунды (теперь возвращает "0 seconds"
). Добавлен
ts-parse-fill
и ts-parse-org-fill
.ts-in
.Измененный
ts-now
больше не является встроенной. Это позволяет изменять его во время выполнения, например, с помощью cl-letf
, что полезно при тестировании.Зафиксированный
ts-fill
. (Функция split-string
, которая вызывается в нем, изменяет данные совпадения.)ts-parse-org
. (Функция org-parse-time-string
, которая вызывается в ней, изменяет данные соответствия.)Документация
Первый отмеченный релиз. Опубликовано в MELPA.
лицензия GPLv3