github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekatime/time.go (about)

     1  // Copyright © 2020. All rights reserved.
     2  // Author: Ilya Stroy.
     3  // Contacts: iyuryevich@pm.me, https://github.com/qioalice
     4  // License: https://opensource.org/licenses/MIT
     5  
     6  package ekatime
     7  
     8  type (
     9  	// Hour is a special type that has enough space to store Hour's number.
    10  	// Valid values: [0..23].
    11  	Hour int8
    12  
    13  	// Minute is a special type that has enough space to store Minute's number.
    14  	// Valid values: [0..59].
    15  	Minute int8
    16  
    17  	// Second is a special type that has enough space to store Second's number.
    18  	// Valid values: [0..59].
    19  	Second int8
    20  
    21  	// Time is a special object that has enough space to store a some Time (Clock)
    22  	// (including Hour, Minute, Second) but does it most RAM efficient way
    23  	// taking only 4 bytes.
    24  	Time uint32
    25  )
    26  
    27  // IsValidTime reports whether 'h', 'm' and 's' in their valid ranges.
    28  func IsValidTime(h Hour, m Minute, s Second) bool {
    29  	return h >= 0 && h <= 23 && m >= 0 && m <= 59 && s >= 0 && s <= 59
    30  }
    31  
    32  // IsValid is an alias for IsValidTime().
    33  func (t Time) IsValid() bool {
    34  	return IsValidTime(t.Split())
    35  }
    36  
    37  // Hour returns the hour number the current Time includes which.
    38  //
    39  // It guarantees that Hour() returns the valid hour number Time is of,
    40  // only if Time has not been created manually but using constructors
    41  // like NewTime(), Timestamp.Time(), Timestamp.Split(), etc.
    42  func (t Time) Hour() Hour {
    43  	return Hour(t>>_TIME_OFFSET_HOUR) & _TIME_MASK_HOUR
    44  }
    45  
    46  // Minute returns the minute number the current Time includes which.
    47  //
    48  // It guarantees that Minute() returns the valid minute number Time is of,
    49  // only if Time has not been created manually but using constructors
    50  // like NewTime(), Timestamp.Time(), Timestamp.Split(), etc.
    51  func (t Time) Minute() Minute {
    52  	return Minute(t>>_TIME_OFFSET_MINUTE) & _TIME_MASK_MINUTE
    53  }
    54  
    55  // Second returns the second number the current Time includes which.
    56  //
    57  // It guarantees that Second() returns the valid hour number Time is of,
    58  // only if Time has not been created manually but using constructors
    59  // like NewTime(), Timestamp.Time(), Timestamp.Split(), etc.
    60  func (t Time) Second() Second {
    61  	return Second(t>>_TIME_OFFSET_SECOND) & _TIME_MASK_SECOND
    62  }
    63  
    64  // Split returns the hour number, minutes number and seconds number the current Date
    65  // includes which.
    66  // It's just like a separate Hour(), Minute(), Second() calls.
    67  func (t Time) Split() (h Hour, m Minute, s Second) {
    68  	return t.Hour(), t.Minute(), t.Second()
    69  }
    70  
    71  // NewTime creates a new Time object using provided hour number, minute number,
    72  // second number, normalizing these values and shifting time if it's required.
    73  //
    74  // Totally, it just stores the provided data if values are in their valid ranges,
    75  // like: [0..23] for hour, [0..59] for minutes and seconds.
    76  //
    77  // If they are not, the time may be (will be) shifted. E.g:
    78  // 21:02:64 (h == 21, m == 2, s == 64) -> 21:03:04 (h == 21, m == 3, s == 4).
    79  func NewTime(h Hour, m Minute, s Second) Time {
    80  
    81  	h, m, s = normalizeTime(h, m, s)
    82  
    83  	// Do not forgot 'bitwise AND' between h, m, s and their bitmasks
    84  	// if you will change the logic of getting valid h, m, s from invalid ones.
    85  	// Now it's unnecessary (redundant).
    86  	//h, m, s = h & _TIME_MASK_HOUR, m & _TIME_MASK_MINUTE, s & _TIME_MASK_SECOND
    87  
    88  	return (Time(h) << _TIME_OFFSET_HOUR) |
    89  		(Time(m) << _TIME_OFFSET_MINUTE) |
    90  		(Time(s) << _TIME_OFFSET_SECOND)
    91  }
    92  
    93  // Replace returns a new Time based on the current.
    94  // It returns the current Time with changed Hour, Minute, Second to those passed values,
    95  // which are in their allowed ranges. Does not doing time addition. Only replacing.
    96  // For time addition, use Add() method.
    97  //
    98  // Examples:
    99  //  NewTime(12, 13, 14)    // -> 12:13:14
   100  //    .Replace(13, 1, 2)   // -> 13:01:02
   101  //    .Replace(20, -2, 4)  // -> 20:01:04
   102  //    .Replace(1, 0, -50)  // -> 01:00:04
   103  //    .Replace(24, 61, 30) // -> 01:00:30
   104  func (t Time) Replace(h Hour, m Minute, s Second) Time {
   105  	h_, m_, s_ := t.Split()
   106  	if 0 <= h && h <= 23 {
   107  		h_ = h
   108  	}
   109  	if 0 <= m && m <= 59 {
   110  		m_ = m
   111  	}
   112  	if 0 <= s && s <= 59 {
   113  		s_ = s
   114  	}
   115  	return NewTime(h_, m_, s_)
   116  }
   117  
   118  // Add returns a new Time based on the current.
   119  // It returns the current Time with changed Hour, Minute, Second, using passed values,
   120  // as their addition's deltas.
   121  //
   122  // Examples:
   123  //  NewTime(12, 13, 14) // -> 12:13:14
   124  //    .Add(1, 2, 3)     // -> 13:15:17
   125  //    .Add(-3, 0, 20)   // -> 10:15:37
   126  //    .Add(-23, 0, 61)  // -> 11:16:38
   127  //    .Add(0, -60, 0)   // -> 10:16:38
   128  //    .Add(127, 0, 0)   // -> 17:16:38 (works OK with potential integer overflow)
   129  func (t Time) Add(h Hour, m Minute, s Second) Time {
   130  	h_, m_, s_ := t.Split()
   131  
   132  	h_ += h % 24
   133  	h_ += Hour(m / 60)
   134  
   135  	m_ += m % 60
   136  	m_ += Minute(s / 60)
   137  
   138  	s_ += s % 60
   139  
   140  	if h_ %= 24; h_ < 0 {
   141  		h_ += 24
   142  	}
   143  	if m_ %= 60; m_ < 0 {
   144  		m_ += 60
   145  	}
   146  	if s_ %= 60; s_ < 0 {
   147  		s_ += 60
   148  	}
   149  
   150  	return NewTime(h_, m_, s_)
   151  }
   152  
   153  // WithDate returns the current Time with the presented Date's year, month, day
   154  // as a new Timestamp object.
   155  func (t Time) WithDate(y Year, m Month, d Day) Timestamp {
   156  	hh, mm, ss := t.Split()
   157  	return NewTimestamp(y, m, d, hh, mm, ss)
   158  }