1 // Copyright 2019 yuzu emulator team
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include <climits>
6 
7 #include "common/assert.h"
8 #include "common/logging/log.h"
9 #include "core/file_sys/content_archive.h"
10 #include "core/file_sys/nca_metadata.h"
11 #include "core/file_sys/registered_cache.h"
12 #include "core/file_sys/romfs.h"
13 #include "core/file_sys/system_archive/system_archive.h"
14 #include "core/hle/service/time/time_zone_manager.h"
15 
16 namespace Service::Time::TimeZone {
17 
18 static constexpr s32 epoch_year{1970};
19 static constexpr s32 year_base{1900};
20 static constexpr s32 epoch_week_day{4};
21 static constexpr s32 seconds_per_minute{60};
22 static constexpr s32 minutes_per_hour{60};
23 static constexpr s32 hours_per_day{24};
24 static constexpr s32 days_per_week{7};
25 static constexpr s32 days_per_normal_year{365};
26 static constexpr s32 days_per_leap_year{366};
27 static constexpr s32 months_per_year{12};
28 static constexpr s32 seconds_per_hour{seconds_per_minute * minutes_per_hour};
29 static constexpr s32 seconds_per_day{seconds_per_hour * hours_per_day};
30 static constexpr s32 years_per_repeat{400};
31 static constexpr s64 average_seconds_per_year{31556952};
32 static constexpr s64 seconds_per_repeat{years_per_repeat * average_seconds_per_year};
33 
34 struct Rule {
35     enum class Type : u32 { JulianDay, DayOfYear, MonthNthDayOfWeek };
36     Type rule_type{};
37     s32 day{};
38     s32 week{};
39     s32 month{};
40     s32 transition_time{};
41 };
42 
43 struct CalendarTimeInternal {
44     s64 year{};
45     s8 month{};
46     s8 day{};
47     s8 hour{};
48     s8 minute{};
49     s8 second{};
CompareService::Time::TimeZone::CalendarTimeInternal50     int Compare(const CalendarTimeInternal& other) const {
51         if (year != other.year) {
52             if (year < other.year) {
53                 return -1;
54             }
55             return 1;
56         }
57         if (month != other.month) {
58             return month - other.month;
59         }
60         if (day != other.day) {
61             return day - other.day;
62         }
63         if (hour != other.hour) {
64             return hour - other.hour;
65         }
66         if (minute != other.minute) {
67             return minute - other.minute;
68         }
69         if (second != other.second) {
70             return second - other.second;
71         }
72         return {};
73     }
74 };
75 
76 template <typename TResult, typename TOperand>
SafeAdd(TResult & result,TOperand op)77 static bool SafeAdd(TResult& result, TOperand op) {
78     result = result + op;
79     return true;
80 }
81 
82 template <typename TResult, typename TUnit, typename TBase>
SafeNormalize(TResult & result,TUnit & unit,TBase base)83 static bool SafeNormalize(TResult& result, TUnit& unit, TBase base) {
84     TUnit delta{};
85     if (unit >= 0) {
86         delta = unit / base;
87     } else {
88         delta = -1 - (-1 - unit) / base;
89     }
90     unit -= delta * base;
91     return SafeAdd(result, delta);
92 }
93 
94 template <typename T>
IsLeapYear(T year)95 static constexpr bool IsLeapYear(T year) {
96     return ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0);
97 }
98 
99 template <typename T>
GetYearLengthInDays(T year)100 static constexpr T GetYearLengthInDays(T year) {
101     return IsLeapYear(year) ? days_per_leap_year : days_per_normal_year;
102 }
103 
GetLeapDaysFromYearPositive(s64 year)104 static constexpr s64 GetLeapDaysFromYearPositive(s64 year) {
105     return year / 4 - year / 100 + year / years_per_repeat;
106 }
107 
GetLeapDaysFromYear(s64 year)108 static constexpr s64 GetLeapDaysFromYear(s64 year) {
109     if (year < 0) {
110         return -1 - GetLeapDaysFromYearPositive(-1 - year);
111     } else {
112         return GetLeapDaysFromYearPositive(year);
113     }
114 }
115 
GetMonthLength(bool is_leap_year,int month)116 static constexpr int GetMonthLength(bool is_leap_year, int month) {
117     constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
118     constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30,
119                                                      31, 31, 30, 31, 30, 31};
120     return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
121 }
122 
IsDigit(char value)123 static constexpr bool IsDigit(char value) {
124     return value >= '0' && value <= '9';
125 }
126 
GetQZName(const char * name,int offset,char delimiter)127 static constexpr int GetQZName(const char* name, int offset, char delimiter) {
128     while (name[offset] != '\0' && name[offset] != delimiter) {
129         offset++;
130     }
131     return offset;
132 }
133 
GetTZName(const char * name,int offset)134 static constexpr int GetTZName(const char* name, int offset) {
135     for (char value{name[offset]};
136          value != '\0' && !IsDigit(value) && value != ',' && value != '-' && value != '+';
137          offset++) {
138         value = name[offset];
139     }
140     return offset;
141 }
142 
GetInteger(const char * name,int & offset,int & value,int min,int max)143 static constexpr bool GetInteger(const char* name, int& offset, int& value, int min, int max) {
144     value = 0;
145     char temp{name[offset]};
146     if (!IsDigit(temp)) {
147         return {};
148     }
149     do {
150         value = value * 10 + (temp - '0');
151         if (value > max) {
152             return {};
153         }
154         temp = name[offset];
155     } while (IsDigit(temp));
156 
157     return value >= min;
158 }
159 
GetSeconds(const char * name,int & offset,int & seconds)160 static constexpr bool GetSeconds(const char* name, int& offset, int& seconds) {
161     seconds = 0;
162     int value{};
163     if (!GetInteger(name, offset, value, 0, hours_per_day * days_per_week - 1)) {
164         return {};
165     }
166     seconds = value * seconds_per_hour;
167 
168     if (name[offset] == ':') {
169         offset++;
170         if (!GetInteger(name, offset, value, 0, minutes_per_hour - 1)) {
171             return {};
172         }
173         seconds += value * seconds_per_minute;
174         if (name[offset] == ':') {
175             offset++;
176             if (!GetInteger(name, offset, value, 0, seconds_per_minute)) {
177                 return {};
178             }
179             seconds += value;
180         }
181     }
182     return true;
183 }
184 
GetOffset(const char * name,int & offset,int & value)185 static constexpr bool GetOffset(const char* name, int& offset, int& value) {
186     bool is_negative{};
187     if (name[offset] == '-') {
188         is_negative = true;
189         offset++;
190     } else if (name[offset] == '+') {
191         offset++;
192     }
193     if (!GetSeconds(name, offset, value)) {
194         return {};
195     }
196     if (is_negative) {
197         value = -value;
198     }
199     return true;
200 }
201 
GetRule(const char * name,int & position,Rule & rule)202 static constexpr bool GetRule(const char* name, int& position, Rule& rule) {
203     bool is_valid{};
204     if (name[position] == 'J') {
205         position++;
206         rule.rule_type = Rule::Type::JulianDay;
207         is_valid = GetInteger(name, position, rule.day, 1, days_per_normal_year);
208     } else if (name[position] == 'M') {
209         position++;
210         rule.rule_type = Rule::Type::MonthNthDayOfWeek;
211         is_valid = GetInteger(name, position, rule.month, 1, months_per_year);
212         if (!is_valid) {
213             return {};
214         }
215         if (name[position++] != '.') {
216             return {};
217         }
218         is_valid = GetInteger(name, position, rule.week, 1, 5);
219         if (!is_valid) {
220             return {};
221         }
222         if (name[position++] != '.') {
223             return {};
224         }
225         is_valid = GetInteger(name, position, rule.day, 0, days_per_week - 1);
226     } else if (isdigit(name[position])) {
227         rule.rule_type = Rule::Type::DayOfYear;
228         is_valid = GetInteger(name, position, rule.day, 0, days_per_leap_year - 1);
229     } else {
230         return {};
231     }
232     if (!is_valid) {
233         return {};
234     }
235     if (name[position] == '/') {
236         position++;
237         return GetOffset(name, position, rule.transition_time);
238     } else {
239         rule.transition_time = 2 * seconds_per_hour;
240     }
241     return true;
242 }
243 
TransitionTime(int year,Rule rule,int offset)244 static constexpr int TransitionTime(int year, Rule rule, int offset) {
245     int value{};
246     switch (rule.rule_type) {
247     case Rule::Type::JulianDay:
248         value = (rule.day - 1) * seconds_per_day;
249         if (IsLeapYear(year) && rule.day >= 60) {
250             value += seconds_per_day;
251         }
252         break;
253     case Rule::Type::DayOfYear:
254         value = rule.day * seconds_per_day;
255         break;
256     case Rule::Type::MonthNthDayOfWeek: {
257         // Use Zeller's Congruence (https://en.wikipedia.org/wiki/Zeller%27s_congruence) to
258         // calculate the day of the week for any Julian or Gregorian calendar date.
259         const int m1{(rule.month + 9) % 12 + 1};
260         const int yy0{(rule.month <= 2) ? (year - 1) : year};
261         const int yy1{yy0 / 100};
262         const int yy2{yy0 % 100};
263         int day_of_week{((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7};
264 
265         if (day_of_week < 0) {
266             day_of_week += days_per_week;
267         }
268         int day{rule.day - day_of_week};
269         if (day < 0) {
270             day += days_per_week;
271         }
272         for (int i{1}; i < rule.week; i++) {
273             if (day + days_per_week >= GetMonthLength(IsLeapYear(year), rule.month - 1)) {
274                 break;
275             }
276             day += days_per_week;
277         }
278 
279         value = day * seconds_per_day;
280         for (int index{}; index < rule.month - 1; ++index) {
281             value += GetMonthLength(IsLeapYear(year), index) * seconds_per_day;
282         }
283         break;
284     }
285     default:
286         UNREACHABLE();
287     }
288     return value + rule.transition_time + offset;
289 }
290 
ParsePosixName(const char * name,TimeZoneRule & rule)291 static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
292     constexpr char default_rule[]{",M4.1.0,M10.5.0"};
293     const char* std_name{name};
294     int std_len{};
295     int offset{};
296     int std_offset{};
297 
298     if (name[offset] == '<') {
299         offset++;
300         std_name = name + offset;
301         const int std_name_offset{offset};
302         offset = GetQZName(name, offset, '>');
303         if (name[offset] != '>') {
304             return {};
305         }
306         std_len = offset - std_name_offset;
307         offset++;
308     } else {
309         offset = GetTZName(name, offset);
310         std_len = offset;
311     }
312     if (std_len == 0) {
313         return {};
314     }
315     if (!GetOffset(name, offset, std_offset)) {
316         return {};
317     }
318 
319     int char_count{std_len + 1};
320     int dest_len{};
321     int dest_offset{};
322     const char* dest_name{name + offset};
323     if (rule.chars.size() < std::size_t(char_count)) {
324         return {};
325     }
326 
327     if (name[offset] != '\0') {
328         if (name[offset] == '<') {
329             dest_name = name + (++offset);
330             const int dest_name_offset{offset};
331             offset = GetQZName(name, offset, '>');
332             if (name[offset] != '>') {
333                 return {};
334             }
335             dest_len = offset - dest_name_offset;
336             offset++;
337         } else {
338             dest_name = name + (offset);
339             offset = GetTZName(name, offset);
340             dest_len = offset;
341         }
342         if (dest_len == 0) {
343             return {};
344         }
345         char_count += dest_len + 1;
346         if (rule.chars.size() < std::size_t(char_count)) {
347             return {};
348         }
349         if (name[offset] != '\0' && name[offset] != ',' && name[offset] != ';') {
350             if (!GetOffset(name, offset, dest_offset)) {
351                 return {};
352             }
353         } else {
354             dest_offset = std_offset - seconds_per_hour;
355         }
356         if (name[offset] == '\0') {
357             name = default_rule;
358             offset = 0;
359         }
360         if (name[offset] == ',' || name[offset] == ';') {
361             offset++;
362 
363             Rule start{};
364             if (!GetRule(name, offset, start)) {
365                 return {};
366             }
367             if (name[offset++] != ',') {
368                 return {};
369             }
370 
371             Rule end{};
372             if (!GetRule(name, offset, end)) {
373                 return {};
374             }
375             if (name[offset] != '\0') {
376                 return {};
377             }
378 
379             rule.type_count = 2;
380             rule.ttis[0].gmt_offset = -dest_offset;
381             rule.ttis[0].is_dst = true;
382             rule.ttis[0].abbreviation_list_index = std_len + 1;
383             rule.ttis[1].gmt_offset = -std_offset;
384             rule.ttis[1].is_dst = false;
385             rule.ttis[1].abbreviation_list_index = 0;
386             rule.default_type = 0;
387 
388             s64 jan_first{};
389             int time_count{};
390             int jan_offset{};
391             int year_beginning{epoch_year};
392             do {
393                 const int year_seconds{GetYearLengthInDays(year_beginning - 1) * seconds_per_day};
394                 year_beginning--;
395                 if (!SafeAdd(jan_first, -year_seconds)) {
396                     jan_offset = -year_seconds;
397                     break;
398                 }
399             } while (epoch_year - years_per_repeat / 2 < year_beginning);
400 
401             int year_limit{year_beginning + years_per_repeat + 1};
402             int year{};
403             for (year = year_beginning; year < year_limit; year++) {
404                 int start_time{TransitionTime(year, start, std_offset)};
405                 int end_time{TransitionTime(year, end, dest_offset)};
406                 const int year_seconds{GetYearLengthInDays(year) * seconds_per_day};
407                 const bool is_reversed{end_time < start_time};
408                 if (is_reversed) {
409                     int swap{start_time};
410                     start_time = end_time;
411                     end_time = swap;
412                 }
413 
414                 if (is_reversed ||
415                     (start_time < end_time &&
416                      (end_time - start_time < (year_seconds + (std_offset - dest_offset))))) {
417                     if (rule.ats.size() - 2 < std::size_t(time_count)) {
418                         break;
419                     }
420 
421                     rule.ats[time_count] = jan_first;
422                     if (SafeAdd(rule.ats[time_count], jan_offset + start_time)) {
423                         rule.types[time_count++] = is_reversed ? 1 : 0;
424                     } else if (jan_offset != 0) {
425                         rule.default_type = is_reversed ? 1 : 0;
426                     }
427 
428                     rule.ats[time_count] = jan_first;
429                     if (SafeAdd(rule.ats[time_count], jan_offset + end_time)) {
430                         rule.types[time_count++] = is_reversed ? 0 : 1;
431                         year_limit = year + years_per_repeat + 1;
432                     } else if (jan_offset != 0) {
433                         rule.default_type = is_reversed ? 0 : 1;
434                     }
435                 }
436                 if (!SafeAdd(jan_first, jan_offset + year_seconds)) {
437                     break;
438                 }
439                 jan_offset = 0;
440             }
441             rule.time_count = time_count;
442             if (time_count == 0) {
443                 rule.type_count = 1;
444             } else if (years_per_repeat < year - year_beginning) {
445                 rule.go_back = true;
446                 rule.go_ahead = true;
447             }
448         } else {
449             if (name[offset] == '\0') {
450                 return {};
451             }
452 
453             s64 their_std_offset{};
454             for (int index{}; index < rule.time_count; ++index) {
455                 const s8 type{rule.types[index]};
456                 if (rule.ttis[type].is_standard_time_daylight) {
457                     their_std_offset = -rule.ttis[type].gmt_offset;
458                 }
459             }
460 
461             s64 their_offset{their_std_offset};
462             for (int index{}; index < rule.time_count; ++index) {
463                 const s8 type{rule.types[index]};
464                 rule.types[index] = rule.ttis[type].is_dst ? 1 : 0;
465                 if (!rule.ttis[type].is_gmt) {
466                     if (!rule.ttis[type].is_standard_time_daylight) {
467                         rule.ats[index] += dest_offset - their_std_offset;
468                     } else {
469                         rule.ats[index] += std_offset - their_std_offset;
470                     }
471                 }
472                 their_offset = -rule.ttis[type].gmt_offset;
473                 if (!rule.ttis[type].is_dst) {
474                     their_std_offset = their_offset;
475                 }
476             }
477             rule.ttis[0].gmt_offset = -std_offset;
478             rule.ttis[0].is_dst = false;
479             rule.ttis[0].abbreviation_list_index = 0;
480             rule.ttis[1].gmt_offset = -dest_offset;
481             rule.ttis[1].is_dst = true;
482             rule.ttis[1].abbreviation_list_index = std_len + 1;
483             rule.type_count = 2;
484             rule.default_type = 0;
485         }
486     } else {
487         // Default is standard time
488         rule.type_count = 1;
489         rule.time_count = 0;
490         rule.default_type = 0;
491         rule.ttis[0].gmt_offset = -std_offset;
492         rule.ttis[0].is_dst = false;
493         rule.ttis[0].abbreviation_list_index = 0;
494     }
495 
496     rule.char_count = char_count;
497     for (int index{}; index < std_len; ++index) {
498         rule.chars[index] = std_name[index];
499     }
500 
501     rule.chars[std_len++] = '\0';
502     if (dest_len != 0) {
503         for (int index{}; index < dest_len; ++index) {
504             rule.chars[std_len + index] = dest_name[index];
505         }
506         rule.chars[std_len + dest_len] = '\0';
507     }
508 
509     return true;
510 }
511 
ParseTimeZoneBinary(TimeZoneRule & time_zone_rule,FileSys::VirtualFile & vfs_file)512 static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFile& vfs_file) {
513     TzifHeader header{};
514     if (vfs_file->ReadObject<TzifHeader>(&header) != sizeof(TzifHeader)) {
515         return {};
516     }
517 
518     constexpr s32 time_zone_max_leaps{50};
519     constexpr s32 time_zone_max_chars{50};
520     if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps &&
521           0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) &&
522           0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) &&
523           0 <= header.char_count && header.char_count < time_zone_max_chars &&
524           (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) &&
525           (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) {
526         return {};
527     }
528     time_zone_rule.time_count = header.time_count;
529     time_zone_rule.type_count = header.type_count;
530     time_zone_rule.char_count = header.char_count;
531 
532     int time_count{};
533     u64 read_offset = sizeof(TzifHeader);
534     for (int index{}; index < time_zone_rule.time_count; ++index) {
535         s64_be at{};
536         vfs_file->ReadObject<s64_be>(&at, read_offset);
537         time_zone_rule.types[index] = 1;
538         if (time_count != 0 && at <= time_zone_rule.ats[time_count - 1]) {
539             if (at < time_zone_rule.ats[time_count - 1]) {
540                 return {};
541             }
542             time_zone_rule.types[index - 1] = 0;
543             time_count--;
544         }
545         time_zone_rule.ats[time_count++] = at;
546         read_offset += sizeof(s64_be);
547     }
548     time_count = 0;
549     for (int index{}; index < time_zone_rule.time_count; ++index) {
550         const u8 type{*vfs_file->ReadByte(read_offset)};
551         read_offset += sizeof(u8);
552         if (time_zone_rule.time_count <= type) {
553             return {};
554         }
555         if (time_zone_rule.types[index] != 0) {
556             time_zone_rule.types[time_count++] = type;
557         }
558     }
559     time_zone_rule.time_count = time_count;
560     for (int index{}; index < time_zone_rule.type_count; ++index) {
561         TimeTypeInfo& ttis{time_zone_rule.ttis[index]};
562         u32_be gmt_offset{};
563         vfs_file->ReadObject<u32_be>(&gmt_offset, read_offset);
564         read_offset += sizeof(u32_be);
565         ttis.gmt_offset = gmt_offset;
566 
567         const u8 dst{*vfs_file->ReadByte(read_offset)};
568         read_offset += sizeof(u8);
569         if (dst >= 2) {
570             return {};
571         }
572         ttis.is_dst = dst != 0;
573 
574         const s32 abbreviation_list_index{*vfs_file->ReadByte(read_offset)};
575         read_offset += sizeof(u8);
576         if (abbreviation_list_index >= time_zone_rule.char_count) {
577             return {};
578         }
579         ttis.abbreviation_list_index = abbreviation_list_index;
580     }
581 
582     vfs_file->ReadArray(time_zone_rule.chars.data(), time_zone_rule.char_count, read_offset);
583     time_zone_rule.chars[time_zone_rule.char_count] = '\0';
584     read_offset += time_zone_rule.char_count;
585     for (int index{}; index < time_zone_rule.type_count; ++index) {
586         if (header.ttis_std_count == 0) {
587             time_zone_rule.ttis[index].is_standard_time_daylight = false;
588         } else {
589             const u8 dst{*vfs_file->ReadByte(read_offset)};
590             read_offset += sizeof(u8);
591             if (dst >= 2) {
592                 return {};
593             }
594             time_zone_rule.ttis[index].is_standard_time_daylight = dst != 0;
595         }
596     }
597 
598     for (int index{}; index < time_zone_rule.type_count; ++index) {
599         if (header.ttis_std_count == 0) {
600             time_zone_rule.ttis[index].is_gmt = false;
601         } else {
602             const u8 dst{*vfs_file->ReadByte(read_offset)};
603             read_offset += sizeof(u8);
604             if (dst >= 2) {
605                 return {};
606             }
607             time_zone_rule.ttis[index].is_gmt = dst != 0;
608         }
609     }
610 
611     const u64 position{(read_offset - sizeof(TzifHeader))};
612     const s64 bytes_read = s64(vfs_file->GetSize() - sizeof(TzifHeader) - position);
613     if (bytes_read < 0) {
614         return {};
615     }
616     constexpr s32 time_zone_name_max{255};
617     if (bytes_read > (time_zone_name_max + 1)) {
618         return {};
619     }
620 
621     std::array<char, time_zone_name_max + 1> temp_name{};
622     vfs_file->ReadArray(temp_name.data(), bytes_read, read_offset);
623     if (bytes_read > 2 && temp_name[0] == '\n' && temp_name[bytes_read - 1] == '\n' &&
624         std::size_t(time_zone_rule.type_count) + 2 <= time_zone_rule.ttis.size()) {
625         temp_name[bytes_read - 1] = '\0';
626 
627         std::array<char, time_zone_name_max> name{};
628         std::memcpy(name.data(), temp_name.data() + 1, std::size_t(bytes_read - 1));
629 
630         TimeZoneRule temp_rule;
631         if (ParsePosixName(name.data(), temp_rule)) {
632             UNIMPLEMENTED();
633         }
634     }
635     if (time_zone_rule.type_count == 0) {
636         return {};
637     }
638     if (time_zone_rule.time_count > 1) {
639         UNIMPLEMENTED();
640     }
641 
642     s32 default_type{};
643 
644     for (default_type = 0; default_type < time_zone_rule.time_count; default_type++) {
645         if (time_zone_rule.types[default_type] == 0) {
646             break;
647         }
648     }
649 
650     default_type = default_type < time_zone_rule.time_count ? -1 : 0;
651     if (default_type < 0 && time_zone_rule.time_count > 0 &&
652         time_zone_rule.ttis[time_zone_rule.types[0]].is_dst) {
653         default_type = time_zone_rule.types[0];
654         while (--default_type >= 0) {
655             if (!time_zone_rule.ttis[default_type].is_dst) {
656                 break;
657             }
658         }
659     }
660     if (default_type < 0) {
661         default_type = 0;
662         while (time_zone_rule.ttis[default_type].is_dst) {
663             if (++default_type >= time_zone_rule.type_count) {
664                 default_type = 0;
665                 break;
666             }
667         }
668     }
669     time_zone_rule.default_type = default_type;
670     return true;
671 }
672 
CreateCalendarTime(s64 time,int gmt_offset,CalendarTimeInternal & calendar_time,CalendarAdditionalInfo & calendar_additional_info)673 static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time,
674                                      CalendarAdditionalInfo& calendar_additional_info) {
675     s64 year{epoch_year};
676     s64 time_days{time / seconds_per_day};
677     s64 remaining_seconds{time % seconds_per_day};
678     while (time_days < 0 || time_days >= GetYearLengthInDays(year)) {
679         s64 delta = time_days / days_per_leap_year;
680         if (!delta) {
681             delta = time_days < 0 ? -1 : 1;
682         }
683         s64 new_year{year};
684         if (!SafeAdd(new_year, delta)) {
685             return ERROR_OUT_OF_RANGE;
686         }
687         time_days -= (new_year - year) * days_per_normal_year;
688         time_days -= GetLeapDaysFromYear(new_year - 1) - GetLeapDaysFromYear(year - 1);
689         year = new_year;
690     }
691 
692     s64 day_of_year{time_days};
693     remaining_seconds += gmt_offset;
694     while (remaining_seconds < 0) {
695         remaining_seconds += seconds_per_day;
696         day_of_year--;
697     }
698 
699     while (remaining_seconds >= seconds_per_day) {
700         remaining_seconds -= seconds_per_day;
701         day_of_year++;
702     }
703 
704     while (day_of_year < 0) {
705         if (!SafeAdd(year, -1)) {
706             return ERROR_OUT_OF_RANGE;
707         }
708         day_of_year += GetYearLengthInDays(year);
709     }
710 
711     while (day_of_year >= GetYearLengthInDays(year)) {
712         day_of_year -= GetYearLengthInDays(year);
713         if (!SafeAdd(year, 1)) {
714             return ERROR_OUT_OF_RANGE;
715         }
716     }
717 
718     calendar_time.year = year;
719     calendar_additional_info.day_of_year = static_cast<u32>(day_of_year);
720     s64 day_of_week{
721         (epoch_week_day +
722          ((year - epoch_year) % days_per_week) * (days_per_normal_year % days_per_week) +
723          GetLeapDaysFromYear(year - 1) - GetLeapDaysFromYear(epoch_year - 1) + day_of_year) %
724         days_per_week};
725     if (day_of_week < 0) {
726         day_of_week += days_per_week;
727     }
728 
729     calendar_additional_info.day_of_week = static_cast<u32>(day_of_week);
730     calendar_time.hour = static_cast<s8>((remaining_seconds / seconds_per_hour) % seconds_per_hour);
731     remaining_seconds %= seconds_per_hour;
732     calendar_time.minute = static_cast<s8>(remaining_seconds / seconds_per_minute);
733     calendar_time.second = static_cast<s8>(remaining_seconds % seconds_per_minute);
734 
735     for (calendar_time.month = 0;
736          day_of_year >= GetMonthLength(IsLeapYear(year), calendar_time.month);
737          ++calendar_time.month) {
738         day_of_year -= GetMonthLength(IsLeapYear(year), calendar_time.month);
739     }
740 
741     calendar_time.day = static_cast<s8>(day_of_year + 1);
742     calendar_additional_info.is_dst = false;
743     calendar_additional_info.gmt_offset = gmt_offset;
744 
745     return RESULT_SUCCESS;
746 }
747 
ToCalendarTimeInternal(const TimeZoneRule & rules,s64 time,CalendarTimeInternal & calendar_time,CalendarAdditionalInfo & calendar_additional_info)748 static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
749                                          CalendarTimeInternal& calendar_time,
750                                          CalendarAdditionalInfo& calendar_additional_info) {
751     if ((rules.go_ahead && time < rules.ats[0]) ||
752         (rules.go_back && time > rules.ats[rules.time_count - 1])) {
753         s64 seconds{};
754         if (time < rules.ats[0]) {
755             seconds = rules.ats[0] - time;
756         } else {
757             seconds = time - rules.ats[rules.time_count - 1];
758         }
759         seconds--;
760 
761         const s64 years{(seconds / seconds_per_repeat + 1) * years_per_repeat};
762         seconds = years * average_seconds_per_year;
763 
764         s64 new_time{time};
765         if (time < rules.ats[0]) {
766             new_time += seconds;
767         } else {
768             new_time -= seconds;
769         }
770         if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) {
771             return ERROR_TIME_NOT_FOUND;
772         }
773         if (const ResultCode result{
774                 ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)};
775             result != RESULT_SUCCESS) {
776             return result;
777         }
778         if (time < rules.ats[0]) {
779             calendar_time.year -= years;
780         } else {
781             calendar_time.year += years;
782         }
783 
784         return RESULT_SUCCESS;
785     }
786 
787     s32 tti_index{};
788     if (rules.time_count == 0 || time < rules.ats[0]) {
789         tti_index = rules.default_type;
790     } else {
791         s32 low{1};
792         s32 high{rules.time_count};
793         while (low < high) {
794             s32 mid{(low + high) >> 1};
795             if (time < rules.ats[mid]) {
796                 high = mid;
797             } else {
798                 low = mid + 1;
799             }
800         }
801         tti_index = rules.types[low - 1];
802     }
803 
804     if (const ResultCode result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset,
805                                                    calendar_time, calendar_additional_info)};
806         result != RESULT_SUCCESS) {
807         return result;
808     }
809 
810     calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst;
811     const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]};
812     for (int index{}; time_zone[index] != '\0'; ++index) {
813         calendar_additional_info.timezone_name[index] = time_zone[index];
814     }
815     return RESULT_SUCCESS;
816 }
817 
ToCalendarTimeImpl(const TimeZoneRule & rules,s64 time,CalendarInfo & calendar)818 static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) {
819     CalendarTimeInternal calendar_time{};
820     const ResultCode result{
821         ToCalendarTimeInternal(rules, time, calendar_time, calendar.additiona_info)};
822     calendar.time.year = static_cast<s16>(calendar_time.year);
823 
824     // Internal impl. uses 0-indexed month
825     calendar.time.month = static_cast<s8>(calendar_time.month + 1);
826 
827     calendar.time.day = calendar_time.day;
828     calendar.time.hour = calendar_time.hour;
829     calendar.time.minute = calendar_time.minute;
830     calendar.time.second = calendar_time.second;
831     return result;
832 }
833 
834 TimeZoneManager::TimeZoneManager() = default;
835 TimeZoneManager::~TimeZoneManager() = default;
836 
ToCalendarTime(const TimeZoneRule & rules,s64 time,CalendarInfo & calendar) const837 ResultCode TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time,
838                                            CalendarInfo& calendar) const {
839     return ToCalendarTimeImpl(rules, time, calendar);
840 }
841 
SetDeviceLocationNameWithTimeZoneRule(const std::string & location_name,FileSys::VirtualFile & vfs_file)842 ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
843                                                                   FileSys::VirtualFile& vfs_file) {
844     TimeZoneRule rule{};
845     if (ParseTimeZoneBinary(rule, vfs_file)) {
846         device_location_name = location_name;
847         time_zone_rule = rule;
848         return RESULT_SUCCESS;
849     }
850     return ERROR_TIME_ZONE_CONVERSION_FAILED;
851 }
852 
SetUpdatedTime(const Clock::SteadyClockTimePoint & value)853 ResultCode TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) {
854     time_zone_update_time_point = value;
855     return RESULT_SUCCESS;
856 }
857 
ToCalendarTimeWithMyRules(s64 time,CalendarInfo & calendar) const858 ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const {
859     if (is_initialized) {
860         return ToCalendarTime(time_zone_rule, time, calendar);
861     } else {
862         return ERROR_UNINITIALIZED_CLOCK;
863     }
864 }
865 
ParseTimeZoneRuleBinary(TimeZoneRule & rules,FileSys::VirtualFile & vfs_file) const866 ResultCode TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules,
867                                                     FileSys::VirtualFile& vfs_file) const {
868     if (!ParseTimeZoneBinary(rules, vfs_file)) {
869         return ERROR_TIME_ZONE_CONVERSION_FAILED;
870     }
871     return RESULT_SUCCESS;
872 }
873 
ToPosixTime(const TimeZoneRule & rules,const CalendarTime & calendar_time,s64 & posix_time) const874 ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules,
875                                         const CalendarTime& calendar_time, s64& posix_time) const {
876     posix_time = 0;
877 
878     CalendarTimeInternal internal_time{
879         .year = calendar_time.year,
880         // Internal impl. uses 0-indexed month
881         .month = static_cast<s8>(calendar_time.month - 1),
882         .day = calendar_time.day,
883         .hour = calendar_time.hour,
884         .minute = calendar_time.minute,
885         .second = calendar_time.second,
886     };
887 
888     s32 hour{internal_time.hour};
889     s32 minute{internal_time.minute};
890     if (!SafeNormalize(hour, minute, minutes_per_hour)) {
891         return ERROR_OVERFLOW;
892     }
893     internal_time.minute = static_cast<s8>(minute);
894 
895     s32 day{internal_time.day};
896     if (!SafeNormalize(day, hour, hours_per_day)) {
897         return ERROR_OVERFLOW;
898     }
899     internal_time.day = static_cast<s8>(day);
900     internal_time.hour = static_cast<s8>(hour);
901 
902     s64 year{internal_time.year};
903     s64 month{internal_time.month};
904     if (!SafeNormalize(year, month, months_per_year)) {
905         return ERROR_OVERFLOW;
906     }
907     internal_time.month = static_cast<s8>(month);
908 
909     if (!SafeAdd(year, year_base)) {
910         return ERROR_OVERFLOW;
911     }
912 
913     while (day <= 0) {
914         if (!SafeAdd(year, -1)) {
915             return ERROR_OVERFLOW;
916         }
917         s64 temp_year{year};
918         if (1 < internal_time.month) {
919             ++temp_year;
920         }
921         day += static_cast<s32>(GetYearLengthInDays(temp_year));
922     }
923 
924     while (day > days_per_leap_year) {
925         s64 temp_year{year};
926         if (1 < internal_time.month) {
927             temp_year++;
928         }
929         day -= static_cast<s32>(GetYearLengthInDays(temp_year));
930         if (!SafeAdd(year, 1)) {
931             return ERROR_OVERFLOW;
932         }
933     }
934 
935     while (true) {
936         const s32 month_length{GetMonthLength(IsLeapYear(year), internal_time.month)};
937         if (day <= month_length) {
938             break;
939         }
940         day -= month_length;
941         internal_time.month++;
942         if (internal_time.month >= months_per_year) {
943             internal_time.month = 0;
944             if (!SafeAdd(year, 1)) {
945                 return ERROR_OVERFLOW;
946             }
947         }
948     }
949     internal_time.day = static_cast<s8>(day);
950 
951     if (!SafeAdd(year, -year_base)) {
952         return ERROR_OVERFLOW;
953     }
954     internal_time.year = year;
955 
956     s32 saved_seconds{};
957     if (internal_time.second >= 0 && internal_time.second < seconds_per_minute) {
958         saved_seconds = 0;
959     } else if (year + year_base < epoch_year) {
960         s32 second{internal_time.second};
961         if (!SafeAdd(second, 1 - seconds_per_minute)) {
962             return ERROR_OVERFLOW;
963         }
964         saved_seconds = second;
965         internal_time.second = 1 - seconds_per_minute;
966     } else {
967         saved_seconds = internal_time.second;
968         internal_time.second = 0;
969     }
970 
971     s64 low{LLONG_MIN};
972     s64 high{LLONG_MAX};
973     while (true) {
974         s64 pivot{low / 2 + high / 2};
975         if (pivot < low) {
976             pivot = low;
977         } else if (pivot > high) {
978             pivot = high;
979         }
980         s32 direction{};
981         CalendarTimeInternal candidate_calendar_time{};
982         CalendarAdditionalInfo unused{};
983         if (ToCalendarTimeInternal(rules, pivot, candidate_calendar_time, unused) !=
984             RESULT_SUCCESS) {
985             if (pivot > 0) {
986                 direction = 1;
987             } else {
988                 direction = -1;
989             }
990         } else {
991             direction = candidate_calendar_time.Compare(internal_time);
992         }
993         if (!direction) {
994             const s64 time_result{pivot + saved_seconds};
995             if ((time_result < pivot) != (saved_seconds < 0)) {
996                 return ERROR_OVERFLOW;
997             }
998             posix_time = time_result;
999             break;
1000         } else {
1001             if (pivot == low) {
1002                 if (pivot == LLONG_MAX) {
1003                     return ERROR_TIME_NOT_FOUND;
1004                 }
1005                 pivot++;
1006                 low++;
1007             } else if (pivot == high) {
1008                 if (pivot == LLONG_MIN) {
1009                     return ERROR_TIME_NOT_FOUND;
1010                 }
1011                 pivot--;
1012                 high--;
1013             }
1014             if (low > high) {
1015                 return ERROR_TIME_NOT_FOUND;
1016             }
1017             if (direction > 0) {
1018                 high = pivot;
1019             } else {
1020                 low = pivot;
1021             }
1022         }
1023     }
1024     return RESULT_SUCCESS;
1025 }
1026 
ToPosixTimeWithMyRule(const CalendarTime & calendar_time,s64 & posix_time) const1027 ResultCode TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time,
1028                                                   s64& posix_time) const {
1029     if (is_initialized) {
1030         return ToPosixTime(time_zone_rule, calendar_time, posix_time);
1031     }
1032     posix_time = 0;
1033     return ERROR_UNINITIALIZED_CLOCK;
1034 }
1035 
GetDeviceLocationName(LocationName & value) const1036 ResultCode TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
1037     if (!is_initialized) {
1038         return ERROR_UNINITIALIZED_CLOCK;
1039     }
1040     std::memcpy(value.data(), device_location_name.c_str(), device_location_name.size());
1041     return RESULT_SUCCESS;
1042 }
1043 
1044 } // namespace Service::Time::TimeZone
1045