aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.odin17
-rw-r--r--src/time.odin266
2 files changed, 237 insertions, 46 deletions
diff --git a/src/main.odin b/src/main.odin
index d4240ce..2030c2f 100644
--- a/src/main.odin
+++ b/src/main.odin
@@ -4,6 +4,10 @@ import "core:fmt"
main :: proc() {
test()
+ new_workday({0, 0, 1, 1, 1},
+ {00, 08, 5, 5, 2023},
+ {00, 23, 5, 5, 2023},
+ {30, 21, 5, 5, 2023})
}
test :: proc() {
@@ -14,11 +18,11 @@ test :: proc() {
fmt.println(toString(test_delta))
test_delta = {0, 0, 0}
fmt.println(toString(test_delta))
- test_delta = {45, 12, 1}
+ test_delta = {0, 12, 2}
fmt.printf("I've been waiting for %s! That's a long time!\n", toString(test_delta))
start: Moment = {45, 8, 20, 4, 2023}
- end: Moment = {45, 8, 20, 4, 2023}
+ end: Moment = add(start, test_delta)
block: Timeblock = {start, end, 0, ""}
fmt.println(toString(block))
@@ -36,6 +40,7 @@ test :: proc() {
fmt.println(toString(test_moment))
+
fmt.println("\n--- TESTING OPERATIONS ---")
fmt.printf("So far test_moment, holds: %s.\n", toString(test_moment))
@@ -46,13 +51,19 @@ test :: proc() {
- fmt.println("\n--- TESTING DELTA ---")
+ fmt.println("\n--- TESTING DELTA & SORTABLE ---")
+
+ fmt.printf("The sortable() version of %s is %i.\n", toString(test_moment), sortable(test_moment))
+ fmt.printf("The sortable() version of %s is %i.\n", toString(test_delta), sortable(test_delta))
+
fmt.printf("The diff() between %s and %s is %s.\n",
toString(test_moment),
toString(add(test_moment, test_delta)),
toString(diff(test_moment, add(test_moment, test_delta))))
+
//for i: int=1; i < 14; i += 1 {
// fmt.println(days_in(i, 2023))
//}
+ fmt.println("")
} \ No newline at end of file
diff --git a/src/time.odin b/src/time.odin
index d6f6046..6acfc20 100644
--- a/src/time.odin
+++ b/src/time.odin
@@ -5,6 +5,7 @@ import "core:fmt"
import "core:strings"
import "core:strconv"
import "core:slice"
+import "core:sort"
//
// --- STRUCTURES ---
@@ -50,10 +51,108 @@ Workday :: struct {
total_timeblocks : int,
}
+
//
-// --- BASIC OPERATIONS ---
+// --- MAJOR PROCEDURES ---
//
+new_workday :: proc(previous_wrap : Moment,
+ calltime : Moment,
+ wraptime : Moment,
+ planned_wraptime : Moment) -> (workday: Workday) {
+
+ workday.call = calltime
+ workday.wrap = wraptime
+ workday.planned_wrap = planned_wraptime
+
+ using workday
+
+ initial_block: Timeblock = {call,
+ // Paragraph 6.7 says that up to 2 hours of unused warned overtime counts as worktime,
+ // though so that at least one hour of the unused overtime is not counted.
+ // (It's unclear if an 8-hour day that ends 3 hours in counts as having 5 hours of unused overtime)
+ max(clamp(sub(planned_wrap, {0, 1, 0}), wrap, add(planned_wrap, {0, 2, 0})),
+ add(call, {0, 4, 0})), 0, ""}
+ // ^ Minimum 4 hour day ^
+
+ sp_length :: 11
+ splitpoints:= [sp_length]Moment{ // --$-- Points where the price may change --$-- //
+
+ // TODO: Replace this terribleness with a system for parsing simple, user-editable rulseset-files
+
+ add(previous_wrap, {0, 10, 0}), // Sleepbreach, 10 hours after previous wrap, aka. turnover
+ {0, 5, call.day, call.month, call.year}, // 2 hours before 7, aka 5
+ {0, 6, call.day, call.month, call.year}, // 6 in the morning
+ add(call, {0, 8, 0}), // Normal 8 hours of work
+ add(call, {0, 9, 0}), // 1st hour of overtime is over
+ add(call, {0, 11, 0}), // 3rd hour of overtime is over
+ planned_wraptime, // End of warned overtime
+ add(call, {0, 14, 0}), // The 14-hour mark
+ {0, 22, call.day, call.month, call.year}, // 22:00 in the evening
+ add({0, 23, call.day, call.month, call.year}, {0, 1, 0}), // Midnight
+ add({0, 22, call.day, call.month, call.year}, {0, 7, 0}), // 06:00 the next morning
+ }
+
+ // Eliminate planned wrap, if it occurs within normal 8-hour period.
+ // This is to make sure the first period of time becomes a pure 8 hours,
+ // which makes detecting the main section of the workday easier.
+ if sortable(splitpoints[6]) < sortable(splitpoints[3]) {
+ splitpoints[6] = splitpoints[3];
+ }
+
+ splitpoints_sorted: [sp_length]Moment = splitpoints
+ slice.sort_by(splitpoints_sorted[:], lessMoment)
+
+ working_block: Timeblock = initial_block
+
+ j: int = 0
+ for each_moment in splitpoints_sorted {
+ // If each splitpoint moment is within the workday, and is not equal to the start of the current block
+ if sortable(each_moment) > sortable(call) && sortable(each_moment) < sortable(wrap) && each_moment != initial_block.start {
+ blocks[j], working_block = timesplit(working_block, each_moment)
+ j += 1
+ fmt.println("Split and wrote:", j)
+ }
+ }
+ blocks[j] = working_block
+ j += 1
+ total_timeblocks = j
+
+ slice.sort_by(blocks[:], lessTimeblock)
+
+ for each_block, i in blocks {
+ fmt.printf("Block %2i: %s\n", i+1, toString(each_block))
+ }
+
+ // TODO:
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+ // BIG BLOCK OF RULES N SHIT GOES HERE
+
+ return
+}
+
windIndividual :: proc(input_moment: ^Moment,
minutes: int,
hours: int,
@@ -114,6 +213,29 @@ windDelta :: proc(moment: ^Moment, delta: Delta) {
}
wind :: proc{windIndividual, windDelta}
+timesplit :: proc(block: Timeblock, splitpoint: Moment) -> (first_half: Timeblock, second_half: Timeblock) {
+ // Splits a timeblock at splitpoint.
+
+ if sortable(splitpoint) < sortable(block.start) ||
+ sortable(splitpoint) > sortable(block.end) ||
+ splitpoint == block.start || splitpoint == block.end {
+ fmt.println("WHOOPS: Splitpoint is outside timeblock range!")
+ fmt.println("Timeblock:", toString(block))
+ fmt.println("Splitpoint:", toString(splitpoint))
+ second_half = block
+ return
+ }
+
+ first_half = {block.start, splitpoint, block.valuefactor, block.price_reason}
+ second_half = {splitpoint, block.end, block.valuefactor, block.price_reason}
+
+ return
+}
+
+//
+// --- BASIC OPERATIONS ---
+//
+
add :: proc(moment: Moment, delta: Delta) -> (output: Moment) {
output = moment
@@ -126,43 +248,126 @@ sub :: proc(moment: Moment, delta: Delta) -> (output: Moment) {
wind(&output, minutes*-1, hours*-1, days*-1)
return
}
+maxMoment :: proc(moment_a: Moment, moment_b: Moment) -> Moment {
+ if sortable(moment_a) > sortable(moment_b) do return moment_a
+ return moment_b
+}
+maxDelta :: proc(delta_a: Delta, delta_b: Delta) -> Delta {
+ if sortable(delta_a) > sortable(delta_b) do return delta_a
+ return delta_b
+}
+max :: proc{maxDelta, maxMoment}
-gtMoment :: proc(moment_a: Moment, moment_b: Moment) -> bool {
- // TODO: This is wrong
- return true
+minMoment :: proc(moment_a: Moment, moment_b: Moment) -> Moment {
+ if sortable(moment_a) < sortable(moment_b) do return moment_a
+ return moment_b
}
-gtDelta :: proc(delta_a: Delta, delta_b: Delta) -> bool {
- // TODO: This is wrong
- return true
+minDelta :: proc(delta_a: Delta, delta_b: Delta) -> Delta {
+ if sortable(delta_a) < sortable(delta_b) do return delta_a
+ return delta_b
}
-gt :: proc{gtMoment, gtDelta}
+min :: proc{minDelta, minMoment}
+
+clampMoment :: proc(moment: Moment, moment_min: Moment, moment_max: Moment) -> Moment {
+ return min(max(moment, moment_min), moment_max)
+}
+clampDelta :: proc(delta: Delta, delta_min: Delta, delta_max: Delta) -> Delta {
+ return min(max(delta, delta_min), delta_max)
+}
+clamp :: proc{clampMoment, clampDelta}
+
+greatMoment :: proc(moment_a: Moment, moment_b: Moment) -> bool {
+ return bool(sortable(moment_a) > sortable(moment_b))
+}
+greatDelta :: proc(delta_a: Delta, delta_b: Delta) -> bool {
+ return bool(sortable(delta_a) > sortable(delta_b))
+}
+great :: proc{greatMoment, greatDelta}
+
+lessMoment :: proc(moment_a: Moment, moment_b: Moment) -> bool {
+ return bool(sortable(moment_a) < sortable(moment_b))
+}
+lessDelta :: proc(delta_a: Delta, delta_b: Delta) -> bool {
+ return bool(sortable(delta_a) < sortable(delta_b))
+}
+lessTimeblock :: proc(block_a: Timeblock, block_b: Timeblock) -> bool {
+ if block_b.start == {0, 0, 0, 0, 0} do return true
+ if block_a.start == {0, 0, 0, 0, 0} do return false
+ return bool(sortable(block_a.start) < sortable(block_b.start))
+}
+less :: proc{lessMoment, lessDelta, lessTimeblock}
+
diff :: proc(moment_a: Moment, moment_b: Moment) -> (acc: Delta) {
-
- acc = {0, 0, 0}
- if moment_a == moment_b do return
+ // TODO: Finish writing this
// Uses what I call an accumulator-decumulator design
// Count how long it takes to approach a benchmark,
// and that count is the difference
- reverse: bool = gt(moment_b, moment_a)
+ acc = {0, 0, 0}
+ if moment_a == moment_b do return
+ // smallest operand becomes benchmark to approach
+ reverse: bool = sortable(moment_a) < sortable(moment_b)
+ bench : Moment
+ dec : Moment
+ if reverse {
+ bench = moment_a
+ dec = moment_b
+ } else {
+ bench = moment_b
+ dec = moment_a
+ }
- // TODO: Finish writing this
+ // It is possible to write something that does this in months at a time, instead of days,
+ // which would be faster, but I am not expecting to have to do this with such
+ // long periods of time, so screw that.
+ for ((dec.year - bench.year) > 1 ||
+ (dec.month - bench.month) > 1 ||
+ (dec.day - bench.day) > 1) {
+ wind(&dec, 0, 0, -1)
+ acc.days += 1
+ }
+ for (dec.hours - bench.hours > 1) {
+ wind(&dec, 0, -1, 0)
+ acc.hours += 1
+ }
+ for acc.hours > 23 {
+ acc.hours -= 24
+ acc.days += 1
+ }
+
+ for dec != bench {
+ wind(&dec, -1, 0, 0)
+ acc.minutes += 1
+ }
+ for acc.minutes > 59 {
+ acc.minutes -= 60
+ acc.hours += 1
+ }
+
+ // Repeating this is a little bit ugly, but it works
+ for acc.hours > 23 {
+ acc.hours -= 24
+ acc.days += 1
+ }
return
}
-sortableTimeDelta :: proc(delta: Delta) -> int {
+sortableTimeDelta :: proc(delta: Delta) -> (output: u64) {
using delta
- return strconv.atoi(fmt.tprintf("%2i%2i%2i", days, hours, minutes))
+ output, _ = strconv.parse_u64(fmt.tprintf("1%3i%2i%2i", days, hours, minutes))
+ return
}
-sortableTimeMoment :: proc(moment: Moment) -> int {
+sortableTimeMoment :: proc(moment: Moment) -> (output: u64) {
using moment
- return strconv.atoi(fmt.tprintf("%4i%2i%2i%2i%2i", year, month, day, hours, minutes))
+ output, _ = strconv.parse_u64(fmt.tprintf("%4i%2i%2i%2i%2i", year, month, day, hours, minutes))
+ return
}
+sortable :: proc{sortableTimeMoment, sortableTimeDelta}
deltaToString :: proc(delta: Delta) -> (output: string) {
using delta
@@ -225,7 +430,7 @@ momentToString :: proc(moment: Moment) -> (output: string) {
cat_array: [dynamic]string
- output = fmt.tprintf("%i-%2i-%2i %2i:%2i", year, month, day, hours, minutes)
+ output = fmt.tprintf("%4i-%2i-%2i %2i:%2i", year, month, day, hours, minutes)
return
}
@@ -249,11 +454,6 @@ clockprintTimeblock :: proc(block: Timeblock) -> string {
}
timeprint :: proc{clockprintTimeblock, clockprintMoment}
-
-//
-// --- PROCEDURES ---
-//
-
days_in :: proc(month: int, year: int) -> int {
switch month {
case 1:
@@ -288,23 +488,3 @@ days_in :: proc(month: int, year: int) -> int {
assert(month < 13 && month > 0)
return 30
}
-
-new_workday :: proc(previous_wrap : Moment,
- calltime : Moment,
- wraptime : Moment,
- planned_wraptime : Moment) -> (workday: Workday) {
-
- workday.call = calltime
- workday.wrap = wraptime
- workday.planned_wrap = planned_wraptime
-
- /*
- initial_block: Timeblock = {
- math.max(
- math.clamp(),
- )
- }
- */
-
- return
-}