time manipulation functions in Pure mksh™
authorThorsten Glaser <tg@mirbsd.org>
Sun, 15 Jul 2012 14:25:15 +0000 (14:25 +0000)
committerThorsten Glaser <tg@mirbsd.org>
Sun, 15 Jul 2012 14:25:15 +0000 (14:25 +0000)
POSIX (i.e. no leap second support and Y2038 problem)

available definitions:
• tm_* constants (offset of "struct tm" members in indexed array)
• mirtime_months, mirtime_wdays (mapping to strings in the "C" locale)

available functions:
• timet2mjd ${posix_timet_seconds}
  ⇒ ${mjd_day} ${mjd_sec}
• mjd2timet ${mjd_day} ${mjd_sec}
  ⇒ ${posix_timet_seconds}
• mjd_explode ${mjd_day} ${mjd_sec}
  ⇒ ${struct_tm[@]}
• mjd_implode ${struct_tm[@]}
  ⇒ ${mjd_day} ${mjd_sec}

The functions all currently share that they take and output
an array (mirtime_mjd or struct tm) in split form, i.e. one
function argument per struct member, and space-separated output.
(This works with this data.)

Not all members of struct tm are used:

When emitting a struct tm (by mjd_explode), these are hardcoded:
• tm_isdst = 0
• tm_gmtoff = 0
• tm_zone = "UTC"

When parsing a struct tm (by mjd_implode), these are ignored:
• tm_wday, tm_yday (only tm_mon+tm_mday are used)
• tm_isdst (only tm_gmtoff is used as additional offset)
• tm_zone (it's a descriptive string anyway)

This matches behaviour of the mirtime functions in MirBSD libkern
(kernel, bootloader, libc), except that no leap second information
is available, and mksh_ari_t (32 bit) is used instead of time_t.

mksh/timefns [new file with mode: 0644]

diff --git a/mksh/timefns b/mksh/timefns
new file mode 100644 (file)
index 0000000..7732dd8
--- /dev/null
@@ -0,0 +1,179 @@
+# From MirOS: www/mk/common,v 1.4 2012/07/15 13:06:14 tg Exp $
+#-
+# Copyright © 2012
+#      Thorsten “mirabilos” Glaser <tg@mirbsd.org>
+#
+# Provided that these terms and disclaimer and all copyright notices
+# are retained or reproduced in an accompanying document, permission
+# is granted to deal in this work without restriction, including un‐
+# limited rights to use, publicly perform, distribute, sell, modify,
+# merge, give away, or sublicence.
+#
+# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
+# the utmost extent permitted by applicable law, neither express nor
+# implied; without malicious intent or gross negligence. In no event
+# may a licensor, author or contributor be held liable for indirect,
+# direct, other damage, loss, or other issues arising in any way out
+# of dealing in the work, even if advised of the possibility of such
+# damage or existence of a defect, except proven that it results out
+# of said person’s immediate fault when using the work as intended.
+#-
+# Time manipulation functions in Pure mksh™ – POSIX, no leap seconds
+#
+# Example use by the MirOS website: https://www.mirbsd.org/cvs.cgi/www/mk/
+
+# magic from MirOS: src/kern/c/mirtime.c,v 1.3 2011/11/20 23:40:10 tg Exp $
+
+# struct tm members and (POSIX) time functions
+typeset -ir tm_sec=0           # seconds [0-59]
+typeset -ir tm_min=1           # minutes [0-59]
+typeset -ir tm_hour=2          # hours [0-23]
+typeset -ir tm_mday=3          # day of month [1-31]
+typeset -ir tm_mon=4           # month of year - 1 [0-11]
+typeset -ir tm_year=5          # year - 1900
+typeset -ir tm_wday=6          # day of week [0 = sunday]      input:ignored
+typeset -ir tm_yday=7          # day of year [0-365]           input:ignored
+typeset -ir tm_isdst=8         # summer time act.? [0/1] (0)   input:ignored
+typeset -ir tm_gmtoff=9                # seconds offset from UTC (0)
+typeset -ir tm_zone=10         # abbrev. of timezone ("UTC")   input:ignored
+
+set -A mirtime_months -- Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
+set -A mirtime_wdays -- Sun Mon Tue Wed Thu Fri Sat
+readonly mirtime_months[*] mirtime_wdays[*]
+
+# $ timet2mjd posix_timet
+# ⇒ mjd sec
+function timet2mjd {
+       local -i10 mjd=$1 sec
+
+       (( sec = mjd % 86400 ))
+       (( mjd = (mjd / 86400) + 40587 ))
+       while (( sec < 0 )); do
+               (( --mjd ))
+               (( sec += 86400 ))
+       done
+
+       print -- $mjd $sec
+}
+
+# $ mjd2timet mjd sec
+# ⇒ posix_timet
+function mjd2timet {
+       local -i10 t=$1 sec=$2
+
+       (( t = (t - 40587) * 86400 + sec ))
+       print -- $t
+}
+
+# $ mjd_explode mjd sec
+# ⇒ tm_sec tm_min tm_hour tm_mday tm_mon tm_year \
+#   tm_wday tm_yday "0" "0" "UTC"
+function mjd_explode {
+       local tm
+       set -A tm
+       local -i10 sec=$2 day yday mon year=$1
+
+       while (( sec < 0 )); do
+               (( --year ))
+               (( sec += 86400 ))
+       done
+       while (( sec >= 86400 )); do
+               (( ++year ))
+               (( sec -= 86400 ))
+       done
+
+       (( day = year % 146097 + 678881 ))
+       (( year = 4 * ((year / 146097) + (day / 146097)) ))
+       (( day %= 146097 ))
+       (( tm[tm_wday] = (day + 3) % 7 ))
+       if (( day == 146096 )); then
+               (( year += 3 ))
+               (( day = 36524 ))
+       else
+               (( year += day / 36524 ))
+               (( day %= 36524 ))
+       fi
+       (( year = 4 * ((year * 25) + (day / 1461)) ))
+       (( day %= 1461 ))
+       (( yday = (day < 306) ? 1 : 0 ))
+       if (( day == 1460 )); then
+               (( year += 3 ))
+               (( day = 365 ))
+       else
+               (( year += day / 365 ))
+               (( day %= 365 ))
+       fi
+       (( yday += day ))
+       (( day *= 10 ))
+       (( mon = (day + 5) / 306 ))
+       (( day = ((day + 5) % 306) / 10 ))
+       if (( mon >= 10 )); then
+               (( mon -= 10 ))
+               (( yday -= 306 ))
+               (( ++year ))
+       else
+               (( mon += 2 ))
+               (( yday += 59 ))
+       fi
+       (( tm[tm_sec] = sec % 60 ))
+       (( sec /= 60 ))
+       (( tm[tm_min] = sec % 60 ))
+       (( tm[tm_hour] = sec / 60 ))
+       (( tm[tm_mday] = day + 1 ))
+       (( tm[tm_mon] = mon ))
+       (( tm[tm_year] = (year < 1 ? year - 1 : year) - 1900 ))
+       (( tm[tm_yday] = yday ))
+       (( tm[tm_isdst] = 0 ))
+       (( tm[tm_gmtoff] = 0 ))
+       tm[tm_zone]=UTC
+
+       print -r -- "${tm[@]}"
+}
+
+# $ mjd_implode tm_sec tm_min tm_hour tm_mday tm_mon tm_year \
+#   ignored ignored ignored tm_gmtoff [ignored]
+# ⇒ mjd sec
+function mjd_implode {
+       local tm
+       set -A tm -- "$@"
+       local -i10 day x y sec
+
+       (( sec = tm[tm_sec] + 60 * tm[tm_min] + 3600 * tm[tm_hour] - \
+           tm[tm_gmtoff] ))
+       (( (day = tm[tm_year] + 1900) < 0 )) && (( ++day ))
+       (( y = day % 400 ))
+       (( day = (day / 400) * 146097 - 678882 + tm[tm_mday] ))
+       while (( sec < 0 )); do
+               (( --day ))
+               (( sec += 86400 ))
+       done
+       while (( sec >= 86400 )); do
+               (( ++day ))
+               (( sec -= 86400 ))
+       done
+       (( x = tm[tm_mon] ))
+       while (( x < 0 )); do
+               (( --y ))
+               (( x += 12 ))
+       done
+       (( y += x / 12 ))
+       (( x %= 12 ))
+       if (( x < 2 )); then
+               (( x += 10 ))
+               (( --y ))
+       else
+               (( x -= 2 ))
+       fi
+       (( day += (306 * x + 5) / 10 ))
+       while (( y < 0 )); do
+               (( day -= 146097 ))
+               (( y += 400 ))
+       done
+       (( day += 146097 * (y / 400) ))
+       (( y %= 400 ))
+       (( day += 365 * (y % 4) ))
+       (( y /= 4 ))
+       (( day += 1461 * (y % 25) + 36524 * (y / 25) ))
+
+       print -- $day $sec
+}