| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module time |
| 5 | |
| 6 | // unix returns a Time calculated from the given Unix timestamp in seconds since 1970-01-01 . |
| 7 | pub fn unix(epoch i64) Time { |
| 8 | return unix_nanosecond(epoch, 0) |
| 9 | } |
| 10 | |
| 11 | // unix_milli returns a Time calculated from the given Unix timestamp in milliseconds since 1970-01-01 . |
| 12 | pub fn unix_milli(ms i64) Time { |
| 13 | return ts_to_time_impl(ms, 1_000, 1_000_000) |
| 14 | } |
| 15 | |
| 16 | // unix_micro returns a Time calculated from the given Unix timestamp in microseconds since 1970-01-01 . |
| 17 | pub fn unix_micro(us i64) Time { |
| 18 | return ts_to_time_impl(us, 1_000_000, 1_000) |
| 19 | } |
| 20 | |
| 21 | // unix_nano returns a Time calculated from the given Unix timestamp in nanoseconds since 1970-01-01 . |
| 22 | pub fn unix_nano(ns i64) Time { |
| 23 | return ts_to_time_impl(ns, 1_000_000_000, 1) |
| 24 | } |
| 25 | |
| 26 | fn ts_to_time_impl(value i64, down i64, up i64) Time { |
| 27 | epoch := value / down |
| 28 | remainder := (value % down) * up |
| 29 | return unix_nanosecond(epoch, int(remainder)) |
| 30 | } |
| 31 | |
| 32 | // unix_microsecond returns a Time struct, given an Unix timestamp in seconds, and a microsecond value. |
| 33 | pub fn unix_microsecond(epoch i64, microsecond int) Time { |
| 34 | return unix_nanosecond(epoch, microsecond * 1000) |
| 35 | } |
| 36 | |
| 37 | // unix_nanosecond returns a Time struct given a Unix timestamp in seconds and a nanosecond value. |
| 38 | pub fn unix_nanosecond(abs_unix_timestamp i64, nanosecond int) Time { |
| 39 | // Split into day and time |
| 40 | mut day_offset := abs_unix_timestamp / seconds_per_day |
| 41 | if abs_unix_timestamp % seconds_per_day < 0 { |
| 42 | // Compensate for rounding towards zero on integers as we want floored instead |
| 43 | day_offset-- |
| 44 | } |
| 45 | year, month, day := calculate_date_from_day_offset(day_offset) |
| 46 | hour_, minute_, second_ := |
| 47 | calculate_time_from_second_offset(abs_unix_timestamp % seconds_per_day) |
| 48 | return Time{ |
| 49 | year: year |
| 50 | month: month |
| 51 | day: day |
| 52 | hour: hour_ |
| 53 | minute: minute_ |
| 54 | second: second_ |
| 55 | nanosecond: nanosecond |
| 56 | unix: abs_unix_timestamp |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | // calculate_date_from_day_offset returns the year, month, and day based on the given day offset. |
| 61 | fn calculate_date_from_day_offset(day_offset_ i64) (int, int, int) { |
| 62 | mut day_offset := day_offset_ |
| 63 | |
| 64 | // source: http://howardhinnant.github.io/date_algorithms.html#civil_from_days |
| 65 | |
| 66 | // shift from 1970-01-01 to 0000-03-01 |
| 67 | day_offset += 719468 // int(days_per_400_years * 1970 / 400 - (28+31)) |
| 68 | |
| 69 | mut era := 0 |
| 70 | if day_offset >= 0 { |
| 71 | era = int(day_offset / days_per_400_years) |
| 72 | } else { |
| 73 | era = int((day_offset - days_per_400_years - 1) / days_per_400_years) |
| 74 | } |
| 75 | |
| 76 | // day_of_era => [0..146096] |
| 77 | day_of_era := day_offset - era * days_per_400_years |
| 78 | |
| 79 | // year_of_era => [0..399] |
| 80 | year_of_era := (day_of_era - day_of_era / (days_per_4_years - 1) + |
| 81 | day_of_era / days_per_100_years - day_of_era / (days_per_400_years - 1)) / days_in_year |
| 82 | |
| 83 | mut year := int(year_of_era + era * 400) |
| 84 | |
| 85 | // day_of_year => with year beginning Mar 1 [0..365] |
| 86 | day_of_year := day_of_era - (days_in_year * year_of_era + year_of_era / 4 - year_of_era / 100) |
| 87 | |
| 88 | month_position := (5 * day_of_year + 2) / 153 |
| 89 | day := int(day_of_year - (153 * month_position + 2) / 5 + 1) |
| 90 | mut month := int(month_position) |
| 91 | |
| 92 | if month_position < 10 { |
| 93 | month += 3 |
| 94 | } else { |
| 95 | month -= 9 |
| 96 | } |
| 97 | |
| 98 | if month <= 2 { |
| 99 | year += 1 |
| 100 | } |
| 101 | |
| 102 | return year, month, day |
| 103 | } |
| 104 | |
| 105 | // calculate_time_from_second_offset returns the hour, minute, and second |
| 106 | // based on the given second offset. |
| 107 | fn calculate_time_from_second_offset(second_offset_ i64) (int, int, int) { |
| 108 | mut second_offset := second_offset_ |
| 109 | if second_offset < 0 { |
| 110 | second_offset += seconds_per_day |
| 111 | } |
| 112 | hour_ := second_offset / seconds_per_hour |
| 113 | second_offset %= seconds_per_hour |
| 114 | minute_ := second_offset / seconds_per_minute |
| 115 | second_offset %= seconds_per_minute |
| 116 | return int(hour_), int(minute_), int(second_offset) |
| 117 | } |
| 118 | |