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
|
#!/usr/bin/env python3
import datetime
JST = datetime.timezone(datetime.timedelta(hours=9))
class RadikoTime(datetime.datetime):
def timestring(self):
return self.strftime("%Y%m%d%H%M%S")
def broadcast_day(self):
# timetable api counts 05:00 -> 28:59 (04:59 next day) as all the same day
# like the 30-hour day, 06:00 -> 29:59 (05:59)
# https://en.wikipedia.org/wiki/Date_and_time_notation_in_Japan#Time
# but ends earlier, presumably so the early morning programmes dont look like late night ones
# this means we have to shift back by a day so we can get the right programme for stuff past midnight
dt = self
if dt.hour < 5:
dt -= datetime.timedelta(days=1)
dt = dt.date() # dont care about hours/mins, we're working in days
return dt
def broadcast_day_string(self):
dt = self.broadcast_day()
return dt.strftime("%Y%m%d")
def broadcast_day_end(self):
date = self.broadcast_day()
date += datetime.timedelta(days=1)
dt = datetime.datetime(date.year, date.month, date.day, 5, 0, 0, tzinfo=JST)
return dt
def broadcast_day_start(self):
date = self.broadcast_day()
dt = datetime.datetime(date.year, date.month, date.day, 5, 0, 0, tzinfo=JST)
return dt
class RadikoSiteTime(RadikoTime):
def __new__(self, timestring):
timestring = str(timestring)
year = int(timestring[:4]); month = int(timestring[4:6]); day = int(timestring[6:8])
hour = min(int(timestring[8:10]), 24)
minute = min(int(timestring[10:12]), 59)
second = timestring[12:14]
# edge cases
next_day = False # hour is 24, meaning 00 the next day
no_second = second == "" # there's no second, meaning we have to -1 second for some reason
if hour > 23:
hour = hour - 24
next_day = True
if not no_second:
second = min(int(second), 59)
else:
second = 0
rt = super().__new__(RadikoTime, year, month, day, hour, minute, second, tzinfo = JST)
if next_day:
rt += datetime.timedelta(days=1)
if no_second:
rt -= datetime.timedelta(seconds=1)
return rt
if __name__ == "__main__":
# normal
assert RadikoSiteTime('20230823180000').timestring() == "20230823180000"
# seconds (clamped to 59)
assert RadikoSiteTime('20230819105563').timestring() == "20230819105559"
# minutes (clamped to 59)
assert RadikoSiteTime('20230819106200').timestring() == "20230819105900"
# hours (clamped to 23)
assert RadikoSiteTime('20230819240000').timestring() == "20230820000000"
# cursed (no seconds) - seems to do -1s
assert RadikoSiteTime('202308240100').timestring() == "20230824005959"
# broadcast day starts at 05:00, ends at 04:59 (29:59)
assert RadikoSiteTime('20230824030000').broadcast_day() == '20230823'
assert RadikoSiteTime('20230824130000').broadcast_day_end() == datetime.datetime(2023, 8, 25, 5, 0, 0, tzinfo=JST)
assert RadikoSiteTime('20230824030000').broadcast_day_end() == datetime.datetime(2023, 8, 24, 5, 0, 0, tzinfo=JST)
# checking timezone
assert RadikoSiteTime('20230823090000').timestamp() == 1692748800
class RadikoShareTime(RadikoTime):
def __new__(self, timestring):
timestring = str(timestring)
year = int(timestring[:4]); month = int(timestring[4:6]); day = int(timestring[6:8])
hour = int(timestring[8:10]); minute = int(timestring[10:12]); second = int(timestring[12:14])
minutes_to_add = second // 60
second = second % 60
minute += minutes_to_add
hours_to_add = minute // 60
minute = minute % 60
hour += hours_to_add
days_to_add = hour // 24
hour = hour % 24
days_to_add += max(0, day - 28)
day = min(day, 28)
# site won't handle month out of range (eg 2023-13-05), thank fuck
rt = super().__new__(RadikoTime, year, month, day, hour, minute, second, tzinfo = JST)
rt += datetime.timedelta(days=days_to_add)
return rt
if __name__ == "__main__":
assert RadikoShareTime('20230630296200').timestring() == '20230701060200' # 30-hour + >59 minutes
assert RadikoShareTime('20230630235960').timestring() == '20230701000000' # month boundary
assert RadikoShareTime('20240229010000').timestring() == '20240229010000' # feb 29th in leap year
assert RadikoShareTime('20230229010000').timestring() == '20230301010000' # feb 29th in not-leap year
assert RadikoShareTime('20230823180000').timestring() == '20230823180000' # normal
|