We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.

Ur-time

From Esolang
Jump to navigation Jump to search

Ur-time is an esoteric timestamp representation format.

An Ur-time string represents and UTC timestamp, or civil time in the UTC timezone in before atomic clocks. There are no bounds for the timestamp that Ur-time can represent, but the strings are variable length, and timestamps farther from the present need more characters to represent time with the same precision. Ur-time strings have at least one hour of precision, but you can extend them with more characters on the right to make them more precise.

Ur-time strings are optimized to be used in the path or search form value part of a HTTP URL, or in the HTML-escaped version of such an URL.

Range and precision

Every Ur-time uses one of the characters opqrstuvwxyz exactly once, called the hour digit. If the hour digit is the last character of an Ur-time string then the precision of the timestamp is one hour; if there is one character after the hour digit then the precision is one minute; if there are two characters after the hour digit then the precision is one second; if there are four characters after the hour digit then the precision is one sixtieth of a second, etc. If an Ur-time string has characters after the hour digit then you can remove its last character to reduce its precision. You can append a ! character to any Ur-time string to increase its precision without changing its value. Each extra character at the end of the string makes the timestamp 60 times more precise.

Dates between start of the year 1870 to the end of the year 2030 have the shortest representation, with only three characters before the hour digit. Dates between the years 2030 to 4429 or between the years -530 to 1869 have four characters before the hour digit.

The encoding is centered around the epoch at 1950-01-01 00:00:00 UTC. This epoch is sometimes used for anthropology, where it's confusingly named Before/After Present. I chose this epoch to put the rollover from three to four characters before the hour digit in the near future, thus encouraging all programmers not to treat Ur-time strings as fixed length.

You can compare two Ur-time strings ASCIIbetically to compare the timestamps represented, as long as neither is a prefix of the other.

The characters before the hour digit determine a half day, encoding a UTC timezone day using the Gregorian calendar, or the proleptic Gregorian calendar for old dates. The character immediately before the hour digit selects a half day within a month. The second character before the hour digit selects a month from within five consecutive years. The third character before the hour digit selects a five year interval. If there are at least four characters before the hour digit then the fourth character before the hour digit selects a 300 year long interval, and each earlier character represents a 60 times longer interval than the next.

The hour digit selects an hour within a 12 hour long interval, that is, half of a UTC day. The character immediately after the hour digit selects a minute within the hour. The second character after the hour digit selects a second within a minute. The third character selects a sixtieth of a second within a second, and each subsequent character selects a 60 times shorter interval than the previous one.

Meaning of the start of an Ur-time

The following table illustrates how the first character of a Ur-time string determines how many characters before the hour digit are used, and what ranges each length represents; although you need more than just the first character for dates that start with ! or n.

time Ur-time
start of year -60331032530 !6!!!!!!!o!
end of year -2011032531 !Gjjjjjjlzj
start of year -2011032530 !H!!!!!!o!
end of year -67032531 !jjjjjjlzj
start of year -67032530 $!!!!!!o!
end of year -2232531 $jjjjjlzj
start of year -2232530 '!!!!!o!
start of year -1152530 (!!!!!o!
end of year -72531 (jjjjlzj
start of year -72530 )!!!!o!
start of year -54530 *!!!!o!
start of year -36530 +!!!!o!
start of year -18530 ,!!!!o!
end of year -531 ,jjjlzjj
start of year -530 -!!!o!!
start of year -230 0!!!o!!
start of year +70 1!!!o!!
start of year +370 2!!!o!!
start of year +670 3!!!o!!
start of year +970 4!!!o!!
start of year +1270 5!!!o!!
start of year +1570 6!!!o!!
1869-12-12T23:59:59.99 6jjlzjjj
1870-01-01T00:00:00.00 7!!o!!!
1875-01-01T00:00:00.00 8!!o!!!
1880-01-01T00:00:00.00 9!!o!!!
1885-01-01T00:00:00.00  :!!o!!!
1890-01-01T00:00:00.00  ;!!o!!!
1895-01-01T00:00:00.00 =!!o!!!
1900-01-01T00:00:00.00 @!!o!!!
1905-01-01T00:00:00.00 A!!o!!!
1910-01-01T00:00:00.00 B!!o!!!
1915-01-01T00:00:00.00 C!!o!!!
1920-01-01T00:00:00.00 D!!o!!!
1925-01-01T00:00:00.00 E!!o!!!
1930-01-01T00:00:00.00 F!!o!!!
1935-01-01T00:00:00.00 G!!o!!!
1940-01-01T00:00:00.00 H!!o!!!
1945-01-01T00:00:00.00 I!!o!!!
1949-12-12T23:59:59.99 Ijlzjjj
1950-01-01T00:00:00.00 J!!o!!!
1955-01-01T00:00:00.00 K!!o!!!
1960-01-01T00:00:00.00 L!!o!!!
1965-01-01T00:00:00.00 M!!o!!!
1970-01-01T00:00:00.000000000 N!!o!!!!!!!
1975-01-01T00:00:00.000000000 O!!o!!!!!!!
1980-01-01T00:00:00.000000000 P!!o!!!!!!!
1985-01-01T00:00:00.000000000 Q!!o!!!!!!!
1990-01-01T00:00:00.000000000 R!!o!!!!!!!
1995-01-01T00:00:00.000000000 S!!o!!!!!!!
2000-01-01T00:00:00.000000000 T!!o!!!!!!!
2005-01-01T00:00:00.000000000 U!!o!!!!!!!
2010-01-01T00:00:00.000000000 V!!o!!!!!!!
2015-01-01T00:00:00.000000000 W!!o!!!!!!!
2020-01-01T00:00:00.000000000 X!!o!!!!!!!
2025-01-01T00:00:00.000000000 Y!!o!!!!!!!
2029-12-12T23:59:59.999999999 Yjlzjjjjjjj
2030-01-01T00:00:00.000000000 Z!!!o!!!!!!!
start of year +2330 _!!!o!!!!!!!
start of year +2630 a!!!o!!!!!!!
start of year +2930 b!!!o!!!!!!!
start of year +3230 c!!!o!!!!!!!
start of year +3530 d!!!o!!!!!!!
start of year +3830 e!!!o!!!!!!!
start of year +4130 f!!!o!!!!!!!
end of year +4429 fjjlzjjjjjjj
start of year +4430 g!!!!o!!!!!!!
start of year +22430 h!!!!o!!!!!!!
start of year +40430 i!!!!o!!!!!!!
start of year +58430 j!!!!o!!!!!!!
end of year +76429 jjjjlzjjjjjjj
start of year +76430 k!!!!!o!!!!!!!
start of year +1156430 l!!!!!o!!!!!!!
end of year +2236429 ljjjjlzjjjjjjj
start of year +2236430 m!!!!!!o!!!!!!!
end of year +67036429 mjjjjjlzjjjjjjj
start of year +67036430 n!!!!!!!o!!!!!!!
end of year +2011036429 nGjjjjjlzjjjjjjj
start of year +2011036430 nH!!!!!!!o!!!!!!!
end of year +60331036429 nVjjjjjjlzjjjjjjj
start of year +60331036430 nW!!!!!!!!o!!!!!!!
end of year +1809931036429 ncGjjjjjjlzjjjjjjj
start of year +1809931036430 ncH!!!!!!!!o!!!!!!!
end of year +54297931036429 ng5jjjjjjjlzjjjjjjj

Meaning of the rest of the Ur-time

The following table shows how to decode characters other than the first character and the hour digit. The Ur-time string is similar to a radix 60 representation of a number, except that the hour digit and the previous two characters work differently to match the Gregorian calendar and the 24-hour clock; and that the second digit after the hour digit can have the value 60 when it encodes a leap second. The characters mn can appear only as the first character, which is why there is no meaning shown in this table. See the previous table for how to decode the first character.

between first character and hour character after hour character encoding
300 years 5 years year and month day and half-day minute second fractional second
+0 years +0 years +0 years, month 01 (January) day 1 am 0 0 0 !
+300 years +5 years +0 years, month 02 (February) day 1 pm 1 1 1 $
+600 years +10 years +0 years, month 03 (March) day 2 am 2 2 2 '
+900 years +15 years +0 years, month 04 (April) day 2 pm 3 3 3 (
+1200 years +20 years +0 years, month 05 (May) day 3 am 4 4 4 )
+1500 years +25 years +0 years, month 06 (June) day 3 pm 5 5 5 *
+1800 years +30 years +0 years, month 07 (July) day 4 am 6 6 6 +
+2100 years +35 years +0 years, month 08 (August) day 4 pm 7 7 7 ,
+2400 years +40 years +0 years, month 09 (September) day 5 am 8 8 8 -
+2700 years +45 years +0 years, month 10 (October) day 5 pm 9 9 9 0
+3000 years +50 years +0 years, month 11 (November) day 6 am 10 10 10 1
+3300 years +55 years +0 years, month 12 (December) day 6 pm 11 11 11 2
+3600 years +60 years +1 years, month 01 (January) day 7 am 12 12 12 3
+3900 years +65 years +1 years, month 02 (February) day 7 pm 13 13 13 4
+4200 years +70 years +1 years, month 03 (March) day 8 am 14 14 14 5
+4500 years +75 years +1 years, month 04 (April) day 8 pm 15 15 15 6
+4800 years +80 years +1 years, month 05 (May) day 9 am 16 16 16 7
+5100 years +85 years +1 years, month 06 (June) day 9 pm 17 17 17 8
+5400 years +90 years +1 years, month 07 (July) day 10 am 18 18 18 9
+5700 years +95 years +1 years, month 08 (August) day 10 pm 19 19 19 :
+6000 years +100 years +1 years, month 09 (September) day 11 am 20 20 20 ;
+6300 years +105 years +1 years, month 10 (October) day 11 pm 21 21 21 =
+6600 years +110 years +1 years, month 11 (November) day 12 am 22 22 22 @
+6900 years +115 years +1 years, month 12 (December) day 12 pm 23 23 23 A
+7200 years +120 years +2 years, month 01 (January) day 13 am 24 24 24 B
+7500 years +125 years +2 years, month 02 (February) day 13 pm 25 25 25 C
+7800 years +130 years +2 years, month 03 (March) day 14 am 26 26 26 D
+8100 years +135 years +2 years, month 04 (April) day 14 pm 27 27 27 E
+8400 years +140 years +2 years, month 05 (May) day 15 am 28 28 28 F
+8700 years +145 years +2 years, month 06 (June) day 15 pm 29 29 29 G
+9000 years +150 years +2 years, month 07 (July) day 16 am 30 30 30 H
+9300 years +155 years +2 years, month 08 (August) day 16 pm 31 31 31 I
+9600 years +160 years +2 years, month 09 (September) day 17 am 32 32 32 J
+9900 years +165 years +2 years, month 10 (October) day 17 pm 33 33 33 K
+10200 years +170 years +2 years, month 11 (November) day 18 am 34 34 34 L
+10500 years +175 years +2 years, month 12 (December) day 18 pm 35 35 35 M
+10800 years +180 years +3 years, month 01 (January) day 19 am 36 36 36 N
+11100 years +185 years +3 years, month 02 (February) day 19 pm 37 37 37 O
+11400 years +190 years +3 years, month 03 (March) day 20 am 38 38 38 P
+11700 years +195 years +3 years, month 04 (April) day 20 pm 39 39 39 Q
+12000 years +200 years +3 years, month 05 (May) day 21 am 40 40 40 R
+12300 years +205 years +3 years, month 06 (June) day 21 pm 41 41 41 S
+12600 years +210 years +3 years, month 07 (July) day 22 am 42 42 42 T
+12900 years +215 years +3 years, month 08 (August) day 22 pm 43 43 43 U
+13200 years +220 years +3 years, month 09 (September) day 23 am 44 44 44 V
+13500 years +225 years +3 years, month 10 (October) day 23 pm 45 45 45 W
+13800 years +230 years +3 years, month 11 (November) day 24 am 46 46 46 X
+14100 years +235 years +3 years, month 12 (December) day 24 pm 47 47 47 Y
+14400 years +240 years +4 years, month 01 (January) day 25 am 48 48 48 Z
+14700 years +245 years +4 years, month 02 (February) day 25 pm 49 49 49 _
+15000 years +250 years +4 years, month 03 (March) day 26 am 50 50 50 a
+15300 years +255 years +4 years, month 04 (April) day 26 pm 51 51 51 b
+15600 years +260 years +4 years, month 05 (May) day 27 am 52 52 52 c
+15900 years +265 years +4 years, month 06 (June) day 27 pm 53 53 53 d
+16200 years +270 years +4 years, month 07 (July) day 28 am 54 54 54 e
+16500 years +275 years +4 years, month 08 (August) day 28 pm 55 55 55 f
+16800 years +280 years +4 years, month 09 (September) day 29 am 56 56 56 g
+17100 years +285 years +4 years, month 10 (October) day 29 pm 57 57 57 h
+17400 years +290 years +4 years, month 11 (November) day 30 am 58 58 58 i
+17700 years +295 years +4 years, month 12 (December) day 30 pm 59 59 59 j
day 31 am 60 k
day 31 pm l
m
n

The following table shows how to decode the hour digit. Use the first column if the character before the hour digit represents am, or the second column if the character before the hour digit represents pm.

hour if am hour if pm encoding
00 12 o
01 13 p
02 14 q
03 15 r
04 16 s
05 17 t
06 18 u
07 19 v
08 20 w
09 21 x
10 22 y
11 23 z

Character set

Ur-time strings use a set of 76 ASCII printable characters:

   !$'()*+,-0123456789:;=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz

In particular, the characters "<>[\]^`{|}~ are not used because they might not be allowed to appear in a URL; % is not used because it is an escape character in URLs; # is not used because it separates the path or query part of the URL from the fragment part; & is not used because it is used to separate key-value pairs in the query part, and is also used as an escape character in HTML; ? is not used because it separates the path part from the query part; ./ are not used because some combinations of it can have special meanings in the path part of the URL, and so are useful to separate an Ur-time from other strings within a path segment or query value.

However, other punctuation characters like !$'(), are used because certain markup formats or parsers seem to think that they can't be part of a URL, or can't be the last character of a URL, and I want to exercise those bugs often to encourage designers of those parsers to fix them. VBulletin does not parse bare URLs if they contain a $ and parses a trailing !'), as not part of a bare URL, and does not parse double-quoted URLs at all, but at least it allows any URL if you use explicit BBCode markup. PhpBB thinks that () cannot be part of a bare or double-quoted URL but terminates one, and parses a trailing !$'()*,:;@ as not part of a bare or double-quoted URL, but also allows any URL if you use explicit BBCode markup. Simple Machines SMF thinks that $* is not part of a bare or double-quoted URL but terminates one, and parses trailing !'()+,: as not part of a bare or double-quoted URL, but also allows any URL if you use explicit BBCode markup. StackExchange parsing post body text things that () is not part of a bare URL but terminates one, and a trailing !'(), is not part of a bare URL, but also thinks that the terminating double quote is part of the double-quoted url "http://example.com/page!" ; it also thinks that () cannot be part of a footnote style URL or parenthisized URL. StackExchange chat parsing thinks that $'* are not part of a bare URL and terminates it, and a trailing !$(),:; isn't part of a bare URL. Mediawiki thinks that a trailing !),:; is not part of a bare URL, but allows any URL in square brackets. Discord chat thinks that a trailing '),:; aren't part of a bare URL, but allows any URL in angle brackets.

Name

“Ur” in the name is a pun, referencing the start of the acronym “URL”, the large city in Babylonia, since the format is based on base 60 representation like Babylonian mathematicians used, and the German word “Uhr”.

General rules

Here is how to decode an Ur-time string of arbitrary length into a date and time in the Gregorian calendar. A valid Ur-time string must have exactly one character from the set opqrstuvwxyz, called the hour digit. All other characters in the string must be from the set !$'()*+,-0123456789:;=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmn . The character immediately before the hour digit is called the half-day digit, the characters before that are the month segment.

There must be at least two characters in the month segment, call P the number of these characters. Decode each character in the month segment to its raw numeric value using the first and second columns of the following table, but then subtract 32 from the value of the very first character. All but the first of the characters in the month segment must have a raw value less than 60. Decode the month segment to the integer N by interpreting the raw values as a base 60 integer with the most significant digit first. This means that the value of the last character of the month segment (the second character before the hour digit) by 1, multiply the value of the character before that by 60, multiply the value of the character before that (if any) by 3600, and so on multiplying each character by the next power of 60. The absolute value of this number |N| must be greater than or equal to (64·60↑(P-1) − 64·30↑(P-1)) but less than (64·60↑(P-1) - 32·30↑(P-1)). (Alternately, if you decode the month segment as a base 60 fraction with the first character position worth 1/60 and each subsequent position worth 1/60 of the previous one, still subtracting 32 from the first digit before, then the absolute value of this fraction must be between (64/60 - 64/(60·2↑(P-1))) and (64/60 - 32/(60·2↑(P-1))).) From N we compute the month number, which is the floor of the number of months that the timestamp is after the epoch. If N is nonnegative then M is greater than or equal to 960·(30↑(P-2)−1)/29 and less than 960·(30↑(P-1)−1)/29, specifically M = 960·(30↑(P-2)−1)/29 − 64·60↑(P-1) + 64·30↑(P-1) + N. If N is negative then M is less than or equal to −960·(30↑(P-2)−1)/29 but greater than −960·(30↑(P-1)−1)/29, specifically M = −960·(30↑(P-2)−1)/29 + 64·60↑(P-1) − 64·30↑(P-1) + N. (I am listing ranges for M because you can use those to determine P when you want to encode a Gregorian calendar timestamp into Ur-time.) The year number is floor(M/12), the 1-based month number within the year is 1 + M - floor(M/12).

Decode the half-day digit (the digit after the month segment) into its raw value using the first and second column of the table, call this value S. The 1-based day of the month number is then 1 + floor(S/2). If this day of the month number is greater than the number of days in this months in the Gregorian calendar then the Ur-time string is invalid. Decode the hour digit to its raw value using the first and third column of the table, call this value H. The hour number is H if S is even, or 12 + H is S is odd. Decode each character after the hour digit to its raw value using the first and second column of the table. If there is at least one character after the hour digit then the raw value of the first one gives the minute number within the hour, if this value is greater than 59 then the string is invalid. If there are at least two characters then the value of the second gives the seconds within the minute, if this value is greater than 60 then the string is invalid. Any remaining characters starting from the third character after the hour digit give the fractional second: multiply the value of the first one by 1/60, and each following one by subsequent powers of 1/60, and each of those characters must have a value less than 60.

raw value normal character hour digit
0 ! o
1 $ p
2 ' q
3 ( r
4 ) s
5 * t
6 + u
7 , v
8 - w
9 0 x
10 1 y
11 2 z
12 3
13 4
14 5
15 6
16 7
17 8
18 9
19 :
20 ;
21 =
22 @
23 A
24 B
25 C
26 D
27 E
28 F
29 G
30 H
31 I
32 J
33 K
34 L
35 M
36 N
37 O
38 P
39 Q
40 R
41 S
42 T
43 U
44 V
45 W
46 X
47 Y
48 Z
49 _
50 a
51 b
52 c
53 d
54 e
55 f
56 g
57 h
58 i
59 j
60 k
61 l
62 m
63 n

See also