Last active
August 29, 2015 14:22
-
-
Save deontologician/fc85b0ad35a7c3bbadef to your computer and use it in GitHub Desktop.
hextime
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Josh's homespun hexidecimal time | |
# | power | Name | equivalent | SI seconds | std equiv | kind | | |
# |-------+---------+--------------+-------------+-------------+------| | |
# | 0 | second | - | 1 second | 1 second | time | | |
# | 1 | whisp | 16 seconds | 16 seconds | ~1/4 minute | time | | |
# | 2 | yobun | 16 whisps | 256 seconds | ~4 minutes | time | | |
# | 3 | clock | 16 yobuns | 4Kis | ~1 hour | time | | |
# | 4 | loday | 16 clocks | 64Kis | ~18 hours | date | | |
# | 5 | tolfdag | 16 lodays | 1Mis | ~12 days | date | | |
# | 6 | banyon | 16 tolfdagar | 16Mis | ~6 months | era | | |
# | 7 | octavum | 16 banyons | 256Mis | ~8.5 years | era | | |
# | 8 | era | 16 octava | 1Bis | ~136 years | era | | |
# | 9 | age | 16 eras | 16Bis | ~2177 years | era | | |
import time | |
from datetime import datetime | |
from math import ceil | |
units = { | |
0: ("second", "seconds"), | |
1: ("whisp", "whisps"), | |
2: ("yobun", "yobuns"), | |
3: ("clock", "clocks"), | |
4: ("loday", "lodays"), | |
5: ("tolfdag", "tolfdagar"), | |
6: ("banyon", "banyons"), | |
7: ("octavum", "octava"), | |
8: ("era", "eras"), | |
9: ("age", "ages"), | |
} | |
tolfdag_names = [ | |
"Hydrogen", | |
"Helium", | |
"Lithium", | |
"Beryllium", | |
"Boron", | |
"Carbon", | |
"Nitrogen", | |
"Oxygen", | |
"Fluorine", | |
"Neon", | |
"Sodium", | |
"Magnesium", | |
"Aluminum", | |
"Silicon", | |
"Phosphorous", | |
"Sulfur", | |
] | |
class HexUnit(int): | |
def __add__(self, other): | |
if isinstance(other, HexUnit): | |
if self.exponent == other.exponent: | |
return type(self)(super().__add__(other)) | |
elif self.exponent < other.exponent: | |
return type(self)(self + other**(other.exponent)) | |
else: | |
return other.__add__(self) | |
else: | |
return super().__add__(other) | |
def __repr__(self): | |
return "{}({})".format(type(self).__name__, self) | |
def downcast_factory(cur_exp, target_exp, targetname): | |
slide_amt = cur_exp - target_exp | |
downcast_type = globals()[targetname.title()] | |
def _downcast(self): | |
return downcast_type(self << (4 * slide_amt)) | |
_downcast.__name__ = targetname | |
return _downcast | |
def get_downcasts(exponent): | |
downcasts = {} | |
for exp in range(exponent-1, -1, -1): | |
target_name = unit_data[exp]['name'] | |
downcasts[target_name] = downcast_factory(exponent, exp, target_name) | |
return downcasts | |
unit_data = [ | |
{"name": "second", | |
"exponent": 0, | |
"kind": "time"}, | |
{"name": "whisp", | |
"exponent": 1, | |
"kind": "time"}, | |
{"name": "yobun", | |
"exponent": 2, | |
"kind": "time"}, | |
{"name": "clock", | |
"exponent": 3, | |
"kind": "time"}, | |
{"name": "loday", | |
"exponent": 4, | |
"kind": "date"}, | |
{"name": "tolfdag", | |
"exponent": 5, | |
"plural": "tolfdagar", | |
"kind": "date"}, | |
{"name": "banyon", | |
"exponent": 6, | |
"kind": "era"}, | |
{"name": "octavum", | |
"exponent": 7, | |
"plural": "octava", | |
"kind": "era"}, | |
{"name": "era", | |
"exponent": 8, | |
"kind": "era"}, | |
{"name": "age", | |
"exponent": 9, | |
"kind": "era"}, | |
] | |
# Metaprogrammming trick to create a unit class for each | |
for metadata in unit_data: | |
classname = metadata['name'].title() | |
metadata.update(get_downcasts(metadata['exponent'])) | |
globals()[classname] = type(classname, (HexUnit,), metadata) | |
class HexInstant: | |
def __init__(self, unix_seconds): | |
self._unix_seconds = unix_seconds | |
results = [(unix_seconds >> (4 * p)) % 16 for p in range(0, 11)] | |
self.second = Second(results[0]) | |
self.whisp = Whisp(results[1]) | |
self.yobun = Yobun(results[2]) | |
self.clock = Clock(results[3]) | |
self.loday = Loday(results[4]) | |
self.tolfdag = Tolfdag(results[5]) | |
self.banyon = Banyon(results[6]) | |
self.octavum = Octavum(results[7]) | |
self.era = Era(results[8]) | |
self.age = Age(results[9]) | |
def __str__(self): | |
return "{!s},{!s},{!s}".format( | |
self.hexera(), self.hexdate(), self.hextime() | |
) | |
def __repr__(self): | |
return "{!s} {} {!s}".format( | |
self.hextime(), self.hexdate().medium_format(), self.hexera()) | |
def hextime(self): | |
return HexTime(self.clock, self.yobun, self.whisp, self.second) | |
def hexdate(self): | |
return HexDate(self.tolfdag, self.loday) | |
def hexera(self): | |
return HexEra(self.age, self.era, self.octavum, self.banyon) | |
def next(self, power): | |
val = ((self._unix_seconds >> 4 * power) + 1) << 4 * power | |
return datetime.fromtimestamp(val) | |
class HexEra: | |
def __init__(self, age, era, octavum, banyon): | |
self.age = age | |
self.era = era | |
self.octavum = octavum | |
self.banyon = banyon | |
def __repr__(self): | |
return ("{banyon}{banyon_ord} banyon of the " | |
"{octavum}{octavum_ord} octavum of the " | |
"{era}{era_ord} era".format( | |
banyon=self.banyon+1, banyon_ord=ordinal(self.banyon+1), | |
octavum=self.octavum+1, | |
octavum_ord=ordinal(self.octavum+1), | |
era=self.era+1, era_ord=ordinal(self.era+1), | |
)) | |
def __str__(self): | |
return '{:X}{:X}{:X}{:X}'.format( | |
self.age, self.era, self.octavum, self.banyon) | |
class HexDate: | |
def __init__(self, tolfdag, loday): | |
self.loday = loday | |
self.tolfdag = tolfdag | |
def __str__(self): | |
return "{:X}{:X}".format(self.tolfdag, self.loday) | |
def __repr__(self): | |
return "{}{} loday of the {} tolfdag".format( | |
self.loday+1, ordinal(self.loday+1), self.tolfdag_name) | |
def medium_format(self): | |
return "{} {}{}".format( | |
self.tolfdag_name[:3], self.loday+1, ordinal(self.loday+1)) | |
@property | |
def tolfdag_name(self): | |
return tolfdag_names[self.tolfdag] | |
class HexTime: | |
def __init__(self, clock, yobun, whisp, second): | |
self.clock = clock | |
self.yobun = yobun | |
self.whisp = whisp | |
self.second = second | |
def __str__(self): | |
return '{:X}:{:X}{:X}{:X}'.format( | |
self.clock, self.yobun, self.whisp, self.second) | |
def __repr__(self): | |
return ("{} {}, {} {}, {} {}" | |
" and {} {}".format( | |
self.clock, pluralize(self.clock, 3), | |
self.yobun, pluralize(self.yobun, 2), | |
self.whisp, pluralize(self.whisp, 1), | |
self.second, pluralize(self.second, 0))) | |
def nice_duration(duration): | |
'''Formats a duration nicely in human terms''' | |
seconds = duration.seconds % 60 | |
minutes = (duration.seconds // 60) % 60 | |
hours = (duration.seconds // 3600) % 24 | |
years = int(duration.days // 365.25) | |
years_in_days = ceil(years * 365.25) | |
month_in_days = 365.25 / 12 | |
remaining_days = duration.days - years_in_days | |
months = int(remaining_days // month_in_days) | |
months_in_days = months * month_in_days | |
remaining_days -= months_in_days | |
weeks_in_days = 7 | |
weeks = int(remaining_days // weeks_in_days) | |
remaining_days -= weeks * weeks_in_days | |
days = int(remaining_days) | |
outs = [(years, "year"), (months, "month"), (weeks, "week"), | |
(days, "day"), (hours, "hour"), (minutes, "minute"), | |
(seconds, "second")] | |
relevant = ((val, unit) for val, unit in outs if val != 0) | |
def fmt(x): | |
return "{} {}".format(*x) | |
return " ".join(fmt(pluralize_noun(*x)) for x in relevant) | |
def pluralize_noun(value, noun): | |
if value == 1: | |
return (value, noun) | |
else: | |
return (value, noun+'s') | |
def pluralize(value, power): | |
if value == 1: | |
return units[power][0] | |
else: | |
return units[power][1] | |
def ordinal(value): | |
if value in (11, 12, 13): | |
return 'th' | |
elif value % 10 == 1: | |
return 'st' | |
elif value % 10 == 2: | |
return 'nd' | |
elif value % 10 == 3: | |
return 'rd' | |
else: | |
return 'th' | |
if __name__ == '__main__': | |
seconds = int(time.time()) | |
instant = HexInstant(seconds) | |
print('time: {0!r} ({0!s})'.format(instant.hextime())) | |
print('date: {0!r} [{0!s}]'.format(instant.hexdate())) | |
print('era: {0!r} <{0!s}>'.format(instant.hexera())) | |
print('summary: {0!s} or {0!r}'.format(instant)) | |
print('loday', instant.loday + 2, 'begins in', | |
nice_duration(instant.next(4) - datetime.now())) | |
print(' {:%c}'.format(instant.next(4))) | |
print('The', instant.hexdate().tolfdag_name, 'tolfdag begins in', | |
nice_duration(instant.next(5) - datetime.now())) | |
print(' {:%c}'.format(instant.next(5))) | |
print('next banyon begins in', nice_duration(instant.next(6) - datetime.now())) | |
print(' {:%c}'.format(instant.next(6))) | |
print('next octavum begins in', nice_duration(instant.next(7) - datetime.now())) | |
print(' {:%c}'.format(instant.next(7))) | |
print('next era begins in', nice_duration(instant.next(8) - datetime.now())) | |
print(' {:%c}'.format(instant.next(8))) | |
print('next age begins in', nice_duration(instant.next(9) - datetime.now())) | |
print(' {:%c}'.format(instant.next(9))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment