Skip to content

Latest commit

 

History

History
472 lines (379 loc) · 27.1 KB

README.md

File metadata and controls

472 lines (379 loc) · 27.1 KB

tempus.ahk

GitHub Actions Workflow Status GitHub Release

Tempus is a DateTime library for AutoHotkey. It is, essentially, a wrapper to expose the API of the Rust jiff crate in AHK.

So, to know what tempus.ahk is about, is to know what jiff is about:

Jiff is a datetime library for Rust that encourages you to jump into the pit of success. The focus of this library is providing high level datetime primitives that are difficult to misuse and have reasonable performance. Jiff supports automatic and seamless integration with the Time Zone Database, DST aware arithmetic and rounding, formatting and parsing zone aware datetimes losslessly, [...] and a whole lot more.
Jiff takes enormous inspiration from Temporal, which is a TC39 proposal to improve datetime handling in JavaScript.

Right now, only a portion of the API is implemented, but development towards completion is rapidly underway.

Installation

This project has two components: the compiled tempus_ahk.dll and the tempus.ahk script, which is intended to be used via #Include. This script is for AHK v2 only. In principle, the DLL can also be used with AHK v1, but no such script is provided.

From the releases page you can download the compiled tempus_ahk.dll file and tempus.ahk file (or the tempus_ahk.zip containing these). To ensure Dll loading works correctly, you should ensure that tempus_ahk.dll is somewhere on the Dll Library load search path, such as in the working directory, or a directory on PATH. Alternatively, you may provide your own DllLoad directive before #Include tempus.ahk to load the DLL. See DllLoad for more information.

See also: Binary security.

Usage

The exposed AHK API aims to mirror, as much as is reasonable, the API of jiff. Most of the usage is a straightforward translation from the rust API for jiff.

For example, in Rust with jiff:

extern crate jiff;
use jiff::{Timestamp, ToSpan};
let time: Timestamp = "2024-07-11T01:14:00Z".parse().unwrap();
assert_eq!(time.as_second(), 1720660440);

Looks like this with tempus.ahk:

#Include "tempus.ahk"

time := Timestamp.parse("2024-07-11T01:14:00Z")
MsgBox(time.as_second())

Examples

Timestamp

Jiff Timestamp

Timestamp.strptime / Timestamp.as_second

ts := Timestamp.strptime("%F %H:%M %:z", "2024-07-14 21:14 -04:00")
MsgBox(ts.as_second()) ; 1721006040

Timestamp.parse / Timestamp.to_string

ts := Timestamp.parse("2024-01-01T00:00:00Z")
MsgBox(ts.to_string()) ; 2024-01-01T00:00:00Z

Timestamp.strftime / Timestamp.from_second

ts := Timestamp.from_second(86400)
MsgBox(ts.strftime("%a %b %e %I:%M:%S %p UTC %Y")) ; Fri Jan  2 12:00:00 AM UTC 1970

Timestamp.round

round takes three arguments: the rounding unit, the increment, and the rounding mode.

ts := Timestamp.parse("2024-06-20 03:25:01Z")
rounded := ts.round(Unit.Minute, 1, RoundMode.Ceil)
MsgBox(rounded.to_string()) ; 2024-06-20T03:26:00Z

Convenience objects are available for specifying the unit and mode:

Unit := {
    Nanosecond: 0, 
    Microsecond: 1, 
    Millisecond: 2, 
    Second: 3, 
    Minute: 4, 
    Hour: 5, 
    Day: 6, 
    Week: 7, 
    Month: 8, 
    Year: 9
}

RoundMode := {
    Ceil: 1,
    Floor: 2,
    Expand: 3,
    Trunc: 4,
    HalfCeil: 5,
    HalfFloor: 6,
    HalfExpand: 7,
    HalfTrunc: 8,
    HalfEven: 9,
}

Span

Jiff Span

span1 := Span.new().hours(2).minutes(59)
span2 := Span.new().minutes(2)
span3 := span1.checked_add(span2)
MsgBox(span3.to_string()) ; PT3H1M 

span.parse / span.round

span1 := Span.parse("PT23h50m3.123s")
expected := Span.new().hours(24)
rounded := span1.round(Unit.Minute, 30)
expected.eq(rounded) ; true

span.total

span1 := Span.new().hours(3).minutes(10)
MsgBox(span1.total(Unit.Second)) ; 11400.0

Comparisons

The methods eq, gt, lt, gte, and lte can be used to compare two span objects

span1 := Span.new().hours(3)
span2 := Span.new().minutes(180)
if (span1.eq(span2)) { ; true
  MsgBox("They are equal in length")
}

By default, jiff takes into account various factors when comparing spans and does not assume all days are 24 hours. Therefore, when a span's smallest component is days or greater (that is, it includes a calendar component), you either need to associate a relative datetime (Because, for example, 1 month from March 1 is 31 days, but 1 month from April 1 is 30 days.) or (to compare weeks) opt into an assumption/invariant of days being calculated as 24 hours.

for example:

span1 := Span.new().weeks(4)
span2 := Span.new().days(30)

span1.eq(span2) ; error!

But opting into the 24-hour-days invariant (by passing true as the second argument to the compare method) allows this:

span1 := Span.new().weeks(4)
span2 := Span.new().days(30)

; opt into 24-hour-days invariant to allow comparison of days/weeks
span1.gt(span2, true) ; OK!

Support for specifying a relative timeframe is not yet available.

SignedDuration

Jiff SignedDuration

duration := SignedDuration.parse("2h 30m")

Binary Security

This project is distributed, in part, as a DLL file. DLL files are software compiled in binary form. Because these files are not human-readable, it is important that you can trust the authors that produce them and that you can verify the authenticity and integrity of the file you downloaded.

For this project, the DLL binaries in the releases page are digitally signed as part of the GitHub Action where they are built. This digital signature can be used to verify that you are receiving an authentic copy of tempus_ahk.dll that has not been tampered with.

You can view the digital signature by right-clicking the tempus_ahk.dll file, selecting "properties", clicking the "Digital Signatures" tab and locating the digital signature of "Young Enterprise Solutions LLC" whose signing certificate is issued by Microsoft. If you do not see the "Digital Signatures" tab or the signature shows as invalid or is signed by any other entity, that means you do not have an authentic signed copy of the tempus_ahk.dll binary.

Moreover, the releases page also contains the hashes of all release files for each release. These can be used to verify their integrity. We also proactively submit our DLLs to VirusTotal to ensure our files are free of unexpected detections. You can find the links in the releases page.

Alternatively, you may build this binary yourself from source using Rust. See the Building notes below.

Building

Building this project is fairly simple. But I will try to explain the steps for those who may not have prior experience building Rust projects.

It's expected you already have Rust installed (e.g., you can run rustup, cargo, etc.).

Prerequisites:

This project uses the GNU toolchain by default, so you will need this installed and ensure you add the target with rustup:

  • Add the x86_64-pc-windows-gnu target: rustup target add x86_64-pc-windows-gnu
  • Ensure you have the compatible linker on PATH (e.g. you can run x86_64-w64-mingw32-gcc --version to verify this). For example, you can install MSYS2 and have the toolchain bin directory (C:\msys64\mingw64\bin) on PATH. If you don't see files in this directory, you must install the toolchain by opening the mingw bash shell (C:\msys64\Mingw64.exe) and running the command pacman -S --needed base-devel mingw-w64-x86_64-toolchain

Build:

  • run cargo build --release which should produce the DLL located at target/x86_64-pc-windows-gnu/release/tempus_ahk.dll

If you struggle with building on the GNU toolchain with MSYS2, you can build against the default Windows target by running cargo build --target x86_64-pc-windows-msvc. Though note that the produced DLL will have a dependency on vcruntime140.dll, so target machines you run this on will need the VC redistributable package installed (which, in all likelihood, many users already have due to this being a fairly ubiquitous dependency).

API progress

This is mostly for loose reference. Not all methods will be implemented. Not all methods are listed here (especially things like trait impls, arithmetic, comparisons and more). But may give you an idea of what will be available.

Timestamp

Zoned

Span

SignedDuration

(some variants will likely just be implemented once with 64bit precision)

TimeZone

Date

Time

DateTime