From 536cef56ab74e13572a8761fd39136fa41c260c1 Mon Sep 17 00:00:00 2001 From: Daniel Hunsaker Date: Tue, 17 Jul 2018 05:15:24 -0600 Subject: [PATCH] Migrate documentation to ReadTheDocs And make some (minor) improvements in the process. --- .gitignore | 1 + .travis.yml | 2 +- README.md | 420 ++---------- boxfile.yml | 6 + calendars/stardate.go | 1 + calends.go | 10 +- docs/.static/.gitkeep | 0 docs/.templates/.gitkeep | 0 docs/Makefile | 20 + docs/c/custom_calendars.rst | 331 +++++++++ docs/c/installation.rst | 32 + docs/c/usage.rst | 634 ++++++++++++++++++ docs/conf.py | 194 ++++++ docs/contributions.rst | 24 + docs/features.rst | 118 ++++ docs/go/custom_calendars.rst | 322 +++++++++ docs/go/installation.rst | 13 + docs/go/usage.rst | 512 ++++++++++++++ docs/index.rst | 97 +++ docs/installation.rst | 17 + docs/introduction.rst | 63 ++ docs/make.bat | 36 + docs/php/custom_calendars.rst | 292 ++++++++ docs/php/installation.rst | 46 ++ docs/php/usage.rst | 478 +++++++++++++ docs/systems.rst | 48 ++ docs/systems/gregorian.rst | 26 + docs/systems/jdc.rst | 36 + docs/systems/stardate.rst | 109 +++ docs/systems/tai64.rst | 42 ++ docs/systems/unix.rst | 27 + docs/usage.rst | 19 + libcalends/calendars.go | 7 +- libcalends/calendars_tests.go | 13 +- libcalends/calends_exports.go | 1 + libcalends/php/calends/calends.zep | 45 +- libcalends/php/config.json | 5 + libcalends/php/ext/tests/053.serializing.phpt | 45 ++ requirements.txt | 35 + 39 files changed, 3715 insertions(+), 412 deletions(-) create mode 100644 docs/.static/.gitkeep create mode 100644 docs/.templates/.gitkeep create mode 100644 docs/Makefile create mode 100644 docs/c/custom_calendars.rst create mode 100644 docs/c/installation.rst create mode 100644 docs/c/usage.rst create mode 100644 docs/conf.py create mode 100644 docs/contributions.rst create mode 100644 docs/features.rst create mode 100644 docs/go/custom_calendars.rst create mode 100644 docs/go/installation.rst create mode 100644 docs/go/usage.rst create mode 100644 docs/index.rst create mode 100644 docs/installation.rst create mode 100644 docs/introduction.rst create mode 100644 docs/make.bat create mode 100644 docs/php/custom_calendars.rst create mode 100644 docs/php/installation.rst create mode 100644 docs/php/usage.rst create mode 100644 docs/systems.rst create mode 100644 docs/systems/gregorian.rst create mode 100644 docs/systems/jdc.rst create mode 100644 docs/systems/stardate.rst create mode 100644 docs/systems/tai64.rst create mode 100644 docs/systems/unix.rst create mode 100644 docs/usage.rst create mode 100644 libcalends/php/ext/tests/053.serializing.phpt create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index 47675f6..2aabe44 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ build/ core* !core*.sh *~ +.build/ diff --git a/.travis.yml b/.travis.yml index 88e96da..2a6365e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -97,7 +97,7 @@ after_success: cd ${TRAVIS_BUILD_DIR} ./build-all go get github.com/aktau/github-release - github-release info --user danhunsaker --repo calends --tag ${TRAVIS_TAG} || + github-release info --user danhunsaker --repo calends --tag ${TRAVIS_TAG} || \ github-release release --user danhunsaker --repo calends --tag ${TRAVIS_TAG} --draft for archive in $(find dist/ -iname '*.tgz') do diff --git a/README.md b/README.md index 1dddf99..c93f3fa 100644 --- a/README.md +++ b/README.md @@ -1,120 +1,54 @@ # Calends -[![Software License](https://img.shields.io/github/license/danhunsaker/calends.svg?style=flat-square)](LICENSE) -[![Gitter](https://img.shields.io/gitter/room/danhunsaker/calends.svg?style=flat-square)](https://gitter.im/danhunsaker/calends) -[![Go Report Card](https://goreportcard.com/badge/github.com/danhunsaker/calends?style=flat-square)](https://goreportcard.com/report/github.com/danhunsaker/calends) -[![GoDoc Reference](https://img.shields.io/badge/GoDoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/danhunsaker/calends) - -[![Latest Stable Version](https://img.shields.io/github/release/danhunsaker/calends.svg?label=stable&style=flat-square)](https://github.com/danhunsaker/calends/releases) -[![Latest Unstable Version](https://img.shields.io/github/release/danhunsaker/calends/all.svg?label=unstable&style=flat-square)](https://github.com/danhunsaker/calends/releases) -[![Build Status](https://img.shields.io/travis/danhunsaker/calends.svg?style=flat-square)](https://travis-ci.org/danhunsaker/calends) -[![Codecov](https://img.shields.io/codecov/c/github/danhunsaker/calends.svg?style=flat-square)](https://codecov.io/gh/danhunsaker/calends) -[![Scrutinizer](https://img.shields.io/scrutinizer/g/danhunsaker/calends.svg?style=flat-square)](https://scrutinizer-ci.com/g/danhunsaker/calends/) +[![Software License](https://img.shields.io/github/license/danhunsaker/calends.svg?style=for-the-badge)](LICENSE) +[![Main Docs](https://img.shields.io/readthedocs/calends.svg?label=main+docs&style=for-the-badge)](https://calends.readthedocs.io/) +[![GoDoc Reference](https://img.shields.io/badge/GoDoc-reference-brightgreen.svg?style=flat-square)](https://godoc.org/github.com/danhunsaker/calends) +[![Gitter Chat](https://img.shields.io/gitter/room/danhunsaker/calends.svg?style=flat-square)](https://gitter.im/danhunsaker/calends) [![Total Downloads](https://img.shields.io/github/downloads/danhunsaker/calends/total.svg?style=flat-square)](https://github.com/danhunsaker/calends/releases) -A library for handling dates and times across arbitrary calendar systems - -## Features - -- [x] Large range and high precision. +[![Latest Stable Version](https://img.shields.io/github/release/danhunsaker/calends.svg?style=for-the-badge)](https://github.com/danhunsaker/calends/releases) +[![GitHub Release Date](https://img.shields.io/github/release-date/danhunsaker/calends.svg?style=for-the-badge)](https://github.com/danhunsaker/calends) +[![Github commits (since latest release)](https://img.shields.io/github/commits-since/danhunsaker/calends/latest.svg?style=flat-square)](https://github.com/danhunsaker/calends) +[![GitHub last commit](https://img.shields.io/github/last-commit/danhunsaker/calends.svg?style=flat-square)](https://github.com/danhunsaker/calends) - Calends understands dates 262 seconds into the future or past, in - units as small as 10-45 seconds – that's over 146 billion years - into the past or future (146 138 512 313 years, 169 days, 10 hours, 5 minutes, - and 28 seconds from CE 1970 Jan 01 00:00:00 TAI, Gregorian), at resolutions - smaller than Planck Time (54x10−45 seconds, and the smallest - meaningful duration even on the quantum scale). That encompasses well beyond - the expected lifespan of the Universe, at resolutions enough to represent - quantum events. - -- [x] Supports parsing, formatting, and calculating offsets between date (and - time) values in multiple calendar systems. +[![Maintenance Status](https://img.shields.io/maintenance/yes/2018.svg?style=flat-square)](https://github.com/danhunsaker/calends) +[![Travis Build Status](https://img.shields.io/travis/danhunsaker/calends.svg?style=flat-square)](https://travis-ci.org/danhunsaker/calends) +[![Codecov coverage](https://img.shields.io/codecov/c/github/danhunsaker/calends.svg?style=flat-square)](https://codecov.io/gh/danhunsaker/calends) +[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/danhunsaker/calends.svg?style=flat-square)](https://scrutinizer-ci.com/g/danhunsaker/calends/) +[![Go Report Card](https://goreportcard.com/badge/github.com/danhunsaker/calends?style=flat-square)](https://goreportcard.com/report/github.com/danhunsaker/calends) +[![Libraries.io Dependency Check](https://img.shields.io/librariesio/github/danhunsaker/calends.svg?style=flat-square)](https://libraries.io/github/danhunsaker/calends) - Supported out of the box are the following (all systems are proleptic - \[extrapolated beyond the officially-defined limits] unless specified - otherwise): +A library for handling dates and times across arbitrary calendar systems - - [x] **Unix time**: A count of the number of seconds since CE 1970 Jan 01 - 00:00:00 UTC +## Features - - [x] **[TAI64][]**: Essentially Unix time plus 262, but using TAI - seconds instead of UTC seconds, so times can be converted unambiguously (UTC - uses leap seconds to keep the solar zenith at noon, while TAI is a simple, - unadjusted count). Calends supports an extended version of this spec, with - three more components, to encode out to 45 places instead of just 18; this - is also actually the internal time scale used by Calends itself, which is - how it can support such a broad range of dates at such a high resolution. +More information about each of these features is available in [the full +documentation][full]. +- [x] Large range and high precision +- [x] Supports date (and time) values in multiple calendar systems: + - [x] **Unix time** + - [x] **[TAI64][]** - [x] Automatic calculation of leap second offsets - - [ ] Estimation of undefined past and future leap second insertions - - [ ] Automatic updates for handling leap second insertions - - - [x] **Gregorian**: The current international standard calendar system - + - [x] **Gregorian** - [ ] Disconnect from native `time.Time` implementation, and its limitations - - - [ ] **Julian**: The previous version of the Gregorian calendar - - - [x] **Julian Day Count**: A count of days since BCE 4713 Jan 01 12:00:00 UTC - on the proleptic Julian Calendar - - - [ ] **Hebrew**: ... - - - [ ] **Persian**: ... - - - [ ] **Chinese**: Several variants - - - [ ] **Meso-American**: Commonly called Mayan, but used by several cultures - in the region - - - [ ] **Discordian**: ... - - - [x] **Stardate**: Yes, the ones from Star Trek™; several variants exist - -- [x] Encodes both time spans (`start != end`, `duration != 0`) and instants - (`start == end`, `duration == 0`) in a single interface. - - The library treats the time values it encodes as `[start, end)` sets (that is, - the `start` point is included in the range, as is every point between `start` - and `end`, but the `end` point itself is _not_ included in the range). This - allows `duration` to accurately be `end - start` in all cases. (And yes, that - also means you can create spans with `duration < 0`.) - -- [x] Supports calculations and comparisons on spans and instants. - - Addition, subtraction, intersection, combination, gap calculation, overlap - detection, and similar operations are all supported directly on Calends - values. - -- [ ] Conversion to/from native date/time types. - - While this is possible by using a string representation as an intermediary, in - either direction, some data and precision is lost in such a conversion. - Instead, Calends supports conversion to and from such types directly, - preserving as much data and accuracy as each native type provides. - -- [ ] Geo-temporally aware. - - The library provides methods for passing a location instead of a calendar - system, and selecting an appropriate calendar based on which was most common - in that location at that point in time. _(Some guess work is involved in this - process when parsing dates, so it is still preferred to supply the calendar - system, if known, when parsing.)_ - -- [ ] Time zone support. - -- [x] Well-defined interfaces for extending the library. - - Add more calendar systems, type conversions, or geo-temporal relationships - without forking/modifying the library itself. - -- [x] Shared library (`.so`/`.dll`). - - In order to use the library outside of Golang projects, we first need to - export its functionality in a shared library, which can then be accessed from - other programming evironments and applications, generally via FFI. + - [ ] **Julian** + - [x] **Julian Day Count** + - [ ] **Hebrew** + - [ ] **Persian** + - [ ] **Chinese** + - [ ] **Meso-American** + - [ ] **Discordian** + - [x] **Stardate** +- [x] Encodes both time spans and instants in a single interface +- [x] Supports calculations and comparisons on spans and instants +- [ ] Conversion to/from native date/time types +- [ ] Geo-temporally aware +- [ ] Time zone support +- [x] Well-defined interfaces for extending the library +- [x] Shared library (`.so`/`.dll`) ## Installation @@ -123,281 +57,17 @@ The steps here will vary based on which programming language(s) you're using. For Golang, simply run `go get github.com/danhunsaker/calends`, and then place `"github.com/danhunsaker/calends"` in the `import` wherever you intend use it. -Other languages will use Calends through a language-specific wrapper around the -compiled shared library. So for PHP, as an example, you'd install the `calends` -extension, probably via PECL. +For other languages, refer to [the full documentation][full]. ## Usage -Calends exposes a very small handful of things for use outside the library -itself. One is the `Calends` class, which should be the only interface users of -the library ever need to touch. Another is the `TAI64NAXURTime` class, used to -store and manipulate the instants of time which make up a `Calends` instance. -The rest are interfaces for extending the library's functionality. - -`Calends` objects are immutable - all methods return a new `Calends` object -where they might otherwise alter the current one. This is true even of the -`Set*()` methods. This has the side effect of using more memory to perform -manipulations than updating values on an existing object would. It makes many -operations safer, though, than mutable objects would allow. - -Language-specific documentation is available, and may give a more concrete idea -of how to use Calends in a given language/environment, but the general usage -information given here should be valid for all of them. - -### Create - -- `calends.Create(value, calendar, format)` - - Creates a new `Calends` object, using `calendar` to select a calendar system, - and `format` to parse the contents of `value` into the `Calends` object's - internal instants. The contents of `value` can vary based on the calendar - system itself, but generally speaking can always be a string. In any case, the - value can always be a string→value map (associative array, hash, or whatever - your language of choice prefers to call it), where the keys are any two of - `start`, `end`, and `duration`. If all three are provided, `duration` is - ignored in favor of calculating it directly. If only one is provided, `value` - is passed to the calendar system itself unchanged. The calendar system then - converts `value` to a `TAI64NAXURTime` instant, which the `Calends` - object sets to the appropriate internal value. - -### Read - -- `c.Date(calendar, format)` / `c.EndDate(calendar, format)` - - Retrieves the start or end date of the `Calends` object `c` as a string. The - value is generated by the calendar system given in `calendar`, according to - the format string in `format`. - -- `c.Duration()` - - Retrieves the duration of the `Calends` object `c` as an arbitrary-precision - floating point number. This value will be `0` if the `Calends` object is an - instant. - -### Update +Usage data has been moved to [the full documentation][full]. -- `c.SetDate(value, calendar, format)` / `c.SetEndDate(value, calendar, format)` - - Sets the start or end date of a `Calends` object, based on the `Calends` - object `c`. The inputs are the same as for `Create()`, above, except the - string→value map option isn't available, since you're already specifically - setting the start or end value explicitly, depending on which method you call. - -- `c.SetDuration(duration, calendar)` / `c.SetDurationFromEnd(duration, calendar)` - - Sets the duration of a `Calends` object, adjusting the end or start point - accordingly, based on the `Calends` object `c`. The `duration` value is - interpreted by the calendar system given in `calendar`, so is subject to any - of its rules. `SetDurationFromEnd()` will adjust the start point, using the - end as the anchor for the duration. - -### Manipulate - -- `c.Add(offset, calendar)` / `c.AddFromEnd(offset, calendar)` - - Increases the corresponding date in the `Calends` object `c` by `offset`, as - interpreted by the calendar system given in `calendar`. - -- `c.Subtract(offset, calendar)` / `c.SubtractFromEnd(offset, calendar)` - - Works the same as `Add()` / `AddFromEnd()`, except it decreases the - corresponding date, rather than increasing it. - -- `c.Next(offset, calendar)` / `c.Previous(offset, calendar)` - - Returns a `Calends` object of `offset` duration (as interpreted by the - calendar system given in `calendar`), which abuts the `Calends` object `c`. If - `offset` is empty, `calendar` is ignored, and the duration of `c` is used - instead. - -### Combine - -- `c1.Merge(c2)` - - Returns a `Calends` object spanning from the earliest start date to the latest - end date between `Calends` objects `c1` and `c2`. - -- `c1.Intersect(c2)` - - Returns a `Calends` object spanning the overlap between `Calends` objects `c1` - and `c2`. If `c1` and `c2` don't overlap, returns an error. - -- `c1.Gap(c2)` - - Returns a `Calends` object spanning the gap between `Calends` objects `c1` and - `c2`. If `c1` and `c2` overlap (and there is, therefore, no gap to return), - returns an error. - -### Compare - -- `c1.Difference(c2, mode)` - - Returns the difference of `Calends` object `c1` minus `c2`, using `mode` to - select which values to use in the calculation. Valid `mode`s include: - - - `start` - use the start date of both `c1` and `c2` - - `duration` - use the duration of both `c1` and `c2` - - `end` - use the end date of both `c1` and `c2` - - `start-end` - use the start of `c1`, and the end of `c2` - - `end-start` - use the end of `c1`, and the start of `c2` - -- `c1.Compare(c2, mode)` - - Returns `-1` if `Calends` object `c1` is less than `Calends` object `c2`, `0` - if they are equal, and `1` if `c1` is greater than `c2`, using `mode` to - select which values to use in the comparison. Valid modes are the same as for - `Difference()`, above. - -- `c1.Contains(c2)` - - Returns a boolean value indicating whether `Calends` object `c1` contains all - of `Calends` object `c2`. - -- `c1.Overlaps(c2)` - - Returns a boolean value indicating whether `Calends` object `c1` overlaps with - `Calends` object `c2`. - -- `c1.Abuts(c2)` - - Returns a boolean value indicating whether `Calends` object `c1` abuts - `Calends` object `c2` (that is, whether one begins at the same instant the - other ends). - -- `c1.IsSame(c2)` - - Returns a boolean value indicating whether `Calends` object `c1` covers the - same span of time as `Calends` object `c2`. - -- `c1.IsShorter(c2)` / `c1.IsSameDuration(c2)` / `c1.IsLonger(c2)` - - Returns a boolean comparing the duration of `Calends` objects `c1` and `c2`. - -- `c1.IsBefore(c2)` / `c1.StartsBefore(c2)` / `c1.EndsBefore(c2)` - - Returns a boolean comparing `Calends` object `c1` with the start date of - `Calends` object `c2`. `IsBefore` compares the entirety of `c1` with `c2`; - `StartsBefore` compares only the start date of `c1`; `EndsBefore` compares - only the end date of `c1`. - -- `c1.IsDuring(c2)` / `c1.StartsDuring(c2)` / `c1.EndsDuring(c2)` - - Returns a boolean indicating whether `Calends` object `c1` lies between the - start and end dates of `Calends` object `c2`. `IsDuring` compares the entirety - of `c1` with `c2`; `StartsDuring` compares only the start date of `c1`; - `EndsDuring` compares only the end date of `c1`. - -- `c1.IsAfter(c2)` / `c1.StartsAfter(c2)` / `c1.EndsAfter(c2)` - - Returns a boolean comparing `Calends` object `c1` with the end date of - `Calends` object `c2`. `IsAfter` compares the entirety of `c1` with `c2`; - `StartsAfter` compares only the start date of `c1`; `EndsAfter` compares only - the end date of `c1`. - -### Calendar Systems +## Calendar Systems Currently supported calendar systems, and the options available for each, are -listed below. Formats in **bold** are the default format for that calendar. - -- `tai64` - - Formats - - Calends `TAI64NAXURTime` object _(input only)_ - - string with TAI instant representation in one of the following layouts: - - `decimal` - number of seconds since 1970.01.01 00:00:00 TAI - - `tai64` - TAI64 External Representation; the hexadecimal version of - `decimal` plus 262, with no fractional seconds (16 hexits - total) - - `tai64n` - TAI64N External Representation; `tai64` with 9 decimal places - encoded as 8 additional hexadecimal digits (24 hexits total) - - `tai64na` - TAI64NA External Representation; `tai64n` with 9 more - decimal places (18 total) encoded as 8 additional hexadecimal digits (32 - hexits total) - - `tai64nax` - TAI64NAX External Representation; `tai64na` with 9 more - decimal places (27 total) encoded as 8 additional hexadecimal digits (40 - hexits total) - - `tai64naxu` - TAI64NAXU External Representation; `tai64nax` with 9 more - decimal places (36 total) encoded as 8 additional hexadecimal digits (48 - hexits total) - - **`tai64naxur` - TAI64NAXUR External Representation; `tai64naxu` with 9 - more decimal places (45 total) encoded as 8 additional hexadecimal - digits (56 hexits total)** - - Offsets - - Calends `TAI64NAXURTime` object - - arbitrary-precision floating point number of seconds - - string with `decimal` format layout (above) - -- `unix` - - Formats - - **number of seconds since 1970-01-01 00:00:00 UTC** - - input can be integer or float, in either numeric or string types - - output uses Golang `fmt.Print()` conventions - - Offsets - - number of seconds - - same input formatting as above - -- `gregorian` - - Formats - - Golang `time.Time` object _(input only)_ - - Golang `time` package format strings (**RFC1123 layout**) - - C-style `strptime()`/`strftime()` format strings - - Offsets - - Golang `time.Duration` object - - string with Gregorian time units - - must be relative times - - use full words instead of abbreviations for time units (such as - `seconds` instead of just `s`) - -- `jdc` - - Formats - - `full` - the full, canonical Day Count - - `fullday` - the full Day Count, without the fractional time part - - `fulltime` - just the fractional time part of the full Day Count - - **`modified` - an abbreviated Day Count, 2400000.5 less than the full - (starts at midnight instead of noon)** - - `day` - the modified Day Count, without the fractional time part - - `time` - just the fractional time part of the modified Day Count - - Offsets - - number of days, as integer or float, via numeric or string types - - can include fractional days to indicate time - -- `stardate` - - Formats - - **`main` - A combined variant, which supports both TOS and TNG style - stardates. It does this by subdividing the system into short eras called - "issues", and letting the issue number become negative instead of the - stardate itself, as needed. This is arguably the most widely-used system, - being the one Google uses in their Calendar and other projects. Devised by - Andrew Main.** - - `kennedy` - The other combined variant, which uses a single continuously- - increasing value instead of `main`'s "issues". Devised by Richie Kennedy. - - `pugh90s` - A TNG-focused variant devised by Steve Pugh. The fractional - component of negative stardates is reversed, such that `-15129.99999` is - followed by `-15128.00000`. - - `pughfixed` - A revision of `pugh90s` which uses a fixed year length. - - `schmidt` - Another TNG-focused variant devised by Andreas Schmidt and - Graham Kennedy. Virtually identical to `pugh90s`, but the fractional - component increases normally for negative stardates. - - `guide-equiv`, `guide-tng`, `guide-tos` - The current variants described - on the TrekGuide page covering stardates. `guide-equiv` provides a - stardate intended for out-of-universe dates, while `guide-tng` and - `guide-tos` both provide stardates for their respective eras of the - franchise. - - `guide-oldtng`, `guide-oldtos` - Older variants pulled from archived - versions of the TrekGuide page. (NOTE: `guide-oldtos` is not currently - implemented.) - - `aldrich` - A simplified variant for stardates that end up being very - close to `schmidt`'s. Devised by John C. Aldrich. - - `red-dragon` - The variant used in the Red Dragon Inn's stardate - calculator, which is intended solely for use with its own roleplaying - forums and related activities. - - `sto-hynes`, `sto-academy`, `sto-tom`, `sto-anthodev` - A group of - variants all aimed at calculating stardates for use within Star - Trek™ Online. The most accurate is probably `sto-hynes`, but this - assumption is untested. - - Offsets - - Must be provided as `"{stardate} {variant}"` - (or `"{variant} {stardate}"`). +listed in [the full documentation][full]. Also provided there are the docs for +how to add your own. ## Contributing @@ -409,8 +79,10 @@ and not all changes are desired. Report all security-related issues to [dan (dot) hunsaker (plus) calends (at) gmail](mailto:dan.hunsaker+calends@gmail.com), and use PGP or GPG protections on -your message. Security issues will be addressed internally before making any +your message (the account's key is `44806AB9`, or you can look it up by the +email address). Security issues will be addressed internally before making any vulnerability announcements. [GitHub]:https://github.com/danhunsaker/calends [TAI64]:http://cr.yp.to/libtai/tai64.html +[full]:https://calends.readthedocs.io/ diff --git a/boxfile.yml b/boxfile.yml index 1beac90..0182249 100644 --- a/boxfile.yml +++ b/boxfile.yml @@ -3,12 +3,14 @@ run.config: engine.config: runtime: php-7.1 extensions: + - json - xml - zephir_parser extra_packages: - go - autoconf - re2c + - py27-pip cache_dirs: - .gopath extra_path_dirs: @@ -23,6 +25,10 @@ run.config: ln -sfT /app /app/.gopath/src/github.com/danhunsaker/calends cd /app/.gopath/src/github.com/danhunsaker/calends go get -t -v ./... + - | + # Set up Sphinx documentation support + pip install --upgrade pip + pip install -I -r requirements.txt - | # Install Zephir cd /data diff --git a/calendars/stardate.go b/calendars/stardate.go index bc442e7..df5609c 100644 --- a/calendars/stardate.go +++ b/calendars/stardate.go @@ -94,6 +94,7 @@ Supported Format Strings: this one was attributed to Major Tom, and hosted as a Wolfram Alpha widget. - sto-anthodev - Another STO variant, hosted on GitHub. + */ package calendars diff --git a/calends.go b/calends.go index 6ba8be4..5602a85 100644 --- a/calends.go +++ b/calends.go @@ -305,7 +305,7 @@ func (c *Calends) UnmarshalText(text []byte) error { "end": endTime, }, "tai64", "tai64naxur") - c = &tmp + *c = tmp return err } @@ -338,17 +338,17 @@ func (c *Calends) UnmarshalJSON(text []byte) error { if err == nil { err = startTime.UnmarshalText([]byte(parsed["start"])) if err != nil { - return errors.New("(*Calends).UnmarshalJSON [convert start time " + parsed["start"] + "]: " + err.Error()) + return errors.New("JSON decode failure while parsing start time [" + parsed["start"] + "]: " + err.Error()) } err = endTime.UnmarshalText([]byte(parsed["end"])) if err != nil { - return errors.New("(*Calends).UnmarshalJSON [convert end time " + parsed["end"] + "]: " + err.Error()) + return errors.New("JSON decode failure while parsing end time [" + parsed["end"] + "]: " + err.Error()) } } else { err = startTime.UnmarshalText([]byte(strings.Trim(string(text), `"`))) if err != nil { - return errors.New("(*Calends).UnmarshalJSON [convert time " + strings.Trim(string(text), `"`) + "]: " + err.Error()) + return errors.New("JSON decode failure while parsing time [" + strings.Trim(string(text), `"`) + "]: " + err.Error()) } endTime = startTime @@ -362,7 +362,7 @@ func (c *Calends) UnmarshalJSON(text []byte) error { *c = temp if err != nil { - err = errors.New("(*Calends).UnmarshalJSON [set values]: " + err.Error()) + err = errors.New("JSON decode failure while setting values: " + err.Error()) } return err diff --git a/docs/.static/.gitkeep b/docs/.static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/.templates/.gitkeep b/docs/.templates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..8c241ff --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = Calends +SOURCEDIR = . +BUILDDIR = .build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/c/custom_calendars.rst b/docs/c/custom_calendars.rst new file mode 100644 index 0000000..31d07e9 --- /dev/null +++ b/docs/c/custom_calendars.rst @@ -0,0 +1,331 @@ +.. _custom-calendars-c: + +.. index:: + pair: custom calendars; C/C++ + +Custom Calendars in C/C++ +========================= + +Adding new calendars to Calends is a fairly straightforward process. Implement a +handful of functions, and then simply pass them to the registration function. + +Define +------ + +The functions in question look like this: + +.. c:function:: TAI64Time Calends_calendar_to_internal_string(char* calendar, char* date, char* format) + TAI64Time Calends_calendar_to_internal_long_long(char* calendar, long long int date, char* format) + TAI64Time Calends_calendar_to_internal_double(char* calendar, double date, char* format) + TAI64Time Calends_calendar_to_internal_tai(char* calendar, TAI64Time date) + + :param calendar: The name of the target calendar system. + :type calendar: :c:type:`char*` + :param date: The input date. + :type date: :c:type:`char*` or :c:type:`long long int` or :c:type:`double` + or :c:type:`TAI64Time` + :param format: The format string for parsing the input date. + :type format: :c:type:`char*` + :return: The parsed internal timestamp. + :rtype: :c:type:`TAI64Time` + + Converts an input date/time representation to an internal + :c:type:`TAI64Time`. + +.. c:function:: char* Calends_calendar_from_internal(char* calendar, TAI64Time stamp, char* format) + + :param calendar: The name of the target calendar system. + :type calendar: :c:type:`char*` + :param stamp: The internal timestamp value. + :type stamp: :c:type:`TAI64Time` + :param format: The format string for formatting the output date. + :type format: :c:type:`char*` + :return: The formatted date/time. + :rtype: :c:type:`char*` + + Converts an internal :c:type:`TAI64Time` to a date/time string. + +.. c:function:: TAI64Time Calends_calendar_offset_string(char* calendar, TAI64Time stamp, char* offset) + TAI64Time Calends_calendar_offset_long_long(char* calendar, TAI64Time stamp, long long int offset) + TAI64Time Calends_calendar_offset_double(char* calendar, TAI64Time stamp, double offset) + TAI64Time Calends_calendar_offset_tai(char* calendar, TAI64Time stamp, TAI64Time offset) + + :param calendar: The name of the target calendar system. + :type calendar: :c:type:`char*` + :param stamp: The internal timestamp value. + :type stamp: :c:type:`TAI64Time` + :param offset: The input offset. + :type offset: :c:type:`char*` or :c:type:`long long int` or :c:type:`double` + or :c:type:`TAI64Time` + :return: The adjusted internal timestamp. + :rtype: :c:type:`TAI64Time` + + Adds the given offset to an internal :c:type:`TAI64Time`. + +Registration +------------ + +Register +:::::::: + +Once it is registered with the library, your calendar system can be used from +anywhere in your application. To register a system, pass it to the following +function: + +.. c:function:: void Calends_calendar_register(char* name, char* defaultFormat, Calends_calendar_to_internal_string() to_internal_string, Calends_calendar_to_internal_long_long() to_internal_long_long, Calends_calendar_to_internal_double() to_internal_double, Calends_calendar_to_internal_tai() to_internal_tai, Calends_calendar_from_internal() from_internal, Calends_calendar_offset_string() offset_string, Calends_calendar_offset_long_long() offset_long_long, Calends_calendar_offset_double() offset_double, Calends_calendar_offset_tai() offset_tai) + + :param name: The name to register the calendar system under. + :type name: :c:type:`char*` + :param defaultFormat: The default format string. + :type defaultFormat: :c:type:`char*` + :param to_internal_string: The calendar parser, for :c:type:`char*` input. + :type to_internal_string: :c:func:`Calends_calendar_to_internal_string` + :param to_internal_long_long: The calendar parser, for ``long long int`` + input. + :type to_internal_long_long: :c:func:`Calends_calendar_to_internal_long_long` + :param to_internal_double: The calendar parser, for :c:type:`double` input. + :type to_internal_double: :c:func:`Calends_calendar_to_internal_double` + :param to_internal_tai: The calendar parser, for :c:type:`TAI64Time` input. + :type to_internal_tai: :c:func:`Calends_calendar_to_internal_tai` + :param from_internal: The calendar formatter. + :type from_internal: :c:func:`Calends_calendar_from_internal` + :param offset_string: The calendar offset calculator, for :c:type:`char*` + input. + :type offset_string: :c:func:`Calends_calendar_offset_string` + :param offset_long_long: The calendar offset calculator, for ``long long + int`` input. + :type offset_long_long: :c:func:`Calends_calendar_offset_long_long` + :param offset_double: The calendar offset calculator, for :c:type:`double` + input. + :type offset_double: :c:func:`Calends_calendar_offset_double` + :param offset_tai: The calendar offset calculator, for :c:type:`TAI64Time` + input. + :type offset_tai: :c:func:`Calends_calendar_offset_tai` + + Registers a calendar system class, storing the collected functions as + ``name``, and saving ``defaultFormat`` for later use while parsing or + formatting. + +Unregister +:::::::::: + +.. c:function:: void Calends_calendar_unregister(char* name) + + :param name: The name of the calendar system to remove. + :type name: :c:type:`char*` + + Removes a calendar system from the callback list. + +Check and List +:::::::::::::: + +.. c:function:: bool Calends_calendar_registered(char* name) + + :param name: The calendar system name to check for. + :type name: :c:type:`char*` + :return: Whether or not the calendar system is currently registered. + :rtype: :c:type:`bool` + + Returns whether or not a calendar system has been registered, yet. + +.. c:function:: char* Calends_calendar_list_registered() + + :return: The sorted list of calendar systems currently registered. + :rtype: :c:type:`char*` + + Returns the list of calendar systems currently registered. + +Types and Values +---------------- + +Now we get to the inner workings that make calendar systems function – even the +built-in ones. The majority of the "magic" comes from the :c:type:`TAI64Time` +struct itself, as a reliable way of storing the exact instants being calculated, +and the only way times are handled by the library itself. A handful of functions +provide basic operations that calendar system developers can use to simplify +their conversions (adding and subtracting the values of other timestamps, and +importing/exporting timestamp values from/to other types, in particular), and a +couple of helpers exclusively handle adding and removing UTC leap second +offsets. As long as you can convert your dates to/from Unix timestamps in a +:c:type:`char*`, :c:type:`long long int`, or :c:type:`double`, the rest is +handled entirely by these helpers in the library itself. + +.. c:type:: TAI64Time + + Stores a ``TAI64NAXUR`` instant in a reliable, easy-converted format. Each + 9-digit fractional segment is stored in a separate 32-bit integer to preserve + its value with a very high degree of accuracy, without having to rely on + string parsing or external arbitrary-precision math libraries. + + .. c:member:: long long int seconds + + Seconds since ``CE 1970-01-01 00:00:00 TAI`` + + .. c:member:: unsigned int nano + + Nanoseconds since the given second + + .. c:member:: unsigned int atto + + Attoseconds since the given nanosecond + + .. c:member:: unsigned int xicto + + Xictoseconds since the given attosecond + + .. c:member:: unsigned int ucto + + Uctoseconds since the given xictosecond + + .. c:member:: unsigned int rocto + + Roctoseconds since the given uctosecond + + .. c:member:: unsigned int padding + + Unused, except to round the value out to the nearest 64 bits + +Calculations +------------ + +.. c:function:: TAI64Time TAI64Time_add(TAI64Time t, TAI64Time z) + + :param t: The current timestamp. + :type t: :c:type:`TAI64Time` + :param z: The timestamp to add to the current one. + :type z: :c:type:`TAI64Time` + :return: The sum of the two timestamps. + :rtype: :c:type:`TAI64Time` + + Calculates the sum of two :c:type:`TAI64Time` values. + +.. c:function:: TAI64Time TAI64Time_sub(TAI64Time t, TAI64Time z) + + :param t: The current timestamp. + :type t: :c:type:`TAI64Time` + :param z: The timestamp to subtract from the current one. + :type z: :c:type:`TAI64Time` + :return: The difference of the two timestamps. + :rtype: :c:type:`TAI64Time` + + Calculates the difference of two :c:type:`TAI64Time` values. + +Export +------ + +.. c:function:: char* TAI64Time_string(TAI64Time t) + + :param t: The current timestamp. + :type t: :c:type:`TAI64Time` + :return: The decimal string representation of the current timestamp. + :rtype: :c:type:`char*` + + Returns the decimal string representation of a :c:type:`TAI64Time` value. + +.. c:function:: TAI64Time TAI64Time_from_string(char* in) + + :param in: The decimal string representation of a timestamp to calculate. + :type in: :c:type:`char*` + :return: The calculated timestamp. + :rtype: :c:type:`TAI64Time` + + Calculates a :c:type:`TAI64Time` from its decimal string representation. + +.. c:function:: char* TAI64Time_hex_string(TAI64Time t) + + :param t: The current timestamp. + :type t: :c:type:`TAI64Time` + :return: The hexadecimal string representation of the current timestamp. + :rtype: :c:type:`char*` + + Returns the hexadecimal string representation of a :c:type:`TAI64Time` value. + +.. c:function:: TAI64Time TAI64Time_from_hex_string(char* in) + + :param in: The hexadecimal string representation of a timestamp to calculate. + :type in: :c:type:`char*` + :return: The calculated timestamp. + :rtype: :c:type:`TAI64Time` + + Calculates a :c:type:`TAI64Time` from its hexadecimal string representation. + +.. c:function:: double TAI64Time_double(TAI64Time t) + + :param t: The current timestamp. + :type t: :c:type:`TAI64Time` + :return: The arbitrary-precision floating point representation of the + current timestamp. + :rtype: :c:type:`double` + + Returns the :c:type:`double` representation of a :c:type:`TAI64Time` value. + +.. c:function:: TAI64Time TAI64Time_from_double(double in) + + :param in: The arbitrary-precision floating point representation of a + timestamp to calculate. + :type in: :c:type:`double` + :return: The calculated timestamp. + :rtype: :c:type:`TAI64Time` + + Calculates a :c:type:`TAI64Time` from its :c:type:`double` representation. + +.. c:function:: char* TAI64Time_encode_text(TAI64Time t) + + :param t: The current timestamp. + :type t: :c:type:`TAI64Time` + :return: A string containing the encoded text. + :rtype: :c:type:`char*` + + Encodes a :c:type:`TAI64Time` value as text. + +.. c:function:: TAI64Time TAI64Time_decode_text(char* in) + + :param in: A string containing the encoded text. + :type in: :c:type:`char*` + :return: The decoded timestamp. + :rtype: :c:type:`TAI64Time` + + Decodes a :c:type:`TAI64Time` value from text. + +.. c:function:: void* TAI64Time_encode_binary(TAI64Time t, int *len) + + :param t: The current timestamp. + :type t: :c:type:`TAI64Time` + :param len: Will return the length of the binary data. + :type len: :c:type:`int*` + :return: A pointer to the encoded binary data stream. + :rtype: :c:type:`void*` + + Encodes a :c:type:`TAI64Time` value as a binary data stream. + +.. c:function:: TAI64Time TAI64Time_decode_binary(void* in, int len) + + :param in: A pointer to the encoded binary data stream. + :type in: :c:type:`void*` + :param len: The length of the binary data. + :type len: :c:type:`int` + :return: The decoded timestamp. + :rtype: :c:type:`TAI64Time` + + Decodes a :c:type:`TAI64Time` value from a binary data stream. + +Helpers +------- + +.. c:function:: TAI64Time TAI64Time_utc_to_tai(TAI64Time utc) + + :param utc: The timestamp to remove the UTC offset from. + :type utc: :c:type:`TAI64Time` + :return: The calculated timestamp. + :rtype: :c:type:`TAI64Time` + + Removes the UTC leap second offset from a :c:type:`TAI64Time` value. + +.. c:function:: TAI64Time TAI64Time_tai_to_utc(TAI64Time tai) + + :param tai: The timestamp to add the UTC offset to. + :type tai: :c:type:`TAI64Time` + :return: The calculated timestamp. + :rtype: :c:type:`TAI64Time` + + Adds the UTC leap second offset to a :c:type:`TAI64Time` value. diff --git a/docs/c/installation.rst b/docs/c/installation.rst new file mode 100644 index 0000000..79c5269 --- /dev/null +++ b/docs/c/installation.rst @@ -0,0 +1,32 @@ +.. _installation-c: + +.. index:: + pair: installation; C/C++ + +Installing Calends for C/C++ +============================ + +Binary Install +-------------- + +For use with C/C++, simply grab the latest version of ``libcalends`` from the +`GitHub Releases page `_, and +extract its contents wherever your compiler expects to find ``.h`` and +``.so``/``.dll`` files. Be sure to grab the correct version for your +architecture! + +Source Install +-------------- + +To install from source, you'll need Golang installed to use its compiler. Clone the repository, build ``libcalends``, then copy the resulting ``.so``/``.dll`` and ``.h`` files to wherever your C/C++ compiler expects to find them. + +.. code-block:: bash + + # Sample Linux steps: + mkdir -p $GOPATH/src/github.com/danhunsaker + cd $GOPATH/src/github.com/danhunsaker + git clone https://github.com/danhunsaker/calends + cd calends/libcalends + go build -v -i -buildmode=c-shared -o libcalends.so + +Adjust the above example commands as needed for your actual development OS. diff --git a/docs/c/usage.rst b/docs/c/usage.rst new file mode 100644 index 0000000..2015474 --- /dev/null +++ b/docs/c/usage.rst @@ -0,0 +1,634 @@ +.. _usage-c: + +.. index:: + pair: usage; C/C++ + +Using Calends in C/C++ +====================== + +Calends exposes a very small handful of things for use outside the library +itself. Documented here are the parts most users will interact with. + +Calends also exposes functions and types for extending the library's +functionality. These are covered in the :ref:`Custom Calendars in C/C++ +` section. + +.. note:: + + Because C doesn't support objects, the library returns an identifier instead + of an actual ``Calends`` value, keeping a reference to the internal + ``Calends`` object in the Golang portions of the process space. This + identifier is specific to the process in which it is generated, and is + therefore only useful within that process itself. To save the value for + later, use one of the marshalling functions documented below, and then the + corresponding unmarshalling function to retrieve it elsewhere. + +Create +------ + +.. c:function:: long long unsigned int Calends_create_string(char* value, char* calendar, char* format) + long long unsigned int Calends_create_string_range(char* start, char* end, char* calendar, char* format) + long long unsigned int Calends_create_string_start_period(char* start, char* duration, char* calendar, char* format) + long long unsigned int Calends_create_string_end_period(char* duration, char* end, char* calendar, char* format) + long long unsigned int Calends_create_long_long(long long int value, char* calendar, char* format) + long long unsigned int Calends_create_long_long_range(long long int start, long long int end, char* calendar, char* format) + long long unsigned int Calends_create_long_long_start_period(long long int start, long long int duration, char* calendar, char* format) + long long unsigned int Calends_create_long_long_end_period(long long int duration, long long int end, char* calendar, char* format) + long long unsigned int Calends_create_double(double value, char* calendar, char* format) + long long unsigned int Calends_create_double_range(double start, double end, char* calendar, char* format) + long long unsigned int Calends_create_double_start_period(double start, double duration, char* calendar, char* format) + long long unsigned int Calends_create_double_end_period(double duration, double end, char* calendar, char* format) + + :param value: The value to parse the date/time from. + :type value: :c:type:`char*` or :c:type:`long long int` or :c:type:`double` + :param start: The value to parse the start date/time from. + :type start: :c:type:`char*` or :c:type:`long long int` or :c:type:`double` + :param duration: The value to parse the duration from. + :type duration: :c:type:`char*` or :c:type:`long long int` or + :c:type:`double` + :param end: The value to parse the end date/time from. + :type end: :c:type:`char*` or :c:type:`long long int` or :c:type:`double` + :param calendar: The calendar system to parse the date(s)/time(s) with. + :type calendar: :c:type:`char*` + :param format: The format the date(s)/time(s) is/are expected to use. + :type format: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Creates a new ``Calends`` object identifier, using :c:data:`calendar` to + select a calendar system, and :c:data:`format` to parse the contents of + :c:data:`value`, :c:data:`start`, :c:data:`end`, and/or :c:data:`duration` + into the ``Calends`` object's internal instants. + +Read +---- + +.. c:function:: char* Calends_date(long long unsigned int c, char* calendar, char* format) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param calendar: The calendar system to format the date/time with. + :type calendar: :c:type:`char*` + :param format: The format the date/time is expected to be in. + :type format: :c:type:`char*` + :return: The start date of the ``Calends`` object + :rtype: :c:type:`char*` + + Retrieves the start date of the ``Calends`` object as a string. The value is + generated by the calendar system given in :c:data:`calendar`, according to + the format string in :c:data:`format`. + +.. c:function:: char* Calends_end_date(long long unsigned int c, char* calendar, char* format) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param calendar: The calendar system to format the date/time with. + :type calendar: :c:type:`char*` + :param format: The format the date/time is expected to be in. + :type format: :c:type:`char*` + :return: The end date of the ``Calends`` object + :rtype: :c:type:`char*` + + Retrieves the end date of the ``Calends`` object as a string. The value is + generated by the calendar system given in :c:data:`calendar`, according to + the format string in :c:data:`format`. + +.. c:function:: char* Calends_duration(long long unsigned int c) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :return: The duration of the ``Calends`` object + :rtype: :c:type:`char*` + + Retrieves the duration of the ``Calends`` object as a string. This value will + be ``0`` if the ``Calends`` object is an instant. + +Update +------ + +.. c:function:: long long unsigned int Calends_with_date_string(long long unsigned int c, char* value, char* calendar, char* format) + long long unsigned int Calends_with_date_long_long(long long unsigned int c, long long int value, char* calendar, char* format) + long long unsigned int Calends_with_date_double(long long unsigned int c, double value, char* calendar, char* format) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param value: The value to parse the date/time from. + :type value: :c:type:`char*` or :c:type:`long long int` or :c:type:`double` + :param calendar: The calendar system to parse the date/time with. + :type calendar: :c:type:`char*` + :param format: The format the date/time is expected to use. + :type format: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Sets the start date of a ``Calends`` object, based on the ``Calends`` + object's current value. The inputs are the same as for the + :c:func:`!Calends_create_{type}` functions, above. + +.. c:function:: long long unsigned int Calends_with_end_date_string(long long unsigned int c, char* value, char* calendar, char* format) + long long unsigned int Calends_with_end_date_long_long(long long unsigned int c, long long int value, char* calendar, char* format) + long long unsigned int Calends_with_end_date_double(long long unsigned int c, double value, char* calendar, char* format) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param value: The value to parse the date/time from. + :type value: :c:type:`char*` or :c:type:`long long int` or :c:type:`double` + :param calendar: The calendar system to parse the date/time with. + :type calendar: :c:type:`char*` + :param format: The format the date/time is expected to use. + :type format: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Sets the end date of a ``Calends`` object, based on the ``Calends`` object's + current value. The inputs are the same as for the + :c:func:`!Calends_create_{type}` functions, above. + +.. c:function:: long long unsigned int Calends_with_duration_string(long long unsigned int c, char* duration, char* calendar) + long long unsigned int Calends_with_duration_long_long(long long unsigned int c, long long int duration, char* calendar) + long long unsigned int Calends_with_duration_double(long long unsigned int c, double duration, char* calendar) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param duration: The value to parse the new duration from. + :type duration: :c:type:`char*` or :c:type:`long long int` or + :c:type:`double` + :param calendar: The calendar system to parse the date/time with. + :type calendar: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Sets the duration of a ``Calends`` object, by adjusting its end point, and + using the current start point as an anchor. The :c:data:`duration` value is + interpreted by the calendar system given in :c:data:`calendar`, so is subject + to any of its rules. + +.. c:function:: long long unsigned int Calends_with_duration_from_end_string(long long unsigned int c, char* duration, char* calendar) + long long unsigned int Calends_with_duration_from_end_long_long(long long unsigned int c, long long int duration, char* calendar) + long long unsigned int Calends_with_duration_from_end_double(long long unsigned int c, double duration, char* calendar) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param duration: The value to parse the new duration from. + :type duration: :c:type:`char*` or :c:type:`long long int` or + :c:type:`double` + :param calendar: The calendar system to parse the date/time with. + :type calendar: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Sets the duration of a ``Calends`` object, by adjusting its start point, and + using the current end point as an anchor. The :c:data:`duration` value is + interpreted by the calendar system given in :c:data:`calendar`, so is subject + to any of its rules. + +Destroy +------- + +.. c:function:: void Calends_release(long long unsigned int c) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + + Releases an internal ``Calends`` object, freeing its associated memory. Since + the memory used in this case is kept within the Golang portion of the process + space, we don't have access to free that memory using more conventional C/C++ + methods, so this function offers that functionality instead. + +Manipulate +---------- + +.. c:function:: long long unsigned int Calends_add_string(long long unsigned int c, char* offset, char* calendar) + long long unsigned int Calends_add_long_long(long long unsigned int c, long long int offset, char* calendar) + long long unsigned int Calends_add_double(long long unsigned int c, double offset, char* calendar) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param offset: The value to parse the offset from. + :type offset: :c:type:`char*` + :param calendar: The calendar system to parse the date/time with. + :type calendar: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Increases the end date of the ``Calends`` object's current value by + :c:data:`offset`, as interpreted by the calendar system given in + :c:data:`calendar`. + +.. c:function:: long long unsigned int Calends_add_from_end_string(long long unsigned int c, char* offset, char* calendar) + long long unsigned int Calends_add_from_end_long_long(long long unsigned int c, long long int offset, char* calendar) + long long unsigned int Calends_add_from_end_double(long long unsigned int c, double offset, char* calendar) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param offset: The value to parse the offset from. + :type offset: :c:type:`char*` + :param calendar: The calendar system to parse the date/time with. + :type calendar: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Increases the start date of the ``Calends`` object's current value by + :c:data:`offset`, as interpreted by the calendar system given in + :c:data:`calendar`. + +.. c:function:: long long unsigned int Calends_subtract_string(long long unsigned int c, char* offset, char* calendar) + long long unsigned int Calends_subtract_long_long(long long unsigned int c, long long int offset, char* calendar) + long long unsigned int Calends_subtract_double(long long unsigned int c, double offset, char* calendar) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param offset: The value to parse the offset from. + :type offset: :c:type:`char*` + :param calendar: The calendar system to parse the date/time with. + :type calendar: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Works the same as :c:func:`!Calends_add_{type}`, except it decreases the + start date, rather than increasing it. + +.. c:function:: long long unsigned int Calends_subtract_from_end_string(long long unsigned int c, char* offset, char* calendar) + long long unsigned int Calends_subtract_from_end_long_long(long long unsigned int c, long long int offset, char* calendar) + long long unsigned int Calends_subtract_from_end_double(long long unsigned int c, double offset, char* calendar) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param offset: The value to parse the offset from. + :type offset: :c:type:`char*` + :param calendar: The calendar system to parse the date/time with. + :type calendar: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Works the same as :c:func:`!Calends_add_from_end_{type}`, except it decreases + the end date, rather than increasing it. + +.. c:function:: long long unsigned int Calends_next_string(long long unsigned int c, char* offset, char* calendar) + long long unsigned int Calends_next_long_long(long long unsigned int c, long long int offset, char* calendar) + long long unsigned int Calends_next_double(long long unsigned int c, double offset, char* calendar) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param offset: The value to parse the offset from. + :type offset: :c:type:`char*` + :param calendar: The calendar system to parse the date/time with. + :type calendar: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Returns the identifier of a ``Calends`` object of :c:data:`offset` duration + (as interpreted by the calendar system given in :c:data:`calendar`), which + abuts the current ``Calends`` object's start value. If :c:data:`offset` is + empty, :c:data:`calendar` is ignored, and the current object's duration is + used instead. + +.. c:function:: long long unsigned int Calends_previous_string(long long unsigned int c, char* offset, char* calendar) + long long unsigned int Calends_previous_long_long(long long unsigned int c, long long int offset, char* calendar) + long long unsigned int Calends_previous_double(long long unsigned int c, double offset, char* calendar) + + :param c: The identifier of the current ``Calends`` object. + :type c: :c:type:`long long unsigned int` + :param offset: The value to parse the offset from. + :type offset: :c:type:`char*` + :param calendar: The calendar system to parse the date/time with. + :type calendar: :c:type:`char*` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Returns the identifier of a ``Calends`` object of :c:data:`offset` duration + (as interpreted by the calendar system given in :c:data:`calendar`), which + abuts the current ``Calends`` object's end value. If :c:data:`offset` is + empty, :c:data:`calendar` is ignored, and the current object's duration is + used instead. + +Combine +------- + +.. c:function:: long long unsigned int Calends_merge(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to merge. + :type c2: :c:type:`long long unsigned int` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Returns a ``Calends`` object spanning from the earliest start date to the + latest end date between :c:data:`c1` and :c:data:`c2`. + +.. c:function:: long long unsigned int Calends_intersect(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to intersect. + :type c2: :c:type:`long long unsigned int` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Returns a ``Calends`` object spanning the overlap between :c:data:`c1` and + :c:data:`c2`. If :c:data:`c1` and :c:data:`c2` don't overlap, returns an + error. + +.. c:function:: long long unsigned int Calends_gap(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to gap. + :type c2: :c:type:`long long unsigned int` + :return: A new ``Calends`` object identifier + :rtype: :c:type:`long long unsigned int` + + Returns a ``Calends`` object spanning the gap between :c:data:`c1` and + :c:data:`c2`. If :c:data:`c1` and :c:data:`c2` overlap (and there is, + therefore, no gap to return), returns an error. + +Compare +------- + +.. c:function:: char* Calends_difference(long long unsigned int c1, long long unsigned int c2, char* mode) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :param mode: The comparison mode. + :type mode: :c:type:`char*` + :return: The difference, as a decimal string + :rtype: :c:type:`char*` + + Returns the difference of :c:data:`c1` minus :c:data:`c2`, using + :c:data:`mode` to select which values to use in the calculation. Valid + :c:data:`mode`\ s include: + + - ``"start"`` - use the start date of both :c:data:`c1` and :c:data:`c2` + - ``"duration"`` - use the duration of both :c:data:`c1` and :c:data:`c2` + - ``"end"`` - use the end date of both :c:data:`c1` and :c:data:`c2` + - ``"start-end"`` - use the start of :c:data:`c1`, and the end of + :c:data:`c2` + - ``"end-start"`` - use the end of :c:data:`c1`, and the start of + :c:data:`c2` + +.. c:function:: signed char Calends_compare(long long unsigned int c1, long long unsigned int c2, char* mode) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :param mode: The comparison mode. + :type mode: :c:type:`char*` + :return: A standard comparison result + :rtype: :c:data:`signed char` + + Returns ``-1`` if :c:data:`c1` is less than :c:data:`c2`, ``0`` if they are + equal, and ``1`` if :c:data:`c1` is greater than :c:data:`c2`, using + :c:data:`mode` to select which values to use in the comparison. Valid + :c:data:`mode`\ s are the same as for :c:func:`Calends_difference`, above. + +.. c:function:: signed char Calends_contains(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Checks whether :c:data:`c1` contains all of :c:data:`c2`. + +.. c:function:: signed char Calends_overlaps(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Checks whether :c:data:`c1` overlaps with :c:data:`c2`. + +.. c:function:: signed char Calends_abuts(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Checks whether :c:data:`c1` abuts :c:data:`c2` (that is, whether one begins + at the same instant the other ends). + +.. c:function:: signed char Calends_is_same(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Checks whether :c:data:`c1` covers the same span of time as :c:data:`c2`. + +.. c:function:: signed char Calends_is_shorter(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Compares the duration of :c:data:`c1` and :c:data:`c2`. + +.. c:function:: signed char Calends_is_same_duration(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Compares the duration of :c:data:`c1` and :c:data:`c2`. + +.. c:function:: signed char Calends_is_longer(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Compares the duration of :c:data:`c1` and :c:data:`c2`. + +.. c:function:: signed char Calends_is_before(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Compares the entirety of :c:data:`c1` with the start date of :c:data:`c2`. + +.. c:function:: signed char Calends_starts_before(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Compares the start date of :c:data:`c1` with the start date of :c:data:`c2`. + +.. c:function:: signed char Calends_ends_before(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Compares the end date of :c:data:`c1` with the start date of :c:data:`c2`. + +.. c:function:: signed char Calends_is_during(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Checks whether the entirety of :c:data:`c1` lies between the start and end + dates of :c:data:`c2`. + +.. c:function:: signed char Calends_starts_during(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Checks whether the start date of :c:data:`c1` lies between the start and end + dates of :c:data:`c2`. + +.. c:function:: signed char Calends_ends_during(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Checks whether the end date of :c:data:`c1` lies between the start and end + dates of :c:data:`c2`. + +.. c:function:: signed char Calends_is_after(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Compares the entirety of :c:data:`c1` with the end date of :c:data:`c2`. + +.. c:function:: signed char Calends_starts_after(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Compares the start date of :c:data:`c1` with the end date of :c:data:`c2`. + +.. c:function:: signed char Calends_ends_after(long long unsigned int c1, long long unsigned int c2) + + :param c1: The identifier of the current ``Calends`` object. + :type c1: :c:type:`long long unsigned int` + :param c2: The identifier of the ``Calends`` object to compare. + :type c2: :c:type:`long long unsigned int` + :return: The result of the comparison + :rtype: :c:type:`signed char` + + Compares the end date of :c:data:`c1` with the end date of :c:data:`c2`. + +Export +------ + +.. c:function:: char* Calends_string(long long unsigned int c) + + :param c: The :go:type:`Calends` object to convert. + :type c: :c:type:`long long unsigned int` + :return: The string representation of the current value. + :rtype: :c:type:`char*` + + Converts the value of :c:data:`c` to a string. + +.. c:function:: char* Calends_encode_text(long long unsigned int c) + + :param c: The :go:type:`Calends` object to encode. + :type c: :c:type:`long long unsigned int` + :return: The encoded representation of the current value. + :rtype: :c:type:`char*` + + Encodes the value of :c:data:`c` as text, for external storage. + +.. c:function:: long long unsigned int Calends_decode_text(char* in) + + :param in: The encoded representation of a :go:type:`Calends` value. + :type in: :c:type:`char*` + :return: The decoded :go:type:`Calends` object's identifier. + :rtype: :c:type:`long long unsigned int` + + Decodes the value of :c:data:`in` to a new :c:type:`Calends` object. + +.. c:function:: char* Calends_encode_json(long long unsigned int c) + + :param c: The :go:type:`Calends` object to encode. + :type c: :c:type:`long long unsigned int` + :return: The encoded representation of the current value. + :rtype: :c:type:`char*` + + Encodes the value of :c:data:`c` as JSON, for external communication. + +.. c:function:: long long unsigned int Calends_decode_json(char* in) + + :param in: The encoded representation of a :go:type:`Calends` value. + :type in: :c:type:`char*` + :return: The decoded :go:type:`Calends` object's identifier. + :rtype: :c:type:`long long unsigned int` + + Decodes the value of :c:data:`in` to a new :c:type:`Calends` object. + +Error Handling +-------------- + +.. c:function:: void Calends_register_panic_handler(Calends_panic_handler callback) + + :param callback: A panic handler function. + :type callback: :c:type:`void function(char*)` + + When errors happen, Go normally returns the error as an additional return + value. Since most programming languages don't support this, the C/C++ + interface to the library instead relies on a Golang feature called a + ``panic``, which is a lot like an exception in many other languages. This + function allows users to register a callback function of their own to handle + the error conditions which might come up. :c:data:`callback` should accept a + :c:type:`char*` containing the error message supplied by the library, and + return nothing. + +.. TODO :: + Actually add C++ support, because the C namespace support is ... not great. diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..648063b --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'Calends' +copyright = '2018, Dan Hunsaker' +author = 'Dan Hunsaker' + +# The short X.Y version +version = '0.0' +# The full version, including alpha/beta/rc tags +release = '0.0.4' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.doctest', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'sphinx.ext.ifconfig', + 'sphinx.ext.viewcode', + 'sphinxcontrib.golangdomain', + 'sphinxcontrib.phpdomain', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['.templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path . +exclude_patterns = ['.build', 'Thumbs.db', '.DS_Store', '.git'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +add_function_parentheses = True + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['.static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Calendsdoc' + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'Calends.tex', 'Calends: Documented', + 'Dan Hunsaker', 'manual'), +] + + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'calends', 'Calends: Documented', + [author], 1) +] + + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Calends', 'Calends: Documented', + author, 'Calends', 'One line description of project.', + 'Miscellaneous'), +] + + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + + +# -- Extension configuration ------------------------------------------------- + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False diff --git a/docs/contributions.rst b/docs/contributions.rst new file mode 100644 index 0000000..bfe6a4c --- /dev/null +++ b/docs/contributions.rst @@ -0,0 +1,24 @@ +.. _contributions: + +Contributions +============= + +Pull requests are always welcome on `GitHub`_! That said, please be open to +discussing the PR content, and possibly revising it if requested. Not all +requests can be merged, and not all changes are desired. + +Security Reporting +------------------ + +Report all security-related issues to `dan (dot) hunsaker (plus) calends (at) +gmail `_, and use PGP or GPG protections +on your message (the account's key is ``44806AB9``, or you can look it up by the +email address). Security issues will be addressed internally before making any +vulnerability announcements. + +Contributors +------------ + +`@danhunsaker `_ + +.. _GitHub: https://github.com/danhunsaker/calends diff --git a/docs/features.rst b/docs/features.rst new file mode 100644 index 0000000..8fa7476 --- /dev/null +++ b/docs/features.rst @@ -0,0 +1,118 @@ +.. _features: + +Features in Calends +=================== + +For a current indication of which of these features are fully implemented at the moment, check `the README `_. + +* Large range and high precision + Calends understands dates |2^62| seconds into the future or past, in units + as small as |10^-45| seconds – that's over 146 billion years into the past + or future (146 138 512 313 years, 169 days, 10 hours, 5 minutes, and 28 + seconds from ``CE 1970 Jan 01 00:00:00 TAI Gregorian``), at resolutions + smaller than Planck Time (54×\ |10^-45| seconds, and the smallest meaningful + duration even on the quantum scale). That encompasses well beyond the + expected lifespan of the Universe, at resolutions enough to represent + quantum events. + +* Supports date (and time) values in multiple calendar systems + Supported out of the box are the following (all systems are proleptic – + extrapolated beyond the officially-defined limits – unless specified + otherwise): + + \ + + * :ref:`Unix time ` + A count of the number of seconds since ``CE 1970 Jan 01 00:00:00 UTC + Gregorian`` + + * :ref:`TAI64 ` + Essentially Unix time plus |2^62|, but using TAI seconds instead of UTC + seconds, so times can be converted unambiguously (UTC uses leap seconds + to keep the solar zenith at noon, while TAI is a simple, unadjusted + count). Calends supports an extended version of this spec, with three + more components, to encode out to 45 places instead of just 18; this is + also actually the internal time scale used by Calends itself, which is + how it can support such a broad range of dates at such a high + resolution. + + \ + + * Automatic calculation of leap second offsets + * Estimation of undefined past and future leap second insertions + * Automatic updates for handling leap second insertions + + * :ref:`Gregorian ` + The current international standard calendar system + + \ + + * Disconnect from native :go:type:`time.Time` implementation, and its + limitations + + * :ref:`Julian ` + The previous version of the Gregorian calendar + + * :ref:`Julian Day Count ` + A count of days since ``BCE 4713 Jan 01 12:00:00 UTC Julian + (proleptic)`` + + * :ref:`Hebrew ` + \ + + * :ref:`Persian ` + \ + + * :ref:`Chinese ` + Several variants + + * :ref:`Meso-American ` + Commonly called Mayan, but used by several cultures in the region + + * :ref:`Discordian ` + \ + + * :ref:`Stardate ` + Yes, the ones from :t:`Star Trek`\ ™; several variants exist + +* Encodes both time spans and instants in a single interface + The library treats the time values it encodes as ``[start, end)`` sets (that + is, the ``start`` point is included in the range, as is every point between + ``start`` and ``end``, but the ``end`` point itself is _not_ included in the + range). This allows ``duration`` to accurately be ``end - start`` in all + cases. (And yes, that also means you can create spans with ``duration < + 0``.) + +* Supports calculations and comparisons on spans and instants + Addition, subtraction, intersection, combination, gap calculation, overlap + detection, and similar operations are all supported directly on Calends + values. + +* Conversion to/from native date/time types + While this is possible by using a string representation as an intermediary, + in either direction, some data and precision is lost in such a conversion. + Instead, Calends supports conversion to and from such types directly, + preserving as much data and accuracy as each native type provides. + +* Geo-temporally aware + The library provides methods for passing a location instead of a calendar + system, and selecting an appropriate calendar based on which was most common + in that location at that point in time. *(Some guess work is involved in + this process when parsing dates, so it is still preferred to supply the + calendar system, if known, when parsing.)* + +* Time zone support + \ + +* Well-defined interfaces for extending the library + Add more calendar systems, type conversions, or geo-temporal relationships + without forking/modifying the library itself. + +* Shared library (``.so``/``.dll``) + In order to use the library outside of Golang projects, we first need to + export its functionality in a shared library, which can then be accessed + from other programming evironments and applications, generally via FFI. + +.. |10^-45| replace:: 10\ :sup:`-45` +.. |10^-20| replace:: 10\ :sup:`-20` +.. |2^62| replace:: 2\ :sup:`62` diff --git a/docs/go/custom_calendars.rst b/docs/go/custom_calendars.rst new file mode 100644 index 0000000..bfea9a8 --- /dev/null +++ b/docs/go/custom_calendars.rst @@ -0,0 +1,322 @@ +.. _custom-calendars-go: + +.. go:currentpackage:: calends/calendars + +.. index:: + pair: custom calendars; Golang + +Custom Calendars in Golang +========================== + +Adding new calendars to Calends is a fairly straightforward process. Implement +one interface, or its three methods as standalone functions, and then simply +pass them to one of the two registration functions. + +Define +------ + +The interface in question looks like this: + +.. go:package:: calends/calendars + +.. go:type:: CalendarDefinition + + .. go:function:: func (CalendarDefinition) ToInternal(date interface, format string) (TAI64NAXURTime, error) + + :param date: The input date. Should support :go:type:`string` at the very + minimum. + :type date: :go:type:`interface{}` + :param format: The format string for parsing the input date. + :type format: :go:type:`string` + :return: The parsed internal timestamp. + :rtype: :go:type:`TAI64NAXURTime` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Converts an input date/time representation to an internal + :go:type:`TAI64NAXURTime`. + + .. go:function:: func (CalendarDefinition) FromInternal(stamp TAI64NAXURTime, format string) (string, error) + + :param stamp: The internal timestamp value. + :type stamp: :go:type:`TAI64NAXURTime` + :param format: The format string for formatting the output date. + :type format: :go:type:`string` + :return: The formatted date/time. + :rtype: :go:type:`string` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Converts an internal :go:type:`TAI64NAXURTime` to a date/time string. + + .. go:function:: func (CalendarDefinition) Offset(stamp TAI64NAXURTime, offset interface) (TAI64NAXURTime, error) + + :param stamp: The internal timestamp value. + :type stamp: :go:type:`TAI64NAXURTime` + :param offset: The input offset. Should support :go:type:`string` at the + very minimum. + :type offset: :go:type:`interface{}` + :return: The adjusted internal timestamp. + :rtype: :go:type:`TAI64NAXURTime` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Adds the given offset to an internal :go:type:`TAI64NAXURTime`. + +Registration +------------ + +Register +:::::::: + +Once it is registered with the library, your calendar system can be used from +anywhere in your application. To register a system, pass it to one of the +following two functions: + +.. go:function:: func RegisterClass(name string, definition CalendarDefinition, defaultFormat string) + + :param name: The name to register the calendar system under. + :type name: :go:type:`string` + :param definition: The calendar system itself. + :type definition: :go:type:`CalendarDefinition` + :param defaultFormat: The default format string. + :type defaultFormat: :go:type:`string` + + Registers a calendar system class, storing ``definition`` as ``name``, and + saving ``defaultFormat`` for later use while parsing or formatting. + +.. go:function:: func RegisterElements(name string, toInternal ToInternal, fromInternal FromInternal, offset Offset, defaultFormat string) + + :param name: The name to register the calendar system under. + :type name: :go:type:`string` + :param toInternal: The function for parsing dates into internal timestamps. + :type toInternal: :go:func:`(CalendarDefinition) ToInternal` + :param fromInternal: The function for formatting internal timestamps as + dates. + :type fromInternal: :go:func:`(CalendarDefinition) FromInternal` + :param offset: The function for adding an offset to internal timestamps. + :type offset: :go:func:`(CalendarDefinition) Offset` + :param defaultFormat: The default format string. + :type defaultFormat: :go:type:`string` + + Registers a calendar system from its distinct functions. It does this by + storing ``toInternal``, ``fromInternal``, and ``offset`` as the elements of + ``name``, and saving ``defaultFormat`` for later use while parsing or + formatting. + +Unregister +:::::::::: + +.. go:function:: func Unregister(name string) + + :param name: The name of the calendar system to remove. + :type name: :go:type:`string` + + Removes a calendar system from the callback list. + +Check and List +:::::::::::::: + +.. go:function:: func Registered(calendar string) bool + + :param name: The calendar system name to check for. + :type name: :go:type:`string` + :return: Whether or not the calendar system is currently registered. + :rtype: :go:type:`bool` + + Returns whether or not a calendar system has been registered, yet. + +.. go:function:: func ListRegistered() []string + + :return: The sorted list of calendar systems currently registered. + :rtype: :go:type:`[]string` + + Returns the list of calendar systems currently registered. + +Types and Values +---------------- + +Now we get to the inner workings that make calendar systems function – even the +built-in ones. The majority of the "magic" comes from the +:go:type:`TAI64NAXURTime` object itself, as a reliable way of storing the exact +instants being calculated, and the only way times are handled by the library +itself. A handful of methods provide basic operations that calendar system +developers can use to simplify their conversions (adding and subtracting the +values of other timestamps, and importing/exporting timestamp values from/to +arbitrary-precision floating point :go:type:`math/big.Float`\ s, in particular), +and a couple of helpers exclusively handle adding and removing UTC leap second +offsets. As long as you can convert your dates to/from Unix timestamps in a +:go:type:`string` or :go:type:`math/big.Float`, the rest is handled entirely by +these helpers in the library itself. + +.. go:type:: TAI64NAXURTime + + :param int64 Seconds: The number of TAI seconds since ``CE 1970-01-01 + 00:00:00 TAI``. + :param uint32 Nano: The first 9 digits of the timestamp's fractional + component. + :param uint32 Atto: The 10th through 18th digits of the fractional component. + :param uint32 Xicto: The 19th through 27th digits of the fractional + component. + :param uint32 Ucto: The 28th through 36th digits of the fractional component. + :param uint32 Rocto: The 37th through 45th digits of the fractional + component. + + :go:type:`TAI64NAXURTime` stores a ``TAI64NAXUR`` instant in a reliable, + easy-converted format. Each 9-digit fractional segment is stored in a + separate 32-bit integer to preserve its value with a very high degree of + accuracy, without having to rely on string parsing or Golang's + :go:type:`math/big.*` values. + + .. go:function:: func (TAI64NAXURTime) Add(z TAI64NAXURTime) TAI64NAXURTime + + :param z: The timestamp to add to the current one. + :type z: :go:type:`TAI64NAXURTime` + :return: The sum of the two timestamps. + :rtype: :go:type:`TAI64NAXURTime` + + Calculates the sum of two :go:type:`TAI64NAXURTime` values. + + .. go:function:: func (TAI64NAXURTime) Sub(z TAI64NAXURTime) TAI64NAXURTime + + :param z: The timestamp to subtract from the current one. + :type z: :go:type:`TAI64NAXURTime` + :return: The difference of the two timestamps. + :rtype: :go:type:`TAI64NAXURTime` + + Calculates the difference of two :go:type:`TAI64NAXURTime` values. + + .. go:function:: func (TAI64NAXURTime) String() string + + :return: The decimal string representation of the current timestamp. + :rtype: :go:type:`string` + + Returns the decimal string representation of the :go:type:`TAI64NAXURTime` + value. + + .. go:function:: func (TAI64NAXURTime) HexString() string + + :return: The hexadecimal string representation of the current timestamp. + :rtype: :go:type:`string` + + Returns the hexadecimal string representation of the + :go:type:`TAI64NAXURTime` value. + + .. go:function:: func (TAI64NAXURTime) Float() Float + + :return: The arbitrary-precision floating point representation of the + current timestamp. + :rtype: :go:type:`math/big.(*Float)` + + Returns the :go:type:`math/big.(*Float)` representation of the + :go:type:`TAI64NAXURTime` value. + + .. go:function:: func (TAI64NAXURTime) MarshalText() ([]byte, error) + + :return: A byte slice containing the marshalled text. + :rtype: :go:type:`[]byte` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Implements the :go:type:`encoding.TextMarshaler` interface. + + .. go:function:: func (TAI64NAXURTime) UnmarshalText(in []byte) error + + :param in: A byte slice containing the marshalled text. + :type in: :go:type:`[]byte` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Implements the :go:type:`encoding.TextUnmarshaler` interface. + + .. go:function:: func (TAI64NAXURTime) MarshalBinary() ([]byte, error) + + :return: A byte slice containing the marshalled binary data. + :rtype: :go:type:`[]byte` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Implements the :go:type:`encoding.BinaryMarshaler` interface. + + .. go:function:: func (TAI64NAXURTime) UnmarshalBinary(in []byte) error + + :param in: A byte slice containing the marshalled binary data. + :type in: :go:type:`[]byte` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Implements the :go:type:`encoding.BinaryUnmarshaler` interface. + +Helpers +------- + +.. go:function:: func TAI64NAXURTimeFromDecimalString(in string) TAI64NAXURTime + + :param in: The decimal string representation of a timestamp to calculate. + :type in: :go:type:`string` + :return: The calculated timestamp. + :rtype: :go:type:`TAI64NAXURTime` + + Calculates a :go:type:`TAI64NAXURTime` from its decimal string + representation. + +.. go:function:: func TAI64NAXURTimeFromHexString(in string) TAI64NAXURTime + + :param in: The hexadecimal string representation of a timestamp to calculate. + :type in: :go:type:`string` + :return: The calculated timestamp. + :rtype: :go:type:`TAI64NAXURTime` + + Calculates a :go:type:`TAI64NAXURTime` from its hexadecimal string + representation. + +.. go:function:: func TAI64NAXURTimeFromFloat(in Float) TAI64NAXURTime + + :param in: The arbitrary-precision floating point representation of a + timestamp to calculate. + :type in: :go:type:`math/big.Float` + :return: The calculated timestamp. + :rtype: :go:type:`TAI64NAXURTime` + + Calculates a :go:type:`TAI64NAXURTime` from its :go:type:`math/big.Float` + representation. + +.. go:function:: func UTCtoTAI(utc TAI64NAXURTime) TAI64NAXURTime + + :param utc: The timestamp to remove the UTC offset from. + :type utc: :go:type:`TAI64NAXURTime` + :return: The calculated timestamp. + :rtype: :go:type:`TAI64NAXURTime` + + Removes the UTC leap second offset from a TAI64NAXURTime value. + +.. go:function:: func TAItoUTC(tai TAI64NAXURTime) TAI64NAXURTime + + :param tai: The timestamp to add the UTC offset to. + :type tai: :go:type:`TAI64NAXURTime` + :return: The calculated timestamp. + :rtype: :go:type:`TAI64NAXURTime` + + Adds the UTC leap second offset to a TAI64NAXURTime value. + +Errors +------ + +.. go:type:: ErrUnsupportedInput + + Used to indicate that the input date/time weren't recognized by the calendar + system, or that the data type is incorrect. + +.. go:type:: ErrInvalidFormat + + Indicates that the ``format`` string isn't supported by the calendar system. + +.. go:function:: func ErrUnknownCalendar(calendar string) error + + :param in: The name of the unknown calendar system. + :type in: :go:type:`string` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Generates a "calendar not registered" error, including the calendar's actual + name in the error message. diff --git a/docs/go/installation.rst b/docs/go/installation.rst new file mode 100644 index 0000000..97897cb --- /dev/null +++ b/docs/go/installation.rst @@ -0,0 +1,13 @@ +.. _installation-go: + +.. index:: + pair: installation; Golang + +Installing Calends for Golang +============================= + +Install the library with ``go get github.com/danhunsaker/calends``, and then +``import "github.com/danhunsaker/calends"`` wherever you intend to use it. If +you're using another Go dependency manager, you'll need to use its dependency +installation method instead; consult its documentation for more details, as we +can't possibly cover them all here. diff --git a/docs/go/usage.rst b/docs/go/usage.rst new file mode 100644 index 0000000..d8d7d84 --- /dev/null +++ b/docs/go/usage.rst @@ -0,0 +1,512 @@ +.. _usage-go: + +.. go:currentpackage:: calends + +.. index:: + pair: usage; Golang + +Using Calends in Golang +======================= + +Calends exposes a very small handful of things for use outside the library +itself. One is the :go:type:`Calends` class, documented here, which should be +the only interface users of the library ever need to touch. + +Another thing Calends exposes is the :go:type:`calendars.TAI64NAXURTime` class, +used to store and manipulate the instants of time which make up a +:go:type:`Calends` instance. The rest are interfaces for extending the library's +functionality. These are covered in the :ref:`Custom Calendars in Golang +` section. + +.. note:: + + :go:type:`Calends` objects are immutable - all methods return a new + :go:type:`Calends` object where they might otherwise alter the current one. + This is true even of the :go:func:`!Calends.Set*` methods. This has the side + effect of using more memory to perform manipulations than updating values on + an existing object would. It makes many operations safer, though, than + mutable objects would allow. + +.. go:package:: calends + +.. go:type:: Calends + + The main entry point to Calends and its functionality. :go:type:`Calends` + objects should only be created with the :go:func:`Create` function, and never + directly (especially given its values are all unexported ones). + +Create +------ + +.. go:function:: func Create(value interface, calendar string, format string) (Calends, error) + + :param value: The value to parse the date(s)/time(s) from. + :type value: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date(s)/time(s) + with. + :param string format: The format the date(s)/time(s) is/are expected to use. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Creates a new :go:type:`Calends` object, using ``calendar`` to select a + calendar system, and ``format`` to parse the contents of ``value`` into the + :go:type:`Calends` object's internal instants. The type of ``value`` can vary + based on the calendar system itself, but generally speaking can always be a + string. + + In any case, the value can always be a :go:type:`map[string]interface{}`, + where the keys are any two of ``start``, ``end``, and ``duration``. If all + three are provided, ``duration`` is ignored in favor of calculating it + directly. If only one of the listed keys is provided, ``value`` is passed to + the calendar system itself unchanged. + + The calendar system then converts ``value`` to a + :go:type:`calendars.TAI64NAXURTime` instant, which the :go:type:`Calends` + object sets to the appropriate internal value. + +Read +---- + +.. go:function:: func (Calends) Date(calendar string, format string) (string, error) + + :param string calendar: The calendar system to format the date/time with. + :param string format: The format the date/time is expected to be in. + :return: The start date of the :go:type:`Calends` object + :rtype: :go:type:`string` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Retrieves the start date of the :go:type:`Calends` object as a string. The + value is generated by the calendar system given in ``calendar``, according to + the format string in ``format``. + +.. go:function:: func (Calends) EndDate(calendar string, format string) (string, error) + + :param string calendar: The calendar system to format the date/time with. + :param string format: The format the date/time is expected to be in. + :return: The end date of the :go:type:`Calends` object + :rtype: :go:type:`string` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Retrieves the end date of the :go:type:`Calends` object as a string. The + value is generated by the calendar system given in ``calendar``, according to + the format string in ``format``. + +.. go:function:: func (Calends) Duration() Float + + :return: The duration of the :go:type:`Calends` object + :rtype: :go:type:`math/big.(*Float)` + + Retrieves the duration of the :go:type:`Calends` object as an + arbitrary-precision floating point number. This value will be ``0`` if the + :go:type:`Calends` object is an instant. + +Update +------ + +.. go:function:: func (Calends) SetDate(stamp interface, calendar string, format string) (Calends, error) + + :param value: The value to parse the date/time from. + :type value: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date/time with. + :param string format: The format the date/time is expected to use. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Sets the start date of a :go:type:`Calends` object, based on the + :go:type:`Calends` object's current value. The inputs are the same as for + :go:func:`Create`, above, except the string → value map option isn't + available, since you're already specifically setting the start value + explicitly. + +.. go:function:: func (Calends) SetEndDate(stamp interface, calendar string, format string) (Calends, error) + + :param value: The value to parse the date/time from. + :type value: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date/time with. + :param string format: The format the date/time is expected to use. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Sets the end date of a :go:type:`Calends` object, based on the + :go:type:`Calends` object's current value. The inputs are the same as for + :go:func:`Create`, above, except the string → value map option isn't + available, since you're already specifically setting the end value + explicitly. + +.. go:function:: func (Calends) SetDuration(duration interface, calendar string) (Calends, error) + + :param duration: The value to parse the new duration from. + :type duration: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date/time with. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Sets the duration of a :go:type:`Calends` object, by adjusting its end point, + and using the current start point as an anchor. The ``duration`` value is + interpreted by the calendar system given in ``calendar``, so is subject to + any of its rules. + +.. go:function:: func (Calends) SetDurationFromEnd(duration interface, calendar string) (Calends, error) + + :param duration: The value to parse the new duration from. + :type duration: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date/time with. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Sets the duration of a :go:type:`Calends` object, by adjusting its start + point, and using the current end point as an anchor. The ``duration`` value + is interpreted by the calendar system given in ``calendar``, so is subject to + any of its rules. + +Manipulate +---------- + +.. go:function:: func (Calends) Add(offset interface, calendar string) (Calends, error) + + :param offset: The value to parse the offset from. + :type offset: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date/time with. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Increases the end date of the :go:type:`Calends` object's current value by + ``offset``, as interpreted by the calendar system given in ``calendar``. + +.. go:function:: func (Calends) AddFromEnd(offset interface, calendar string) (Calends, error) + + :param offset: The value to parse the offset from. + :type offset: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date/time with. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Increases the start date of the :go:type:`Calends` object's current value by + ``offset``, as interpreted by the calendar system given in ``calendar``. + +.. go:function:: func (Calends) Subtract(offset interface, calendar string) (Calends, error) + + :param offset: The value to parse the offset from. + :type offset: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date/time with. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Works the same as ``Add``, except it decreases the start date, rather than + increasing it. + +.. go:function:: func (Calends) SubtractFromEnd(offset interface, calendar string) (Calends, error) + + :param offset: The value to parse the offset from. + :type offset: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date/time with. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Works the same as ``AddFromEnd``, except it decreases the end date, rather + than increasing it. + +.. go:function:: func (Calends) Next(offset interface, calendar string) (Calends, error) + + :param offset: The value to parse the offset from. + :type offset: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date/time with. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Returns a :go:type:`Calends` object of ``offset`` duration (as interpreted by + the calendar system given in ``calendar``), which abuts the :go:type:`Calends` + object's current value. If ``offset`` is empty, ``calendar`` is ignored, and + the current object's duration is used instead. + +.. go:function:: func (Calends) Previous(offset interface, calendar string) (Calends, error) + + :param offset: The value to parse the offset from. + :type offset: :go:type:`interface{}` + :param string calendar: The calendar system to parse the date/time with. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Returns a :go:type:`Calends` object of ``offset`` duration (as interpreted by + the calendar system given in ``calendar``), which abuts the :go:type:`Calends` + object's current value. If ``offset`` is empty, ``calendar`` is ignored, and + the current object's duration is used instead. + +Combine +------- + +.. go:function:: func (Calends) Merge(c2 Calends) (Calends, error) + + :param Calends c2: The :go:type:`Calends` object to merge. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Returns a :go:type:`Calends` object spanning from the earliest start date to + the latest end date between the current :go:type:`Calends` object and ``c2``. + +.. go:function:: func (Calends) Intersect(c2 Calends) (Calends, error) + + :param Calends c2: The :go:type:`Calends` object to intersect. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Returns a :go:type:`Calends` object spanning the overlap between the current + :go:type:`Calends` object and ``c2``. If the current object and ``c2`` don't + overlap, returns an error. + +.. go:function:: func (Calends) Gap(c2 Calends) (Calends, error) + + :param Calends c2: The :go:type:`Calends` object to gap. + :return: A new :go:type:`Calends` object + :rtype: :go:type:`Calends` + :return: :go:type:`error` when an error occurs; :go:type:`nil` otherwise + :rtype: :go:type:`error` or :go:type:`nil` + + Returns a :go:type:`Calends` object spanning the gap between the current + :go:type:`Calends` object and ``c2``. If the current object and ``c2`` + overlap (and there is, therefore, no gap to return), returns an error. + +Compare +------- + +.. go:function:: func (Calends) Difference(c2 Calends, mode string) Float + + :param Calends c2: The :go:type:`Calends` object to compare. + :param string mode: The comparison mode. + :return: The difference, as an arbitrary-precision floating point number + :rtype: :go:type:`math/big.Float` + + Returns the difference of the current :go:type:`Calends` object minus ``c2``, + using ``mode`` to select which values to use in the calculation. Valid + ``mode``\ s include: + + - ``start`` - use the start date of both the current object and ``c2`` + - ``duration`` - use the duration of both the current object and ``c2`` + - ``end`` - use the end date of both the current object and ``c2`` + - ``start-end`` - use the start of the current object, and the end of ``c2`` + - ``end-start`` - use the end of the current object, and the start of ``c2`` + +.. go:function:: func (Calends) Compare(c2 Calends, mode string) int + + :param Calends c2: The :go:type:`Calends` object to compare. + :param string mode: The comparison mode. + :return: A standard comparison result + :rtype: :go:type:`int` + + Returns ``-1`` if the current :go:type:`Calends` object is less than ``c2``, + ``0`` if they are equal, and ``1`` if the current object is greater than + ``c2``, using ``mode`` to select which values to use in the comparison. Valid + ``mode``\ s are the same as for :go:func:`(Calends) Difference`, above. + +.. go:function:: func (Calends) Contains(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Checks whether the current :go:type:`Calends` object contains all of ``c2``. + +.. go:function:: func (Calends) Overlaps(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Checks whether the current :go:type:`Calends` object overlaps with ``c2``. + +.. go:function:: func (Calends) Abuts(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Checks whether the current :go:type:`Calends` object abuts ``c2`` (that is, + whether one begins at the same instant the other ends). + +.. go:function:: func (Calends) IsSame(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Checks whether the current :go:type:`Calends` object covers the same span of + time as ``c2``. + +.. go:function:: func (Calends) IsShorter(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Compares the duration of the current :go:type:`Calends` object and ``c2``. + +.. go:function:: func (Calends) IsSameDuration(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Compares the duration of the current :go:type:`Calends` object and ``c2``. + +.. go:function:: func (Calends) IsLonger(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Compares the duration of the current :go:type:`Calends` object and ``c2``. + +.. go:function:: func (Calends) IsBefore(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Compares the entirety of the current :go:type:`Calends` object with the start + date of ``c2``. + +.. go:function:: func (Calends) StartsBefore(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Compares the start date of the current :go:type:`Calends` object with the + start date of ``c2``. + +.. go:function:: func (Calends) EndsBefore(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Compares the end date of the current :go:type:`Calends` object with the start + date of ``c2``. + +.. go:function:: func (Calends) IsDuring(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Checks whether the entirety of the current :go:type:`Calends` object lies + between the start and end dates of ``c2``. + +.. go:function:: func (Calends) StartsDuring(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Checks whether the start date of the current :go:type:`Calends` object lies + between the start and end dates of ``c2``. + +.. go:function:: func (Calends) EndsDuring(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Checks whether the end date of the current :go:type:`Calends` object lies + between the start and end dates of ``c2``. + +.. go:function:: func (Calends) IsAfter(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Compares the entirety of the current :go:type:`Calends` object with the end + date of ``c2``. + +.. go:function:: func (Calends) StartsAfter(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Compares the start date of the current :go:type:`Calends` object with the end + date of ``c2``. + +.. go:function:: func (Calends) EndsAfter(c2 Calends) bool + + :param Calends c2: The :go:type:`Calends` object to compare. + :return: The result of the comparison + :rtype: :go:type:`bool` + + Compares the end date of the current :go:type:`Calends` object with the end + date of ``c2``. + +Export +------ + +.. go:function:: func (Calends) String() string + + :return: The string representation of the current value. + :rtype: :go:type:`string` + + Implements the :go:type:`fmt.Stringer` interface. + +.. go:function:: func (Calends) MarshalText() ([]byte, error) + + :return: A byte slice containing the marshalled text. + :rtype: :go:type:`[]byte` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Implements the :go:type:`encoding.TextMarshaler` interface. + +.. go:function:: func (*Calends) UnmarshalText(in []byte) error + + :param in: A byte slice containing the marshalled text. + :type in: :go:type:`[]byte` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Implements the :go:type:`encoding.TextUnmarshaler` interface. + +.. go:function:: func (Calends) MarshalJSON() ([]byte, error) + + :return: A byte slice containing the marshalled JSON. + :rtype: :go:type:`[]byte` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Implements the :go:type:`encoding/json.Marshaler` interface. + +.. go:function:: func (*Calends) UnmarshalJSON(in []byte) error + + :param in: A byte slice containing the marshalled JSON. + :type in: :go:type:`[]byte` + :return: Any error that occurs. + :rtype: :go:type:`error` + + Implements the :go:type:`encoding/json.Unmarshaler` interface. diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..b37d81c --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,97 @@ +.. Calends documentation master file, created by + sphinx-quickstart on Sun Jul 15 22:41:20 2018. + +.. _home: + +Calends: Documented +=================== + +.. only:: not epub + + |Software License| |Main Docs| |GoDoc Reference| |Gitter Chat| + |Total Downloads| + + |Latest Stable Version| |GitHub Release Date| + |Github commits (since latest release)| |GitHub last commit| + + |Maintenance Status| |Travis Build Status| |Codecov coverage| + |Scrutinizer Code Quality| |Go Report Card| |Libraries.io Dependency Check| + +Calends is a library for handling dates and times across arbitrary calendar +systems. It has a number of features essential to handling dates and times in +any supported system, as well as the ability to add support for more systems +easily, all without touching a line of its own code. Go ahead and read through +the documentation here to learn more! + +.. toctree:: + :maxdepth: 2 + + introduction.rst + features.rst + installation.rst + usage.rst + systems.rst + +Appendix +-------- + +.. toctree:: + :maxdepth: 2 + + contributions.rst + +.. only:: format_html and not singlehtml + + * :ref:`genindex` + +.. only:: builder_html + + * :ref:`search` + +.. only:: not epub + + .. |Software License| image:: https://img.shields.io/github/license/danhunsaker/calends.svg?style=for-the-badge + :target: LICENSE + :alt: Software License + .. |Main Docs| image:: https://img.shields.io/readthedocs/calends.svg?label=main+docs&style=for-the-badge + :target: https://calends.readthedocs.io/ + :alt: Main Docs + .. |GoDoc Reference| image:: https://img.shields.io/badge/GoDoc-reference-brightgreen.svg?style=flat-square + :target: https://godoc.org/github.com/danhunsaker/calends + :alt: GoDoc Reference + .. |Gitter Chat| image:: https://img.shields.io/gitter/room/danhunsaker/calends.svg?style=flat-square + :target: https://gitter.im/danhunsaker/calends + :alt: Gitter Chat + .. |Total Downloads| image:: https://img.shields.io/github/downloads/danhunsaker/calends/total.svg?style=flat-square + :target: https://github.com/danhunsaker/calends/releases + :alt: Total Downloads + .. |Latest Stable Version| image:: https://img.shields.io/github/release/danhunsaker/calends.svg?style=for-the-badge + :target: https://github.com/danhunsaker/calends/releases + :alt: Latest Stable Version + .. |GitHub Release Date| image:: https://img.shields.io/github/release-date/danhunsaker/calends.svg?style=for-the-badge + :target: https://github.com/danhunsaker/calends + :alt: GitHub Release Date + .. |Github commits (since latest release)| image:: https://img.shields.io/github/commits-since/danhunsaker/calends/latest.svg?style=flat-square + :target: https://github.com/danhunsaker/calends + :alt: Github commits (since latest release) + .. |GitHub last commit| image:: https://img.shields.io/github/last-commit/danhunsaker/calends.svg?style=flat-square + :target: https://github.com/danhunsaker/calends + :alt: GitHub last commit + .. |Maintenance Status| image:: https://img.shields.io/maintenance/yes/2018.svg?style=flat-square + :target: https://github.com/danhunsaker/calends + :alt: Maintenance Status + .. |Travis Build Status| image:: https://img.shields.io/travis/danhunsaker/calends.svg?style=flat-square + :target: https://travis-ci.org/danhunsaker/calends + :alt: Travis Build Status + .. |Codecov coverage| image:: https://img.shields.io/codecov/c/github/danhunsaker/calends.svg?style=flat-square + :target: https://codecov.io/gh/danhunsaker/calends + :alt: Codecov coverage + .. |Scrutinizer Code Quality| image:: https://img.shields.io/scrutinizer/g/danhunsaker/calends.svg?style=flat-square + :target: https://scrutinizer-ci.com/g/danhunsaker/calends/ + :alt: Scrutinizer Code Quality + .. |Go Report Card| image:: https://goreportcard.com/badge/github.com/danhunsaker/calends?style=flat-square + :target: https://goreportcard.com/report/github.com/danhunsaker/calends + :alt: Go Report Card + .. |Libraries.io Dependency Check| image:: https://img.shields.io/librariesio/github/danhunsaker/calends.svg?style=flat-square + :target: https://libraries.io/github/danhunsaker/calends + :alt: Libraries.io Dependency Check diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..5f64a61 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,17 @@ +.. _installation: + +.. index:: installation + +Installation of Calends +======================= + +The steps here vary based on which programming language(s) you're using. Golang +is a simple install and import. Other languages use a language-specific wrapper +around the compiled shared library. Select your language to take a deeper look. + +.. toctree:: + :maxdepth: 2 + + Golang + C/C++ + PHP diff --git a/docs/introduction.rst b/docs/introduction.rst new file mode 100644 index 0000000..f2adba0 --- /dev/null +++ b/docs/introduction.rst @@ -0,0 +1,63 @@ +Introduction +============ + +As mentioned before, Calends is a library for handling dates and times across +arbitrary calendar systems. But what does that actually mean? + +Let's say you're working on an application that uses dates. Pretty much anything +can qualify, really – we use dates in everything, throughout our daily lives. +Scheduling, journaling, historical research, projections into the future, or +just displaying the current date in the UI. Now let's say you want your app to +be used by people all over the globe. The current approach, used for decades, is +to simply use the Gregorian Calendar, which has (partially as a side effect of +this decision) become the default calendar system in use across the globe, for +coordinating, tracking, and preserving events worldwide. + +But this decision wasn't made with internationalization and localization in +mind. It was made as a result of practicality, with limited computing +capabilities at the time, and persists mostly as a result of laziness – if the +entire world is already using it anyway, why bother with anything else? It has +also persisted out of ignorance – many people aren't aware there are other +calendars out there in the first place, never mind that several are still in +active use to this day. Properly localizing applications should *include* +adjusting the displayed date to use the preferred calendar system of the user. + +Sadly, most of the solutions currently available for handling dates (and times) +in software are purpose-built for a single calendar system, and use APIs +entirely different from those meant to handle dates in others. This makes it +very tricky to build an application that supports more than one calendar system +at a time. Each new calendar system requires hours of work to learn, connect, +and usually abstract to a point where it is usable within the larger +application, and even that's no guarantee the values can be stored or compared +accurately. + +That's what Calends set out to solve. It provides a single interface for +interacting with any supported calendar system, and an easy way to extend it to +support others, so that once you've added support for a calendar system once, +you have that support anywhere, without having to rewrite anything to fit your +next application. Additionally, you can take full advantage of any calendar +system implemented by anybody else. + +Accept date/time input in any calendar system, perform date/time calculations on +the resulting value, again in any calendar system, and then display the result – +yes, in any calendar system. Dates are stored, internally, in an extremely +accurate value that can track dates out 146 *billion* years into the past or +future, with a resolution of |10^-45| seconds, which is smaller than Planck +Time\ [#fintro1]_. In other words, it should be more than sufficient to record +instants of any duration and resolution desired for any conceived use case. + +.. TODO:: + After more calendar systems which are in common use today are implemented, + talk about how much value they'll add to developers' toolboxes. :) + +.. [#fintro1] `Planck Time `_ is the + smallest meaningful unit of time, and is about 54×\ |10^-45| seconds. It + corresponds to the amount of time it takes a photon (traveling at the speed + of light through a vaccuum) to traverse one Planck Length, which itself is + about |10^-20| times the diameter of a proton. Even quantum interactions + below this scale lose any meaning, and so values below them are considered + extraneous, in addition to being entirely unmeasurable with current + technologies and techniques. + +.. |10^-45| replace:: 10\ :sup:`-45` +.. |10^-20| replace:: 10\ :sup:`-20` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..ecbd389 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=.build +set SPHINXPROJ=Calends + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/php/custom_calendars.rst b/docs/php/custom_calendars.rst new file mode 100644 index 0000000..6268fd7 --- /dev/null +++ b/docs/php/custom_calendars.rst @@ -0,0 +1,292 @@ +.. _custom-calendars-php: + +.. php:namespace:: Calends + +.. index:: + pair: custom calendars; PHP + +Custom Calendars in PHP +========================== + +Adding new calendars to Calends is a fairly straightforward process. Implement +one of two interfaces, and then simply pass it to the registration method. + +Define +------ + +The more common interface looks like this: + +.. php:interface:: CalendarInterface + + .. php:staticmethod:: ToInternal(mixed $date, string $format): TAITime + + :param $date: The input date. Should support strings at the very minimum. + :type $date: ``mixed`` + :param $format: The format string for parsing the input date. + :type $format: ``string`` + :return: The parsed internal timestamp. + :rtype: :php:class:`TAITime` + :throws CalendsException: when an error occurs + + Converts an input date/time representation to an internal + :php:class:`TAITime`. + + .. php:staticmethod:: FromInternal(TAITime $stamp, string $format): string + + :param $stamp: The internal timestamp value. + :type $stamp: :php:class:`TAITime` + :param $format: The format string for formatting the output date. + :type $format: ``string`` + :return: The formatted date/time. + :rtype: ``string`` + :throws CalendsException: when an error occurs + + Converts an internal :php:class:`TAITime` to a date/time string. + + .. php:staticmethod:: Offset(TAITime $stamp, mixed $offset): TAITime + + :param $stamp: The internal timestamp value. + :type $stamp: :php:class:`TAITime` + :param $offset: The input offset. Should support strings at the very + minimum. + :type $offset: ``mixed`` + :return: The adjusted internal timestamp. + :rtype: :php:class:`TAITime` + :throws CalendsException: when an error occurs + + Adds the given offset to an internal :php:class:`TAITime`. + +The other is virtually identical, but uses object instances instead of static +class methods: + +.. php:interface:: CalendarObjectInterface + + .. php:method:: ToInternal(mixed $date, string $format): TAITime + + :param $date: The input date. Should support strings at the very minimum. + :type $date: ``mixed`` + :param $format: The format string for parsing the input date. + :type $format: ``string`` + :return: The parsed internal timestamp. + :rtype: :php:class:`TAITime` + :throws CalendsException: when an error occurs + + Converts an input date/time representation to an internal + :php:class:`TAITime`. + + .. php:method:: FromInternal(TAITime $stamp, string $format): string + + :param $stamp: The internal timestamp value. + :type $stamp: :php:class:`TAITime` + :param $format: The format string for formatting the output date. + :type $format: ``string`` + :return: The formatted date/time. + :rtype: ``string`` + :throws CalendsException: when an error occurs + + Converts an internal :php:class:`TAITime` to a date/time string. + + .. php:method:: Offset(TAITime $stamp, mixed $offset): TAITime + + :param $stamp: The internal timestamp value. + :type $stamp: :php:class:`TAITime` + :param $offset: The input offset. Should support strings at the very + minimum. + :type $offset: ``mixed`` + :return: The adjusted internal timestamp. + :rtype: :php:class:`TAITime` + :throws CalendsException: when an error occurs + + Adds the given offset to an internal :php:class:`TAITime`. + +Registration +------------ + +Register +:::::::: + +Once it is registered with the library, your calendar system can be used from +anywhere in your application. To register a system, pass it to the following +function: + +.. php:class:: Calends + +.. php:staticmethod:: calendarRegister(string $name, string $defaultFormat, mixed $calendar) + + :param $name: The name to register the calendar system under. + :type $name: ``string`` + :param $defaultFormat: The default format string. + :type $defaultFormat: ``string`` + :param $calendar: The calendar system itself. + :type $calendar: :php:interface:`CalendarInterface` or + :php:interface:`CalendarObjectInterface` + + Registers a calendar system class or object, storing ``$calendar`` as + ``$name``, and saving ``$defaultFormat`` for later use while parsing or + formatting. + +Unregister +:::::::::: + +.. php:staticmethod:: calendarUnregister(string $name) + + :param $name: The name of the calendar system to remove. + :type $name: ``string`` + + Removes a calendar system from the callback list. + +Check and List +:::::::::::::: + +.. php:staticmethod:: calendarRegistered(string $name): bool + + :param $name: The calendar system name to check for. + :type $name: ``string`` + :return: Whether or not the calendar system is currently registered. + :rtype: ``bool`` + + Returns whether or not a calendar system has been registered, yet. + +.. php:staticmethod:: calendarListRegistered(): array + + :return: The sorted list of calendar systems currently registered. + :rtype: ``[string]`` + + Returns the list of calendar systems currently registered. + +Types and Values +---------------- + +Now we get to the inner workings that make calendar systems function – even the +built-in ones. The majority of the "magic" comes from the :php:class:`TAITime` +object itself, as a reliable way of storing the exact instants being calculated, +and the only way times are handled by the library itself. A handful of methods +provide basic operations that calendar system developers can use to simplify +their conversions (adding and subtracting the values of other timestamps, and +importing/exporting timestamp values from/to string and numeric types, in +particular), and a couple of helpers exclusively handle adding and removing UTC +leap second offsets. As long as you can convert your dates to/from Unix +timestamps in a string or numeric type, the rest is handled entirely by these +helpers in the library itself. + +.. php:class:: TAITime + + :php:class:`TAITime` stores a ``TAI64NAXUR`` instant in a reliable, + easily-converted format. Each 9-digit fractional segment is stored in a + separate 32-bit integer to preserve its value with a very high degree of + accuracy, without having to rely on string parsing or external + arbitrary-precision mathematics libraries. + + .. php:attr:: seconds (float) + + The number of TAI seconds since ``CE 1970-01-01 00:00:00 TAI``. Should be an integer value; the ``float`` type is used, here, only to be able to hold a full signed 64-bit integer value regardless of architecture. + + .. php:attr:: nano (integer) + + The first 9 digits of the timestamp's fractional component. + + .. php:attr:: atto (integer) + + The 10th through 18th digits of the fractional component. + + .. php:attr:: xicto (integer) + + The 19th through 27th digits of the fractional component. + + .. php:attr:: ucto (integer) + + The 28th through 36th digits of the fractional component. + + .. php:attr:: rocto (integer) + + The 37th through 45th digits of the fractional component. + + .. php:method:: add(TAITime $z): TAITime + + :param $z: The timestamp to add to the current one. + :type $z: :php:class:`TAITime` + :return: The sum of the two timestamps. + :rtype: :php:class:`TAITime` + + Calculates the sum of two :php:class:`TAITime` values. + + .. php:method:: sub(TAITime $z): TAITime + + :param $z: The timestamp to subtract from the current one. + :type $z: :php:class:`TAITime` + :return: The difference of the two timestamps. + :rtype: :php:class:`TAITime` + + Calculates the difference of two :php:class:`TAITime` values. + + .. php:method:: toString(): string + + :return: The decimal string representation of the current timestamp. + :rtype: ``string`` + + Returns the decimal string representation of the :php:class:`TAITime` + value. + + .. Note:: + + :php:class:`TAITime` also implements :php:meth:`!__toString`, so you + can use that instead of calling this function directly, if you prefer. + + .. php:method:: fromString(string $in): TAITime + + :param $in: The decimal string representation of a timestamp to calculate. + :type $in: string + :return: The calculated timestamp. + :rtype: :php:class:`TAITime` + + Calculates a :php:class:`TAITime` from its decimal string representation. + + .. php:method:: toHex(): string + + :return: The hexadecimal string representation of the current timestamp. + :rtype: ``string`` + + Returns the hexadecimal string representation of the :php:class:`TAITime` + value. + + .. php:method:: fromHex(string $in):TAITime + + :param $in: The hexadecimal string representation of a timestamp to calculate. + :type $in: string + :return: The calculated timestamp. + :rtype: :php:class:`TAITime` + + Calculates a :php:class:`TAITime` from its hexadecimal string + representation. + + .. php:method:: toNumber(): float + + :return: The numeric representation of the current timestamp. + :rtype: ``float`` + + Returns the ``float`` representation of the :php:class:`TAITime` value. + + .. php:method:: fromNumber(numeric $in): TAITime + + :param $in: The arbitrary-precision floating point representation of a + timestamp to calculate. + :type $in: ``integer`` or ``float`` + :return: The calculated timestamp. + :rtype: :php:class:`TAITime` + + Calculates a :php:class:`TAITime` from its numeric (``integer`` or + ``float``) representation. + + .. php:method:: fromUTC(): TAITime + + :return: The calculated timestamp. + :rtype: :php:class:`TAITime` + + Removes the UTC leap second offset from a TAITime value. + + .. php:method:: toUTC(): TAITime + + :return: The calculated timestamp. + :rtype: :php:class:`TAITime` + + Adds the UTC leap second offset to a TAITime value. diff --git a/docs/php/installation.rst b/docs/php/installation.rst new file mode 100644 index 0000000..069c30f --- /dev/null +++ b/docs/php/installation.rst @@ -0,0 +1,46 @@ +.. _installation-php: + +.. index:: + pair: installation; PHP + +Installing Calends for PHP +========================== + +.. TODO :: + Make this actually work... + + Binary Install + -------------- + + For use with PHP, simply grab the latest version of ``calends-php`` from the + `GitHub Releases page `_, + and put it in your extension directory. Be sure to grab the correct version + for your PHP version, OS, and processor! + + Source Install + -------------- + +To install from source, you'll need a few prerequisites: + +- Go 1.9+ +- PHP 7.1+ source code (frequently something like ``php-dev``) +- `Zephir/C `_ and its dependencies + +Once those are installed, clone the repository, build ``libcalends``, then run +``zephir install`` from the ``php`` subdirectory. + +.. code-block:: bash + + # Sample Linux steps: + mkdir -p $GOPATH/src/github.com/danhunsaker + cd $GOPATH/src/github.com/danhunsaker + git clone https://github.com/danhunsaker/calends + cd calends/libcalends + go build -v -i -buildmode=c-shared -o libcalends.so + cd php + zephir install + +Adjust the above example commands as needed for your actual development OS. + +Finally, just add ``extension=calends.so`` to your ``php.ini``, and restart +PHP-FPM and/or your web server. diff --git a/docs/php/usage.rst b/docs/php/usage.rst new file mode 100644 index 0000000..eb622a7 --- /dev/null +++ b/docs/php/usage.rst @@ -0,0 +1,478 @@ +.. _usage-php: + +.. index:: + pair: usage; PHP + +.. php:namespace:: Calends + +Using Calends in PHP +==================== + +Calends exposes a very small handful of things for use outside the library +itself. One is the :php:class:`Calends` class, documented here, which should be +the only interface users of the library ever need to touch. + +Another thing Calends exposes is the :php:class:`TAITime` class, used to store +and manipulate the instants of time which make up a :php:class:`Calends` +instance. The rest are interfaces for extending the library's functionality. +These are covered in the :ref:`Custom Calendars in PHP ` +section. + +.. note:: + + :php:class:`Calends` objects are immutable - all methods return a new + :php:class:`Calends` object where they might otherwise alter the current one. + This has the side effect of using more memory to perform manipulations than + updating values on an existing object would. It makes many operations safer, + though, than mutable objects would allow. + +.. php:class:: Calends + + The main entry point to Calends and its functionality. :php:class:`Calends` + objects should only be created with the :php:meth:`Calends\\Calends::create` + function, and never directly (especially given its values are all unexported + ones). :php:class:`Calends` *does* implement the :php:class:`Serializable` + interface, though, so it's safe to :php:func:`serialize`\ /\ + :php:func:`unserialize` instances if you want. + +Create +------ + +.. php:staticmethod:: Calends::create($value, $calendar, $format) + + :param mixed $value: The value to parse the date(s)/time(s) from. + :param string $calendar: The calendar system to parse the date(s)/time(s) + with. + :param string $format: The format the date(s)/time(s) is/are expected to use. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Creates a new :php:class:`Calends` object, using ``$calendar`` to select a + calendar system, and ``$format`` to parse the contents of ``$value`` into the + :php:class:`Calends` object's internal instants. The type of ``$value`` can + vary based on the calendar system itself, but generally speaking can always + be a string. + + In any case, the value can always be an associative array, where the keys are + any two of ``start``, ``end``, and ``duration``. If all three are provided, + ``duration`` is ignored in favor of calculating it directly. If only one of + the listed keys is provided, ``$value`` is passed to the calendar system + itself unchanged. + + The calendar system then converts ``$value`` to a :php:class:`TAITime` + instant, which the :php:class:`Calends` object sets to the appropriate + internal value. + +Read +---- + + .. php:method:: date($calendar, $format) + + :param string $calendar: The calendar system to format the date/time with. + :param string $format: The format the date/time is expected to be in. + :return: The start date of the :php:class:`Calends` object + :rtype: string + :throws CalendsException: when an error occurs + + Retrieves the start date of the :php:class:`Calends` object as a string. + The value is generated by the calendar system given in ``$calendar``, + according to the format string in ``$format``. + + .. php:method:: endDate($calendar, $format) + + :param string $calendar: The calendar system to format the date/time with. + :param string $format: The format the date/time is expected to be in. + :return: The end date of the :php:class:`Calends` object + :rtype: string + :throws CalendsException: when an error occurs + + Retrieves the end date of the :php:class:`Calends` object as a string. The + value is generated by the calendar system given in ``$calendar``, + according to the format string in ``$format``. + + .. php:method:: duration() + + :return: The duration of the :php:class:`Calends` object + :rtype: string + + Retrieves the duration of the :php:class:`Calends` object as a decimal + string. This value will be ``0`` if the :php:class:`Calends` object is an + instant. + +Update +------ + + .. php:method:: withDate($value, $calendar, $format) + + :param mixed $value: The value to parse the date/time from. + :param string $calendar: The calendar system to parse the date/time with. + :param string $format: The format the date/time is expected to use. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Returns a :php:class:`Calends` object with a start date based on the + current :php:class:`Calends` object's value. The inputs are the same as + for :php:meth:`Calends\\Calends::create`, above, except the string → value + map option isn't available, since you're already specifically setting the + start value explicitly. + + .. php:method:: withEndDate($value, $calendar, $format) + + :param mixed $value: The value to parse the date/time from. + :param string $calendar: The calendar system to parse the date/time with. + :param string $format: The format the date/time is expected to use. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Returns a :php:class:`Calends` object with an end date based on the + current :php:class:`Calends` object's value. The inputs are the same as + for :php:meth:`Calends\\Calends::create`, above, except the string → value + map option isn't available, since you're already specifically setting the + end value explicitly. + + .. php:method:: withDuration($duration, $calendar) + + :param string $duration: The value to parse the new duration from. + :param string $calendar: The calendar system to parse the date/time with. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Returns a :php:class:`Calends` object with a duration set by adjusting the + current :php:class:`Calends` object's end point, and using its start point + as an anchor. The ``$duration`` value is interpreted by the calendar + system given in ``$calendar``, so is subject to any of its rules. + + .. php:method:: withDurationFromEnd($duration, $calendar) + + :param string $duration: The value to parse the new duration from. + :param string $calendar: The calendar system to parse the date/time with. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Returns a :php:class:`Calends` object with a duration set by adjusting the + current :php:class:`Calends` object's start point, and using its end point + as an anchor. The ``$duration`` value is interpreted by the calendar + system given in ``$calendar``, so is subject to any of its rules. + +Manipulate +---------- + + .. php:method:: add($offset, $calendar) + + :param string $offset: The value to parse the offset from. + :param string $calendar: The calendar system to parse the date/time with. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Increases the end date of the :php:class:`Calends` object's current value + by ``$offset``, as interpreted by the calendar system given in + ``$calendar``, and returns a new :php:class:`Calends` object with the + result. + + .. php:method:: addFromEnd($offset, $calendar) + + :param string $offset: The value to parse the offset from. + :param string $calendar: The calendar system to parse the date/time with. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Increases the start date of the :php:class:`Calends` object's current + value by ``$offset``, as interpreted by the calendar system given in + ``$calendar``, and returns a new :php:class:`Calends` object with the + result. + + .. php:method:: subtract($offset, $calendar) + + :param string $offset: The value to parse the offset from. + :param string $calendar: The calendar system to parse the date/time with. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Works the same as :php:meth:`add`, except it decreases the start date, + rather than increasing it. + + .. php:method:: subtractFromEnd($offset, $calendar) + + :param string $offset: The value to parse the offset from. + :param string $calendar: The calendar system to parse the date/time with. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Works the same as :php:meth:`addFromEnd`, except it decreases the end + date, rather than increasing it. + + .. php:method:: next($offset, $calendar) + + :param string $offset: The value to parse the offset from. + :param string $calendar: The calendar system to parse the date/time with. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Returns a :php:class:`Calends` object of ``$offset`` duration (as + interpreted by the calendar system given in ``$calendar``), which abuts + the current :php:class:`Calends` object's value. If ``$offset`` is empty, + ``$calendar`` is ignored, and the current object's duration is used + instead. + + .. php:method:: previous($offset, $calendar) + + :param string $offset: The value to parse the offset from. + :param string $calendar: The calendar system to parse the date/time with. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Returns a :php:class:`Calends` object of ``$offset`` duration (as + interpreted by the calendar system given in ``$calendar``), which abuts + the current :php:class:`Calends` object's value. If ``$offset`` is empty, + ``$calendar`` is ignored, and the current object's duration is used + instead. + +Combine +------- + + .. php:method:: merge($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to merge. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Returns a :php:class:`Calends` object spanning from the earliest start + date to the latest end date between the current :php:class:`Calends` + object and ``$c2``. + + .. php:method:: intersect($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to intersect. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Returns a :php:class:`Calends` object spanning the overlap between the + current :php:class:`Calends` object and ``$c2``. If the current object and + ``$c2`` don't overlap, returns an error. + + .. php:method:: gap($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to gap. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + + Returns a :php:class:`Calends` object spanning the gap between the current + :php:class:`Calends` object and ``$c2``. If the current object and ``$c2`` + overlap (and there is, therefore, no gap to return), returns an error. + +Compare +------- + + .. php:method:: difference($c2, $mode) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :param string $mode: The comparison mode. + :return: The difference, as a decimal string + :rtype: string + + Returns the difference of the current :php:class:`Calends` object minus + ``$c2``, using ``$mode`` to select which values to use in the calculation. + Valid ``$mode``\ s include: + + - ``start`` - use the start date of both the current object and ``$c2`` + - ``duration`` - use the duration of both the current object and ``$c2`` + - ``end`` - use the end date of both the current object and ``$c2`` + - ``start-end`` - use the start of the current object, and the end of + ``$c2`` + - ``end-start`` - use the end of the current object, and the start of + ``$c2`` + + .. php:method:: compare($c2, $mode) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :param string $mode: The comparison mode. + :return: A standard comparison result + :rtype: int + + Returns ``-1`` if the current :php:class:`Calends` object is less than + ``$c2``, ``0`` if they are equal, and ``1`` if the current object is + greater than ``$c2``, using ``$mode`` to select which values to use in the + comparison. Valid ``$mode``\ s are the same as for + :php:meth:`Calends\\Calends::difference`, above. + + .. php:method:: contains($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Checks whether the current :php:class:`Calends` object contains all of + ``$c2``. + + .. php:method:: overlaps($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Checks whether the current :php:class:`Calends` object overlaps with + ``$c2``. + + .. php:method:: abuts($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Checks whether the current :php:class:`Calends` object abuts ``$c2`` (that + is, whether one begins at the same instant the other ends). + + .. php:method:: isSame($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Checks whether the current :php:class:`Calends` object covers the same + span of time as ``$c2``. + + .. php:method:: isShorter($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Compares the duration of the current :php:class:`Calends` object and + ``$c2``. + + .. php:method:: isSameDuration($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Compares the duration of the current :php:class:`Calends` object and + ``$c2``. + + .. php:method:: isLonger($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Compares the duration of the current :php:class:`Calends` object and + ``$c2``. + + .. php:method:: isBefore($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Compares the entirety of the current :php:class:`Calends` object with the + start date of ``$c2``. + + .. php:method:: startsBefore($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Compares the start date of the current :php:class:`Calends` object with + the start date of ``$c2``. + + .. php:method:: endsBefore($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Compares the end date of the current :php:class:`Calends` object with the + start date of ``$c2``. + + .. php:method:: isDuring($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Checks whether the entirety of the current :php:class:`Calends` object + lies between the start and end dates of ``$c2``. + + .. php:method:: startsDuring($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Checks whether the start date of the current :php:class:`Calends` object + lies between the start and end dates of ``$c2``. + + .. php:method:: endsDuring($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Checks whether the end date of the current :php:class:`Calends` object + lies between the start and end dates of ``$c2``. + + .. php:method:: isAfter($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Compares the entirety of the current :php:class:`Calends` object with the + end date of ``$c2``. + + .. php:method:: startsAfter($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Compares the start date of the current :php:class:`Calends` object with + the end date of ``$c2``. + + .. php:method:: endsAfter($c2) + + :param Calends\\Calends $c2: The :php:class:`Calends` object to compare. + :return: The result of the comparison + :rtype: bool + + Compares the end date of the current :php:class:`Calends` object with the + end date of ``$c2``. + +Export +------ + +It's possible to export :php:class:`Calends` values in a couple of ways. It +implements :php:class:`Serializable` and :php:class:`JsonSerializable`, as well +as the :php:func:`__toString` method, so the regular mechanisms for each of +those are readily available and usable. In addition, it also offers support for +JSON-decoding values directly: + + .. php:staticmethod:: fromJson($encoded) + + :param string $encoded: The JSON-encoded value to import. + :return: A new :php:class:`Calends` object + :rtype: :php:class:`Calends` + :throws CalendsException: when an error occurs + +Error Handling +-------------- + +.. php:class:: CalendsException + + A very simple exception class, directly extending :php:class:`\Exception`. It + is thrown by the library for all encountered errors. diff --git a/docs/systems.rst b/docs/systems.rst new file mode 100644 index 0000000..6a56dd2 --- /dev/null +++ b/docs/systems.rst @@ -0,0 +1,48 @@ +.. _calendar-systems: + +.. index:: calendars + +Calendar Systems +================ + +The systems listed here are the built-in ones. This list is expected to grow +significantly over time, as more and more calendar systems are added. But you +can also add custom systems to your apps without waiting for us to add them +ourselves – instructions for that are in the Custom Calendars section, below. + +Throughout these documents, the term ``TAITime`` is used to refer generically to +the ``TAI64NAXURTime``, ``TAI64Time``, or ``TAITime`` type. The exact form of +this name you'll see most often varies by programming language, and is covered +in much more detail in the Custom Calendars section. + +.. toctree:: + :maxdepth: 2 + :glob: + + systems/* + +.. _custom-calendars: + +.. index:: custom calendars + +Custom Calendars +================ + +As with every other aspect of Calends, the custom calendar system support uses +the same basic flow in every language, with minor variations in each to account +for the differences those languages introduce. As with every other aspect of +Calends, though, we've opted to document each language's unique approaches +separately, so you don't have to do any mental conversions yourself. + +.. note:: + Custom calendars are considered an advanced feature, so most users woun't be + using anything detailed here. It can be nice to know how these things work + under the hood, though, for those interested in that. Select your language, + below, and dig right in! + +.. toctree:: + :maxdepth: 2 + + Golang + C/C++ + PHP diff --git a/docs/systems/gregorian.rst b/docs/systems/gregorian.rst new file mode 100644 index 0000000..8115edd --- /dev/null +++ b/docs/systems/gregorian.rst @@ -0,0 +1,26 @@ +.. _calendar-system-gregorian: + +.. index:: Gregorian Calendar, calendars; Gregorian Calendar + +The Gregorian Calendar +====================== + +Supports dates and times in the Gregorian calendar system, the current +international standard for communicating dates and times. + +*Calendar Name:* + ``gregorian`` + +*Supported Input Types:* + - string + +*Supported Format Strings:* + - Golang ``time`` package format strings (**RFC1123 layout**) + - C-style ``strptime()``/``strftime()`` format strings + +*Offsets:* + - string with Gregorian time units + + - must be relative times + - use full words instead of abbreviations for time units (such as + ``seconds`` instead of just ``s``) diff --git a/docs/systems/jdc.rst b/docs/systems/jdc.rst new file mode 100644 index 0000000..801b47c --- /dev/null +++ b/docs/systems/jdc.rst @@ -0,0 +1,36 @@ +.. _calendar-system-jdc: + +.. index:: + single: Julian Day Count + single: calendars; Julian Day Count + see: JDC; Julian Day Count + +Julian Day Count +================ + +A count of days since ``BCE 4713 Jan 01 12:00:00 UTC Julian (proleptic)``. Yes, +that's noon. This calendar system is used mostly for astronomy purposes, though +there is a modified variant with a narrower scope which counts from midnight +instead. + +*Calendar Name:* + ``jdc`` + +*Supported Input Types:* + - string + - integer + - arbitrary-precision floating point number of seconds + +*Supported Format Strings:* + - ``full`` - the full, canonical Day Count + - ``fullday`` - the full Day Count, without the fractional time part + - ``fulltime`` - just the fractional time part of the full Day Count + - ``modified`` - **an abbreviated Day Count, 2400000.5 less than the full + (starts at midnight instead of noon)** + - ``day`` - the modified Day Count, without the fractional time part + - ``time`` - just the fractional time part of the modified Day Count + +*Offsets:* + - number of days, as integer or float, via numeric or string types + + - can include fractional days to indicate time diff --git a/docs/systems/stardate.rst b/docs/systems/stardate.rst new file mode 100644 index 0000000..88f711f --- /dev/null +++ b/docs/systems/stardate.rst @@ -0,0 +1,109 @@ +.. _calendar-system-stardate: + +.. index:: Stardate, calendars; Stardate + +Stardates +========= + +Yes, the ones from :t:`Star Trek`\ ™. + +The science fiction universe of :t:`Star Trek`\ ™ introduced a calendar system +which was simultaneously futuristic (for the time) and arbitrary. Over the +decades since its initial use on screen, especially with the growing popularity +of the franchise, the "stardate" has been analyzed and explored and refined into +a number of different variants, each trying to reconcile the arbitrariness of +the on-screen system into something consistent and usable. + +This calendar system implementation is an attempt to combine all these variants +into a single system, using the format parameter to select which variant to use. +It was originally ported in 2018 from code by Aaron Chong (2015 version), under +provisions of the MIT License. My thanks for Aaron's use of the MIT License on +the original code, which allowed me to port it cleanly and legally. + +Original source: http://rinsanity.weebly.com/files/theme/stardate_public.js + +*Calendar Name:* + ``stardate`` + +*Supported Input Types:* + - string + - integer + - arbitrary-precision floating point number of seconds + +*Supported Format Strings:* + - ``main`` - **One of the older, more widely-accepted variants. + Alternately called the "issue number style" stardate, it's a combined + TOS/TNG variant, and the one used by Google Calendar. It was originally + devised by Anhrew Main in CE 1994, with revisions made through CE 1997. See + http://starchive.cs.umanitoba.ca/?stardates/ for the full explanation of + this variant.** + - ``kennedy`` - In 2006, Richie Kennedy released another combined + variant, this one designed to have a single continuous count, more like the + Julian Day Count than Main's issue number system. + - ``pugh90s`` - Steve Pugh devised 2 separate variants, one of them in + the 1990s, and the other later on, both focused on the TNG era. They are + unique in that, for negative stardates, the fractional part increases in the + opposite direction of the expected one. That is, 15129.999999999 would be + followed by 15128.000000000 instead of 15129.999999998. The original version + used an unadjusted Gregorian year as the basis for the duration of a given + range of stardates, meaning that 0.05 units refer to a larger duration of + time during a leap year than it would otherwise. + - ``pughfixed`` - The later of Steve Pugh's systems noted the discrepancy, + and opted to adjust the year length value to the actual average length of a + Gregorian year, 365.2425 days. This means 0.05 units are always the same + duration, but does mean that the Gregorian year doesn't generally start at + the same point in consecutive stardate ranges. + - ``schmidt`` - A joint effort between Andreas Schmidt and Graham + Kennedy, this variant only covers TNG-era stardates, and while it can be + used proleptically, it ignores the alternate format used prior to TNG. It is + also virtually identical to ``pugh90s``, but the fractional component + increases normally for negative stardates. + - ``guide-equiv`` - One of five variants proposed by TrekGuide.com, this is + the "out-of-universe equivalent" calculation. It isn't intended to be + accurate for any use other than personal entertainment. + - ``guide-tng`` - The second of the five TrekGuide variants, this one is + the current scale listed for TNG-era stardates, and is show-accurate (or at + least as close to it as feasible with an entirely arbitrary system). Note, + however, that it is only accurate for TNG-era dates. + - ``guide-tos`` - The third variant, then, covers the TOS-era stardates. + Again, it is only accurate to the TOS era. + - ``guide-oldtng`` - The fourth variant is no longer displayed on the + TrekGuide site, and was actually pulled from a previous version of the + stardates page. It covers the TNG era only, and uses slightly different + numbers in its calculations than the current approach - specifically, it + assumes Earth years cover 1000 stardates. + - ``guide-oldtos`` - Representing the very first set of calculations + available in archives of the TrekGuide site, the fifth TrekGuide variant + assumes that 1000 stardates are one Earth year in the TOS era, and + calculates dates based on that assumption. This variant was replaced within + seven months of that first archival, after it was noticed that TOS-era + stardates don't fit a 1000-stardate model. + + .. note:: + + This calendar system is not yet actually implemented. + + - ``aldrich`` - A proof of concept originally written in C#, this variant + results in dates very close to those produced by Pugh's and Schmidt's, but + uses a more simplified calculation to do it. + - ``red-dragon`` - A system devised by/for the Red Dragon Inn roleplaying + forum site, it uses a fixed ratio of roughly two and three quarters + stardates per Earth day. It makes no representations about accuracy outside + the context of the site itself. + - ``sto-hynes`` - John Hynes, creator of the Digital Time site, offers a + calculation for STO\ [#fsd1]_ stardates which appears to be the most accurate + variant for those interested in generating those. The system doesn't + represent itself as accurate *outside* the game, but is intentionally + proleptic. + - ``sto-academy`` - Based on an online calculator provided by the STO Academy + game help site, it is only accurate for stardates within the game, and does + not offer to calculate dates for the rest of the franchise. + - ``sto-tom`` - Another variant intended only to calculate STO stardates, + this one was attributed to Major Tom, and hosted as a Wolfram Alpha widget. + - ``sto-anthodev`` - Another STO variant, hosted on GitHub. + +*Offsets:* + - Must be provided as a string in the format ``"stardate variant"`` or + ``"variant stardate"``. + +.. [#fsd1] Star Trek™ Online diff --git a/docs/systems/tai64.rst b/docs/systems/tai64.rst new file mode 100644 index 0000000..431c279 --- /dev/null +++ b/docs/systems/tai64.rst @@ -0,0 +1,42 @@ +.. _calendar-system-tai64: + +.. index:: TAI64 Time, calendars; TAI64 Time + +TAI64 Time +========== + +Supports times that are seconds since ``CE 1970-01-01 00:00:00 TAI Gregorian`` +(plus |2^62|, when in hexadecimal), as defined at +https://cr.yp.to/libtai/tai64.html (though this library includes extensions to +the formats described there). These values are also used internally, so this +calendar system can be used to directly expose the underlying internal values in +a manner that allows them to be used elsewhere. + +*Calendar Name:* + ``tai64`` + +*Supported Input Types:* + - string + - ``TAITime`` + +*Supported Format Strings:* + - ``decimal`` - decimal; full (45 decimal places) resolution; number of + seconds since ``CE 1970-01-01 00:00:00 TAI Gregorian`` + - ``tai64`` - hexadecimal; just seconds; TAI64 External Representation + - ``tai64n`` - hexadecimal; with nanoseconds; TAI64N External + Representation + - ``tai64na`` - hexadecimal; with attoseconds; TAI64NA External + Representation + - ``tai64nax`` - hexadecimal; with xictoseconds; TAI64NAX External + Representation + - ``tai64naxu`` - hexadecimal; with uctoseconds; TAI64NAXU External + Representation + - ``tai64naxur`` - **hexadecimal; with roctoseconds**; TAI64NAXUR External + Representation + +*Offsets:* + - ``TAITime`` object + - arbitrary-precision floating point number of seconds + - string with ``decimal`` format layout (above) + +.. |2^62| replace:: 2\ :sup:`62` diff --git a/docs/systems/unix.rst b/docs/systems/unix.rst new file mode 100644 index 0000000..8d9bd3f --- /dev/null +++ b/docs/systems/unix.rst @@ -0,0 +1,27 @@ +.. _calendar-system-unix: + +.. index:: UNIX Time, calendars; UNIX Time + +UNIX Time +========= + +Supports times that are seconds since ``CE 1970-01-01 00:00:00 UTC Gregorian``, +commonly used by computer systems for storing date/time values, internally. + +*Calendar Name:* + ``unix`` + +*Supported Input Types:* + - string + - integer + - arbitrary-precision floating point number of seconds + +*Supported Format Strings:* + - values are always number of seconds since ``CE 1970-01-01 00:00:00 UTC + Gregorian`` + + - ``%d`` - integer string + - ``%f`` - **floating point string** + +*Offsets:* + - number of seconds diff --git a/docs/usage.rst b/docs/usage.rst new file mode 100644 index 0000000..3f575b7 --- /dev/null +++ b/docs/usage.rst @@ -0,0 +1,19 @@ +.. _usage: + +.. index:: usage + +Usage of Calends +================ + +Once you have :ref:`installed Calends `, you'll want to know how +to actually make use of it in your own projects. The exact approach for this +varies by language, so we've broken it into multiple sections to make it easier +to wade through and find what you need. Find your language, below, and dig in a +bit further to see how to do everything you need! + +.. toctree:: + :maxdepth: 2 + + Golang + C/C++ + PHP diff --git a/libcalends/calendars.go b/libcalends/calendars.go index 99ec337..d1b534d 100644 --- a/libcalends/calendars.go +++ b/libcalends/calendars.go @@ -167,14 +167,15 @@ func TAI64Time_decode_text(in *C.char) C.TAI64Time { } //export TAI64Time_encode_binary -func TAI64Time_encode_binary(t C.TAI64Time) unsafe.Pointer { +func TAI64Time_encode_binary(t C.TAI64Time, length *C.int) unsafe.Pointer { defer handlePanic() base := taiCToGo(t) - out, err := base.MarshalBinary() + byteStream, err := base.MarshalBinary() if err != nil { panic(err) } - return C.CBytes(out) + *length = C.int(len(byteStream)) + return C.CBytes(byteStream) } //export TAI64Time_decode_binary diff --git a/libcalends/calendars_tests.go b/libcalends/calendars_tests.go index ff4f14e..f65d9d7 100644 --- a/libcalends/calendars_tests.go +++ b/libcalends/calendars_tests.go @@ -377,18 +377,19 @@ func testTAI64Time_decode_text(t *testing.T) { func testTAI64Time_encode_binary(t *testing.T) { t.Helper() + var length C.int in := C.TAI64Time{0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} - ret := TAI64Time_encode_binary(in) + ret := TAI64Time_encode_binary(in, &length) want := "\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - if string(C.GoBytes(ret, 28)) != want { - t.Errorf("TAI64Time_encode_binary(%#v) returned %#v; wanted %#v", in, string(C.GoBytes(ret, 28)), want) + if string(C.GoBytes(ret, length)) != want { + t.Errorf("TAI64Time_encode_binary(%#v, &length) returned %d:%#v; wanted 28:%#v", in, int(length), string(C.GoBytes(ret, length)), want) } in = C.TAI64Time{10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} - ret = TAI64Time_encode_binary(in) + ret = TAI64Time_encode_binary(in, &length) want = "\x40\x00\x00\x00\x00\x00\x00\x0A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - if string(C.GoBytes(ret, 28)) != want { - t.Errorf("TAI64Time_encode_binary(%#v) returned %#v; wanted %#v", in, string(C.GoBytes(ret, 28)), want) + if string(C.GoBytes(ret, length)) != want { + t.Errorf("TAI64Time_encode_binary(%#v, &length) returned %d:%#v; wanted 28:%#v", in, int(length), string(C.GoBytes(ret, length)), want) } } diff --git a/libcalends/calends_exports.go b/libcalends/calends_exports.go index a370733..dbc81e7 100644 --- a/libcalends/calends_exports.go +++ b/libcalends/calends_exports.go @@ -129,6 +129,7 @@ func Calends_end_date(p C.ulonglong, calendar, format *C.char) *C.char { //export Calends_string func Calends_string(p C.ulonglong) *C.char { + defer handlePanic() c := instGet(p) return C.CString(c.String()) } diff --git a/libcalends/php/calends/calends.zep b/libcalends/php/calends/calends.zep index b3854ca..0630186 100644 --- a/libcalends/php/calends/calends.zep +++ b/libcalends/php/calends/calends.zep @@ -1,6 +1,6 @@ namespace Calends; -class Calends implements \Serializable//, \JsonSerializable +class Calends implements \Serializable, \JsonSerializable { private goId = 0.0; @@ -43,10 +43,27 @@ class Calends implements \Serializable//, \JsonSerializable let this->goId = result; } - // public function jsonSerialize() -> string - // { - // return this->serialize(); - // } + public function jsonSerialize() -> var + { + var out; + + let out = json_decode(Calends_encode_json(this->goId), true); + + return out; + } + + public static function fromJson(string encoded) -> + { + double result; + var out; + let out = new Calends; + + let result = Calends_decode_json(encoded); + + let out->goId = result; + + return out; + } public static function create(var date = null, string calendar = null, string format = null) -> { @@ -168,24 +185,6 @@ class Calends implements \Serializable//, \JsonSerializable return out; } - public function json(string encoded = null) -> string|null - { - if empty encoded { - string out; - - let out = Calends_encode_json(this->goId); - - return out; - } else { - double result; - - let result = Calends_decode_json(encoded); - - let this->goId = result; - } - return; - } - public function difference( z, string mode = null) -> string { string out; diff --git a/libcalends/php/config.json b/libcalends/php/config.json index b7a9a49..669f8ea 100644 --- a/libcalends/php/config.json +++ b/libcalends/php/config.json @@ -40,6 +40,11 @@ "description": "A PHP extension for handling dates and times across arbitrary calendar systems", "author": "Dan Hunsaker ", "version": "0.0.3", + "requires": { + "extensions": [ + "json" + ] + }, "extra-cflags": "-I$(readlink -f $(pwd)/../..)", "extra-libs": "-L$(readlink -f $(pwd)/../..) -lcalends", "optimizer-dirs": [ diff --git a/libcalends/php/ext/tests/053.serializing.phpt b/libcalends/php/ext/tests/053.serializing.phpt new file mode 100644 index 0000000..39b91fa --- /dev/null +++ b/libcalends/php/ext/tests/053.serializing.phpt @@ -0,0 +1,45 @@ +--TEST-- +Calends\Calends serializing Basic test +--SKIPIF-- + +--FILE-- + 0, 'end' => 10]); + $tmp11 = (string)$tmp10; + echo '__toString(1): ', $tmp11, "\n"; + $tmp12 = serialize($tmp10); + echo 'serialize(1): ', $tmp12, "\n"; + $tmp13 = unserialize($tmp12); + echo 'unserialize(1): ', (string)$tmp13, "\n"; + $tmp14 = json_encode($tmp10); + echo 'json_encode(1): ', $tmp14, "\n"; + $tmp15 = Calends\Calends::fromJson($tmp14); + echo 'fromJson(1): ', (string)$tmp15, "\n"; +?> +--EXPECT-- +__toString(0): 3FFFFFFFFFFFFFF93B747D4F3B9AC9F32E697BF03735DCC300FE4046 +serialize(0): C:15:"Calends\Calends":56:{3FFFFFFFFFFFFFF93B747D4F3B9AC9F32E697BF03735DCC300FE4046} +unserialize(0): 3FFFFFFFFFFFFFF93B747D4F3B9AC9F32E697BF03735DCC300FE4046 +json_encode(0): "3FFFFFFFFFFFFFF93B747D4F3B9AC9F32E697BF03735DCC300FE4046" +fromJson(0): 3FFFFFFFFFFFFFF93B747D4F3B9AC9F32E697BF03735DCC300FE4046 +__toString(1): from 3FFFFFFFFFFFFFF93B747D4F3B9AC9F32E697BF03735DCC300FE4046 to 40000000000000020001404F3B9AC633119BB28532DB8E0D3399E120 +serialize(1): C:15:"Calends\Calends":114:{3FFFFFFFFFFFFFF93B747D4F3B9AC9F32E697BF03735DCC300FE4046::40000000000000020001404F3B9AC633119BB28532DB8E0D3399E120} +unserialize(1): from 3FFFFFFFFFFFFFF93B747D4F3B9AC9F32E697BF03735DCC300FE4046 to 40000000000000020001404F3B9AC633119BB28532DB8E0D3399E120 +json_encode(1): {"start":"3FFFFFFFFFFFFFF93B747D4F3B9AC9F32E697BF03735DCC300FE4046","end":"40000000000000020001404F3B9AC633119BB28532DB8E0D3399E120"} +fromJson(1): from 3FFFFFFFFFFFFFF93B747D4F3B9AC9F32E697BF03735DCC300FE4046 to 40000000000000020001404F3B9AC633119BB28532DB8E0D3399E120 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..068325d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,35 @@ +alabaster==0.7.11 +argh==0.26.2 +Babel==2.6.0 +backports-abc==0.5 +certifi==2018.4.16 +chardet==3.0.4 +docutils==0.14 +futures==3.2.0 +go==1.2.1 +idna==2.7 +imagesize==1.0.0 +Jinja2==2.10 +livereload==2.5.2 +MarkupSafe==1.0 +packaging==17.1 +pathtools==0.1.2 +port-for==0.3.1 +Pygments==2.2.0 +pyparsing==2.2.0 +pytz==2018.5 +PyYAML==3.13 +requests==2.19.1 +singledispatch==3.4.0.3 +six==1.11.0 +snowballstemmer==1.2.1 +Sphinx==1.6.7 +sphinx-autobuild==0.7.1 +sphinx-rtd-theme==0.4.0 +sphinxcontrib-golangdomain==0.2.0.dev0 +sphinxcontrib-phpdomain==0.4.1 +sphinxcontrib-websupport==1.1.0 +tornado==5.1 +typing==3.6.4 +urllib3==1.23 +watchdog==0.8.3