-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathwide_strftime.sh
203 lines (196 loc) · 5.78 KB
/
wide_strftime.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
div() # args: x, y
# returns the integer before $1 / $2 ($(($1/$2)) is not OK for
# negative numbers
{
REPLY=$(($1 / $2))
[ "$(($REPLY * $2))" -gt "$1" ] && REPLY=$(($REPLY - 1))
}
days_since_epoch() # args: year, month, day
{
# returns (in $REPLY) the number of days since 1970/1/1 GMT
# valid for any date in the limit of your integer size.
# Expects y/m/d in the Gregorian calendar from 1752/9/14 on,
# and in the Julian calendar until 1752/9/2. Note that even
# though there were no days 3 nor 4 nor... 13 in September
# 1752, if you provide those you will get the date in the
# Gregorian calendar. days_since_epoch(1752, 9, 13) ==
# days_since_epoch(1752, 9, 2).
# "set" is used to avoid clobbering the variable namespace
# with a temporary variable
set -- "$((12 * ($1 + ($1 < 0)) + $2 - 3))" "$3"
div "$1" 12
set -- "$1" "$REPLY" "$2"
div "$((367 * $1 + 7))" 12
set -- "$1" "$2" "$3" "$REPLY"
div "$2" 4
set -- "$1" "$2" "$3" "$(($4 - 2 * $2 + $REPLY + $3))"
if [ "$1" -eq 21030 ] && [ "$3" -lt 3 ] || [ "$1" -lt 21030 ]; then
REPLY=$(($4 - 719471))
else
div "$2" 100
set -- "$2" "$(($4 - $REPLY))"
div "$1" 400
REPLY=$(($2 + $REPLY - 719469))
fi
}
is_leap_year() # args: year
{
[ "$(($1 % 4))" -eq 0 ] && {
[ "$(($1 % 100))" -ne 0 ] || [ "$(($1 % 400))" -eq 0 ] \
|| [ "$1" -le 1752 ]
}
}
timegm() # args: year, month, day, hour, minute, second
{
days_since_epoch "$1" "$2" "$3"
REPLY=$(($6 + 60 * ($5 + 60 * ($4 + 24 * $REPLY))))
}
wide_strftime() # args: format, seconds-since-epoch
{
# a POSIX shell strftime implementation but with a wider range
# (provided your shell numbers are 64 bit large). This one
# assumes a GMT timezone and a POSIX LC_TIME. It should be
# valid from 292,271,021,077 BCE (Julian Calendar, so, far
# before the Big Bang) to 292,277,026,596 (Gregorian
# Calendar).
# The computed date is in the Gregorian Calendar from
# 1752-9-14 on and in the Julian Calendar before (the day
# before is 1752-9-2) just as the POSIX cal(1) utility. This
# corresponds to the date when Great Britain adopted the
# Gregorian calendar, that may be different in other
# countries.
# Appart from the directives defined by SUSv3, there is also
# %J which gives the Julian day number (number of days since
# 4713/1/1 12:00 BCE), useful for google "daterange"s, and GNU
# strftime extensions %k, %l, %P, %s.
# Dates before 0001/1/1 (there is no year 0) are noted -<n>.
# -0001/1/1 is January the 1st 0001 BCE.
# Note that %C is negative, %y, %g are positive for a negative
# year. %Y is always %C%y but %C * 100 + %y only for positive
# years. %G is not always %C%g (for instance on 2199/12/31)
#
# Note that one usage of wide_strftime can be:
# wide_strftime "" 123456789
# echo "Date is $T_c"
T_s=$2
div "$2" 86400
T_d=$REPLY
T_S=$(($2 - $T_d * 86400))
T_d=$(($T_d + 719468))
T_J=$(($T_d + 1721120)) # Julian day
div "$(($T_d + 3))" 7
T_w=$(($T_d + 3 - $REPLY * 7))
REPLY="Sunday:0:Monday:1:Tuesday:2:Wednesday:3
:Thursday:4:Friday:5:Saturday:6"
T_A=${REPLY%%":$T_w"*}
T_A=${T_A##*:}
T_a=${T_A%"${T_A#???}"}
T_j=60
if [ "$T_d" -lt 640102 ]; then
T_d=$(($T_d + 2))
else
[ "$T_d" -lt 640211 ] && T_j=$(($T_j - 11))
div "$((4 * $T_d + 3))" 146097
T_d=$(($T_d + $REPLY))
div "$REPLY" 4
T_d=$(($T_d - $REPLY))
fi
div "$((4 * $T_d + 3))" 1461
T_Y=$REPLY
div "$(($T_Y * 1461))" 4
T_d=$(($T_d - $REPLY))
T_j=$(($T_j + $T_d))
T_m=$(((5 * $T_d + 2) / 153))
T_d=$(($T_d - (153 * $T_m + 2) / 5 + 1))
T_H=$(($T_S / 3600))
if [ "$T_H" -lt 12 ]; then
T_p=AM
T_P=am
T_J=$(($T_J - 1))
else
T_p=PM
T_P=pm
fi
T_I=$((($T_H + 23) % 12 + 1))
T_S=$(($T_S % 3600))
T_M=$(($T_S / 60))
T_S=$(($T_S % 60))
T_m=$(($T_m + 3))
if [ "$T_m" -gt 12 ]; then
T_Y=$(($T_Y + 1))
T_m=$(($T_m - 12))
T_j=$(($T_j - 365))
fi
[ "$T_m" -gt 2 ] && is_leap_year "$T_Y" && T_j=$(($T_j + 1))
T_G=$T_Y
T_U=$((($T_j - 1) / 7))
T_W=$T_U
REPLY=$((($T_j - 1) % 7))
[ "$REPLY" -ge "$T_w" ] && T_U=$(($T_U + 1))
[ "$REPLY" -ge "$((($T_w + 6) % 7))" ] && T_W=$(($T_W + 1))
T_V=$T_W
REPLY=$((($T_j + 7 - $T_w) % 7)) # Jan 1st week day as 0=Mo .. 6=Tu
[ "$REPLY" -gt 3 ] && T_V=$(($T_V + 1))
if [ "$T_V" -eq 0 ]; then # REPLY is 1 (Su), 2 (Sa) or 3 (Fr)
is_leap_year "$(($T_Y - 1))"
T_V=$((52 + ($REPLY > 1 + $?)))
T_G=$(($T_G - 1))
elif [ "$T_m" -eq 12 ] && [ "$(($T_d - ($T_w + 6) % 7))" -gt 28 ]
then
T_V=1
T_G=$(($T_G + 1))
fi
[ "$T_Y" -le 0 ] && T_Y=$(($T_Y - 1)) # there is no year 0
[ "$T_G" -le 0 ] && T_G=$(($T_G - 1)) # there is no year 0
REPLY="January:1:February:2:March:3:April:4:May:5:June:6:July:7:
:August:8:September:9:October:10:November:11:December:12:"
T_B=${REPLY%%":$T_m:"*}
T_B=${T_B##*:}
T_b=${T_B%"${T_B#???}"}
T_h=$T_b
T_C=${T_Y%??}
eval "$(printf '
T_Y=%.4d
T_G=%.4d
T_c="%.3s %.3s%3d %.2d:%.2d:%.2d %.4d"
T_e="%2d"
T_d=%.2d
T_I=%.2d
T_l="%2d"
T_j=%.3d
T_m=%.2d
T_H=%.2d
T_k="%2d"
T_M=%.2d
T_S=%.2d
T_U=%.2d
T_V=%.2d
T_W=%.2d
T_n="\n"
T_t="\t"' "$T_Y" "$T_G" "$T_a" "$T_b" "$T_d" "$T_H" "$T_M" "$T_S" \
"$T_Y" "$T_d" "$T_d" "$T_I" "$T_I" "$T_j" "$T_m" "$T_H" \
"$T_H" "$T_M" "$T_S" "$T_U" "$T_V" "$T_W"
)"
T_C=${T_Y%??}
T_y=${T_Y#"$T_C"}
T_g=${T_G#"${T_G%??}"}
T_D=$T_m/$T_d/$T_y
T_F=$T_Y-$T_m-$T_d
T_r="$T_I:$T_M:$T_S $T_p"
T_R=$T_H:$T_M
T_T=$T_H:$T_M:$T_S
T_x=$T_D
T_X=$T_T
T_z=+0000
T_Z=GMT
if [ -n "$1" ]; then
eval "
REPLY=$(printf '%s\n' "$1" | sed '
s/["$\\`]/\\&/g;s/,/,c/g;s/%%/,p/g
s/%\([A-DF-JMPR-Za-hj-nprstw-z]\)/${T_\1}/g
s/,p/%/g;s/,c/,/g;1s/^/"/;$s/$/"/')"
else
# optimization if you only want to access the T_* vars
REPLY=
fi
}