NAME

da.timelib.muf - time computation library

SYNOPSIS

Properties:
_time/ampm/am (string)
Name for times before noon within the locale, interpolated at "%p" in calls to .tl.strftime (default "AM").
_time/ampm/pm (string)
Name for times after noon within the locale, interpolated at "%p" in calls to .tl.strftime (default "PM").
_time/DST-cache/systime (integer)
Systime at which DST caching is valid.
_time/DST-cache/value (integer)
Cached daylight savings value.
_time/DST-rule (string)
The name or value of the daylight savings time rule within the locale.
_time/format/date-long (string)
Long date format as presented to strftime (default "%A %e %B %Y").
_time/format/date-short (string)
Short date format as presented to strftime (default "%d-%b-%Y").
_time/format/time-long (string)
Long time format as presented to strftime (default "%I:%M:%S %p").
_time/format/time-short (string)
Short time format as presented to strftime (default "%H:%M").
_time/month-long/n (string)
Name for full-length months within the locale, interpolated at "%B" in calls to .tl.strftime (January is 1, December is 12) (default is in English).
_time/month-short/n (string)
Name for abbreviated months within the locale, interpolated at "%b" in calls to .tl.strftime (Jan is 1, Dec is 12) (default is first three letters of each English month).
_time/Offset-DST (string)
Number of seconds the daylight savings time zone is ahead of UTC.
_time/Offset-STD (string)
Number of seconds the standard time zone is ahead of UTC.
_time/weekday-long/n (string)
Name for full-length weekdays within the locale, interpolated at "%A" in calls to .tl.strftime (Sunday is 0, Saturday is 6) (default is in English).
_time/weekday-short/n (string)
Name for abbreviated weekdays within the locale, interpolated at "%a" in calls to .tl.strftime (Sun is 0, Sat is 6) (default is first three letters of each English weekday).
_time/Zone-DST (string)
Name of the time zone during daylight savings time within the locale.
_time/Zone-STD (string)
Name of the time zone during standard time within the locale.

DEFINITIONS

The following terms have special meanings in the scope of the time library.
daylight savings time
(Often abbreviated to "DST".) An advancing of the time zone (usually by an hour) which takes place during part of the year to give the illusion of longer daylight hours in the summer. Also called "summer time".
daylight savings time (DST) rule
A specification of when a player goes on and off daylight savings time, for example, "on at 2 am local standard time on the first Sunday after April 1, off at 2 am local standard time on the first Sunday before November 1". Note that a DST rule does not specify either the standard or the daylight savings time zones, merely when the transitions occur.
local systime
Equivalent to systime but corrected for a local time zone. The number of non-leap seconds elapsed since 00:00:00 (midnight) January 1, 1970 in whatever time zone the term is local to (usually the Muck or a player).
locale
A description of a player's local settings, such as time zone, daylight savings time rule, time and date formats, and names for the weekdays and months, but often restricted to only the latter.
player
Normally an object of type PLAYER, but can actually be any object. For instance, the Muck's locale is stored on #0, whereas the default locale (UTC) is stored on the da.timelib.muf object.
standard time
The default time zone for a player. A player is experiencing either standard time or daylight savings time.
systime
A concept borrowed from Unix. An integer, representing the number of non-leap seconds elapsed since UTC 00:00:00 (midnight) January 1, 1970.
time zone
The difference between a player's local time and UTC, related mainly to longitude difference. While humans express the time zone offset in hours, timelib deals in seconds. A time zone is positive if it precedes UTC, that it, it is east of Greenwich. A player may have two different time zones during the year, one for standard time and one for daylight savings. Time zones have names but they are purely cosmetic to timelib.
UTC
Universal Coordinated Time. Previously called Greenwich Mean Time, it is the mean solar time as seen at longitude 0. Sometimes called "UT".

DESCRIPTION

da.timelib.muf is a suite of functions for processing date and time. You access the functions by using the macros given in the synopsis.

timelib's functions fall into two categories: those which use a locale, and those which don't. Most functions fall into the former category.

Locale functions

Most of timelib's functions refer to a single object to get that object's locale. This is usually a player but doesn't have to be. Whenever the word "local" is used here, it means that the thing being discussed is in terms of that object's locale.

Locale functions come in several flavours: Those which convert from one locale to another, those which produce a string for the current locale, and those which perform other locale-based tasks.

Locale conversion functions
This includes the macros .tl.local-to-ut, .tl.ut-to-local, .tl.my-local-to-ut, .tl.ut-to-my-local and .tl.muck-to-ut, .tl.ut-to-muck.

These macros convert from UTC to a given locale. (In fact, the last four macros are expressed in terms of the first two, and are there for convenience only.) For each of these, you need to give a systime, and in the case of .tl.local-to-ut and .tl.ut-to-local, the dbref of an object to use the locale of. If you attempt to use an object for which no locale information has been set, UTC will be used.

String locale functions
This group includes the macros .tl.date-long, .tl.date-long-format, .tl.date-short, .tl.date-short-format, .tl.time-long, .tl.time-long-format, .tl.time-short, .tl.time-short-format, .tl.locale-weekday-long, .tl.locale-weekday-short, .tl.locale-month-long, .tl.locale-month-short, .tl.strftime, .tl.my-strftime and .tl.timezonename.

Some of these, such as .tl.locale-[weekday|month]-[long|short], and .tl.[date|time]-[long|short]-format, simply return the string associated with a certain part of an object's locale. Some, such as .tl.[date|time]-[long|short], Interpolate a given local systime into these locale strings to produce a date or time in the object's preferred format. These four functions use .tl.strftime.

strftime is a standard C library function which takes a systime and produces a string in a specified format. It works by replacing certain string sequences in the format string with substrings specifying the given date and time. For instance, all occurrences of "%A" will be replaced by the string "Sunday" if the systime you gave strftime was for a Sunday, and likewise for any other day of the week. "%A" thus means that the long weekday name is inserted into the text at that point.

On the Muck, there is a primitive strftime which simply calls the C library function. The primitive has a number of shortcomings, however. The main one is that the MUF strftime is not locale-aware. It is fixed in the Muck's locale, which often isn't the locale that players want to use. Another point of confusion with MUF's strftime is that it accepts a systime local to UTC, yet it implicitly converts this time to the Muck's time zone before displaying it. The timelib version, .tl.strftime, addresses both of thes problems. It cancels the implicit converting of systimes into the Muck-local time zone, so you can call it with a local systime and be sure that it will continue to display it in that time zone. .tl.strftime is also aware of time zone and language locale. Thus "%A" will substitute "Sonntag" if you have set your locale up to use German weekday names, and "%Z" will substitute the name of your time zone at the time you ask to be formatted (which could be your standard or summer time).

This is the full listing of substitutions made by .tl.strftime:

%m
month (01-12)
%h, %b
month, short string (Jan, Feb...) (locale-aware)
%B
month, long string (January, February...) (locale-aware)
%d
day of month (01-31)
%e
day of month (1-31, space-padded)
%j
Julian day of year (001-366)
%w
day of week (0-6, 0 is Sunday)
%a
day of week, short string (Sun, Mon...) (locale-aware)
%A
day of week, long string (Sunday, Monday...) (locale-aware)
%y
year of the century (00-99)
%Y
year (4 digits)
%H
hour (00-23)
%I
hour (01-12)
%k
hour (0-23, space-padded)
%l
hour (1-12, space-padded)
%M
minute (00-59)
%S
second (00-59)
%s
seconds, same as the systime argument S[1]
%p
"AM" or "PM" (locale-aware)
%z, %Z
three letter timezone (locale-aware)
%%
percent character
%U
week number (00-53); weeks start on Sunday and must contain 4 or more days belonging to the year
%W
same as %U except weeks start on Monday
All other characters in the format string are unchanged. This is useful if you want to separate items with colons or hyphens.

Note that the MUF-primitive strftime allows space-padding of fields and the abbreviations %C, %c, %x, %d, %D, %T, %X, %R and %r. .tl.strftime does not support these, however it is easy to expand the latter into their definitions. Type man strftime at the command line to see the definitions of these substitutions.

.tl.my-strftime uses the locale of the player running the program. This is usually what you want. .tl.timezonename prints a player's time zone name assuming their current local time. This is probably not very useful and can be expressed in terms of .tl.strftime.

Other locale functions
This includes the macros .tl.dst?, .tl.systime-ut, .tl.systime-local, .tl.systime-muck and .tl.systime-my-local.

.tl.systime-* simply returns the current systime converted to a local time zone. .tl.systime-ut is identical to the MUF primitive systime. The other three are expressed in terms of .tl.systime-ut and the locale conversion functions above.

.tl.dst? returns 0 or 1, saying whether a locale is experiencing daylight savings time at the given local systime. This uses an object's locale setting for the DST rule. A DST rule looks like this:

010100:040102-0:110102-0
Each of the parts of the string between colons specifies a time that DST swaps from being off to on. (Before the start of the string is parsed, DST is off.) Each transition is in the format MMDDHH[[+|-]W]. The first six digits indicate the month, date and hour (in 24-hour format using the standard time zone) that the transition occurs at. If included, the seventh and eighth characters indicate that from the given date, you should go back (-) or forwards (+) to the next day of the week given (0 is Sunday, 6 is Saturday), not including today. Thus the above DST rule says:
On at 00:00:00 local time January 1.
Off at 02:00:00 local standard time (i.e., probably 3 am summer time) first Sunday preceding April 1.
On at 02:00:00 local standard time first Sunday preceding November 1.
This is the rule for Victoria. Note that in the southern hemisphere, the year starts in DST. The "010100:" prefix fixes that by instantly turning DST on at the first rule segment.

Note that the DST rule always works in standard time. This is why the last part of the rule above had "02" in it, not "03" (assuming that the locale's DST is one hour advanced from standard time, which in Victoria it is, but the DST rule doesn't know or care about that).

Because DST rules are a pain to set up, you can use a small number of rules which don't follow the above format, because there are global mappings from the names to the relevant rules. For instance, you can use the DST rule "USA" and you are implicitly using the global rule "040102+0:110102-0".

Parsing DST rules is expensive in terms of processor time and instruction count. As a result, any time you do a daylight savings test, the time of the test and its result are stored on the object in case you need to make a test for the same time the next time you need to check this object's DST status (it turns out that this is much more common than you might think). This DST cache is considered valid for times one minute either side of the time for which it was calculated. Note that any timelib function which uses local time, including the .tl.systime-* macros, do DST checks.

Non-locale functions

There are a few timelib functions which do not use an object's locale. .tl.leapyear? returns 0 or 1 depending on whether the year given to it is a leap year. .tl.mktime builds a systime from a series of integers indicating a time.

EXAMPLES

A particularly useful example is to convert a program using MUF primitives to one using timelib.

Say a program has the following code:

... systime "format-string" strftime ...
This string will print the time in the Muck's local time zone (even though it looks as though it should print it in UTC). If you want it to continue to print in the Muck's time zone, you would use:
... #0 .tl.systime-muck "format-string" .tl.strftime ...
This uses the Muck's locale, so that little has been gained from this. However, we can equally use a player's locale for this, probably the player running the program:
... .tl.systime-my-local "format-string" .tl.my-strftime ...
With this code, every player running the program will see the time in its own time zone using its own locale information.

If possible, try and use a player's short and long date and time formats, with the .tl.[date|time]-[long|short][-format] macros.

BUGS

timelib is only guaranteed to work up to February 28, 2100, since it is not aware of the Gregorian calendar. Since this code probably need never refer to a date 104 years after it was written, this isn't considered too important a bug.

For places with two-tiered daylight savings time it is not possible to represent the DST rule. A fix is being considered. This will alter the return value of .tl.dst?. Programs should rely on .tl.dst? returning 0 for standard time, 1 for daylight savings time and 2 or more for extra tiers of DST.

When converting from local time to UT (.tl.*-to-ut), there is an ambiguity around the time when DST ends and the clock is wound back. For instance, if the clock is wound back at 3 am summer time to 2 am standard time, then there exists a period of one hour from 2 to 3 am which exists twice that day (once with DST on and once with it off). timelib always assumes in this case that DST is off. Likewise, when the clock is wound forward at the start of DST there is a time from 2 to 3 am which doesn't exist at all. If a time inside that period is given (it shouldn't occur naturally), then timelib assumes that DST is on. These are merely arbitrary decisions which make the function local-to-ut easier to write.

Converting local time to UTC is a somewhat expensive operation in terms of processor time and instruction count. This is why the DST cache was introduced. However, the cache could theoretically be out of date by up to a minute, so be prepared for unexpected results near to a DST transition.

.tl.mktime doesn't do any bounds checking.

AUTHOR

Deborah Pickett (<Ariel> on FDCMuck).