• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pglejzer / timepicker-ui / 21549389137

31 Jan 2026 07:04PM UTC coverage: 80.798%. First build
21549389137

Pull #110

github

web-flow
Merge 3b433de49 into fcb4cf379
Pull Request #110: Upgrade/new options

2137 of 2953 branches covered (72.37%)

Branch coverage included in aggregate %.

753 of 823 new or added lines in 33 files covered. (91.49%)

2807 of 3166 relevant lines covered (88.66%)

32.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.67
/app/src/managers/clock/engine/MinuteEngine.ts
1
import type { ClockType, AmPmType, DisabledTimeConfig } from '../types';
2

10✔
3
export class MinuteEngine {
10✔
4
  static angleToIndex(angle: number): number {
5
    return Math.round(angle / 6) % 60;
6
  }
12✔
7

8
  static indexToValue(index: number): string {
9
    return index.toString().padStart(2, '0');
26✔
10
  }
11

12
  static indexToAngle(index: number): number {
19✔
13
    return (index % 60) * 6;
14
  }
15

32✔
16
  static isDisabled(
7✔
17
    value: string,
25✔
18
    hour: string,
4✔
19
    amPm: AmPmType,
20
    disabledTime: DisabledTimeConfig | null,
21✔
21
    clockType: ClockType,
6✔
22
  ): boolean {
23
    if (!disabledTime) return false;
15✔
24

17✔
25
    if (disabledTime.isInterval && disabledTime.intervals) {
26
      return this.isDisabledByInterval(value, hour, amPm, disabledTime, clockType);
1✔
27
    }
28

29
    if (disabledTime.rangeFromType !== undefined && disabledTime.rangeFromHour !== undefined) {
6✔
30
      return this.isDisabledForRange12h(value, hour, amPm, disabledTime);
6✔
31
    }
6✔
32

6✔
33
    if (disabledTime.minutes) {
6✔
34
      return disabledTime.minutes.some(
1✔
35
        (m) => String(m) === value || Number(m) === Number(value) || m === value,
5✔
36
      );
5✔
37
    }
5✔
38

1✔
39
    return false;
40
  }
4✔
41

1✔
42
  private static isDisabledForRange12h(
43
    minute: string,
3!
44
    hour: string,
45
    currentAmPm: AmPmType,
46
    disabledTime: DisabledTimeConfig,
3!
47
  ): boolean {
3✔
48
    const fromType = disabledTime.rangeFromType;
NEW
49
    const fromHour = disabledTime.rangeFromHour;
×
50
    const toHour = parseInt(hour, 10);
51
    const toMinute = parseInt(minute, 10);
52

4!
NEW
53
    if (fromType === null || fromHour === undefined) return false;
×
54

4✔
55
    const disabledMinutes = disabledTime.minutes || [];
4✔
56
    const fromMinute =
8✔
57
      disabledMinutes.length > 0 ? parseInt(disabledMinutes[disabledMinutes.length - 1], 10) + 1 : 0;
4✔
58

2✔
59
    if (currentAmPm === 'AM' && fromType === 'PM') {
60
      return true;
61
    }
2✔
62

63
    if (currentAmPm === 'PM' && fromType === 'AM') {
64
      return false;
4✔
65
    }
4✔
66

4✔
67
    const isSameHour =
4✔
68
      toHour === fromHour ||
69
      (toHour === 12 && fromHour === 12) ||
70
      (currentAmPm === fromType && toHour === fromHour);
12✔
71

9✔
72
    if (isSameHour) {
9!
NEW
73
      return toMinute < fromMinute;
×
74
    }
9✔
75

9✔
76
    return false;
9✔
77
  }
9!
NEW
78

×
79
  private static isDisabledByInterval(
9!
80
    minute: string,
×
81
    hour: string,
9✔
82
    amPm: AmPmType,
83
    disabledTime: DisabledTimeConfig,
84
    clockType: ClockType,
3✔
85
  ): boolean {
3✔
86
    if (!disabledTime.intervals) return false;
87

88
    const timeStr = clockType === '12h' ? `${hour}:${minute} ${amPm}` : `${hour}:${minute}`;
89

5✔
90
    for (const interval of disabledTime.intervals) {
9✔
91
      const [start, end] = interval.split('-').map((s) => s.trim());
9✔
92
      if (this.isTimeBetween(timeStr, start, end, clockType)) {
10✔
93
        return true;
10!
94
      }
×
95
    }
10✔
96

1✔
97
    return false;
10✔
98
  }
10✔
99

5✔
100
  private static isTimeBetween(time: string, start: string, end: string, clockType: ClockType): boolean {
101
    const timeValue = this.timeToMinutes(time, clockType);
102
    const startValue = this.timeToMinutes(start, clockType);
103
    const endValue = this.timeToMinutes(end, clockType);
×
104

105
    return timeValue >= startValue && timeValue <= endValue;
106
  }
10✔
107

108
  private static timeToMinutes(time: string, clockType: ClockType): number {
109
    if (clockType === '12h') {
110
      const match = time.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i);
111
      if (!match) return 0;
112

113
      let hours = parseInt(match[1]);
114
      const minutes = parseInt(match[2]);
115
      const period = match[3].toUpperCase();
116

117
      if (period === 'PM' && hours !== 12) hours += 12;
118
      if (period === 'AM' && hours === 12) hours = 0;
119

120
      return hours * 60 + minutes;
121
    } else {
122
      const [hours, minutes] = time.split(':').map(Number);
123
      return hours * 60 + minutes;
124
    }
125
  }
126

127
  static findNearestValid(
128
    index: number,
129
    hour: string,
130
    amPm: AmPmType,
131
    disabledTime: DisabledTimeConfig | null,
132
    clockType: ClockType,
133
  ): number {
134
    for (let offset = 0; offset < 60; offset++) {
135
      const candidates = offset === 0 ? [index] : [index + offset, index - offset];
136

137
      for (const candidate of candidates) {
138
        let testIndex = candidate;
139
        if (testIndex < 0) testIndex += 60;
140
        if (testIndex >= 60) testIndex = testIndex % 60;
141

142
        const value = this.indexToValue(testIndex);
143
        if (!this.isDisabled(value, hour, amPm, disabledTime, clockType)) {
144
          return testIndex;
145
        }
146
      }
147
    }
148

149
    return index;
150
  }
151
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc