time-env-add 1 user commands nosh
time-env-add modify a timestamp in an environment variable
`time-env-add` --systemd-compatibility --gnu-compatibility var offset next-prog

## Description

`time-env-add` looks up the value of the `var` environment variable, parses it to yield a TAI64N time, adds offset to that time, sets the environment variable to the revised time, and then chain loads to next-prog with the execvp 3 function.

next-prog may contain its own command line options, which `time-env-add` will ignore.

## Offsets

The format of offset is series of actions, optionally separated by whitespace. Each action, in turn, comprises a decimal number (at least 1 digit long) followed by a units specifier. The unit specfiers are:

`ns`
`nsec`

  nanoseconds

`μs`
`us`
`usec`

  microseconds

`ms`
`msec`

  milliseconds

`s`
`sec`
`second`
`seconds`

  seconds

`m`
`min`
`minute`
`minutes`

  minutes

`h`
`hr`
`hour`
`hours`

  hours

`d`
`day`
`days`

  days

`w`
`wk`
`week`
`weeks`

  weeks

`fortnight`
`fortnights`

  fortnights (2 weeks)

`M`
`mon`
`month`
`months`

  months

`y`
`yr`
`year`
`years`

  years

In the default mode, each action is applied in turn to the TAI64N time. Actions fall into two categories, broadly characterized as simple arithmetic on TAI64N seconds and use of a broken-down calendar local time for calculations.

- An action whose unit is a second or subdivision of a second operates in terms of TAI seconds (as opposed to UTC seconds) and is able to yield a result TAI64N time that is a leap second. Its operation does not involve local calendar time at all. Thus (for example) a timer mechanism that uses `time-env-add` to run every 10 seconds will run every 10 physical seconds, even across a leap second. In other words, in either "posix" or "right" timezones:

  - 2016-12-31 23:59:50 +0000 plus ten seconds is 2016-12-31 23:59:60 +0000.

  - 2016-12-31 23:59:50 +0000 plus twenty seconds is 2017-01-01 00:00:09 +0000.

- An action whose unit is a minute or larger operates in terms of the current timezone's local calendar time. It can only yield a result TAI64N time that is a leap second if the current timezone is a "right" timezone; "posix" timezones will never yield the TAI64N timestamps for leap seconds, the GNU and BSD C libraries yielding an adjoining UTC second instead.

  Moreover, the action will account for whatever the library knows about the local calendar, including UTC offset changes, month lengths, and leap days. Thus (for example) a mechanism that uses `time-env-add` and the action 1month to calculate the 1st days of successive months will yield the same time on the 1st day of every month. In other words:

  - 2040-01-01 00:00:00 +0000 plus one month is 2040-02-01 00:00:00 +0000.

  - 2040-02-01 00:00:00 +0000 plus one month is 2040-03-01 00:00:00 +0000.

  - 2040-03-01 00:00:00 +0000 plus one month is 2040-04-01 00:00:00 +0000.

  - 2040-04-01 00:00:00 +0000 plus one month is 2040-05-01 00:00:00 +0000.

  Similarly, a 1minute action will yield the same point in every minute, even when those points are not uniformly spaced. In other words, in a "right" timezone:

  - 2016-12-31 23:59:50 +0000 plus one minute is 2017-01-01 00:00:50 +0000, actually 61 TAI seconds later.

  - 2016-12-31 23:59:50 +0000 plus two minutes is 2017-01-01 00:01:50 +0000, actually 121 TAI seconds later.

### Compatibility modes

#### systemd time units

The --systemd-compatibility command-line option invokes a mode that is more compatible with systemd's timer behaviour (following the actual behaviour wherever this differs from the systemd documentation).

- All actions with units of a second or smaller operate in terms of the current timezone's local calendar time. Thus leap second times can only result in "right" timezones, and will never result, for any action, in "posix" timezones.

  In a "posix" timezone, a timer mechanism using seconds as units thus will not run at uniform intervals. For example, in contrast with the default mode behaviour:

  - 2016-12-31 23:59:50 +0000 plus ten systemd seconds is 2017-01-01 00:00:00 +0000, actually 11 TAI seconds later.

  - 2016-12-31 23:59:50 +0000 plus twenty systemd seconds is 2017-01-01 00:00:10 +0000, actually 21 TAI seconds later.

- Renormalization only occurs at the end of applying all actions in total. This is the conventional behaviour of systemd timers and of the GNU implementation of the date 1 command. It is, however, not intuitive for human beings who are not programmers.

- Actions with units of a minute or larger use systemd unit sizes rather than calendar units. These unit sizes are fixed multiples of a second. In "right" timezones this yields intervals of uniform length, but calendar arithmetic that appears odd to humans. In "posix" timezones this yields normal-appearing calendar arithmetic (except for calculations involving months and years, which still appear odd), but intervals that are actually non-uniform in length.

  - One minute is always exactly 60 seconds. Thus in a "right" timezone, in contrast with the default mode behaviour:

    - 2016-12-31 23:59:50 +0000 plus one systemd minute is 2017-01-01 00:00:49 +0000, actually 60 TAI seconds later.

    - 2016-12-31 23:59:50 +0000 plus two systemd minutes is 2017-01-01 00:01:49 +0000, actually 120 TAI seconds later.

  - One hour is always exactly 3600 seconds.

  - One day is always exactly 86400 seconds.

  - One week is always exactly 604800 seconds.

  - One fortnight is always exactly 1209600 seconds.

  - One month is always exactly 2629800 seconds, irrespective of actual month length. The systemd documentation incorrectly says 30.44 days, but the actual unit used amounts to 30.4375 days, which is 30 days, 10 hours, and 30 minutes. Thus, in stark contrast to the default mode behaviour:

    - 2040-01-01 00:00:00 +0000 plus one systemd month is 2040-01-31 10:30:00 +0000.

    - 2040-02-01 00:00:00 +0000 plus one systemd month is 2040-03-02 10:30:00 +0000.

    - 2040-03-01 00:00:00 +0000 plus one systemd month is 2040-03-31 10:30:00 +0000.

    - 2040-04-01 00:00:00 +0000 plus one systemd month is 2040-05-01 10:30:00 +0000.

  - One year is always exactly 31557600 seconds, irrespective of leap days. This is exactly 12 systemd months, but is not a whole number multiple of days. Thus, in stark contrast to the default mode behaviour:

    - 2019-02-28 00:00:00 +0000 plus one systemd year is 2020-02-27 06:00:00 +0000.

    - 2020-02-27 06:00:00 +0000 plus one systemd year is 2021-02-27 12:00:00 +0000.

    - 2021-02-27 12:00:00 +0000 plus one systemd year is 2022-02-27 18:00:00 +0000.

    - 2022-02-28 18:00:00 +0000 plus one systemd year is 2023-02-28 00:00:00 +0000.

#### gnu `date`

The --gnu-compatibility command-line option invokes a mode that is a subset of the systemd compatibility mode, that is compatible with the GNU implementation of the date 1 command. This mode is mostly the same as systemd compatibility mode, except that it does not use systemd's definitions of the minute and larger units but instead uses the calendar units. Thus in a "right" timezone, in agreement with the default mode behaviour and in contrast with the systemd mode behaviour:

- 2016-12-31 23:59:50 +0000 plus ten GNU seconds is 2016-12-31 23:59:60 +0000.

- 2016-12-31 23:59:50 +0000 plus twenty GNU seconds is 2017-01-01 00:00:09 +0000.

- 2016-12-31 23:59:50 +0000 plus one GNU minute is 2017-01-01 00:00:50 +0000.

- 2016-12-31 23:59:50 +0000 plus two GNU minutes is 2017-01-01 00:01:50 +0000.

But in a "posix" timezone seconds intervals are as non-uniform as they are for systemd, in contrast with the default mode behaviour:

- 2016-12-31 23:59:50 +0000 plus ten GNU seconds is 2017-01-01 00:00:00 +0000, actually 11 TAI seconds later.

- 2016-12-31 23:59:50 +0000 plus twenty GNU seconds is 2017-01-01 00:00:10 +0000, actually 21 TAI seconds later.

## Author

Jonathan de Boyne Pollard