I know that Date.getDate makes it easy to get the current date or create a date in the future - but I’m wondering if there is a simple tool for adding time to the current date and having it calculate accordingly.
As an example:
If I ran Date.getDate.asSortableString on New Year’s Eve, I’d get a number: 20221231245900. Then, maybe I would want to add 60 seconds, so that the year, day, and hour all turned over.
Simply adding 60 to that number isn’t going to give me an accurate date, so I could parse the string, write a function with information about the year and the months, and so on - but I’m wondering if there is a convenience class that would do something similar.
I’m pretty sure this doesn’t quite work either… It seems to return numbers that wouldn’t fit into a normal time-keeping convention… for instance, if you’re at 7 minutes and 20 seconds… and you add 60, you would get a string with “780”. If you exceed 100 seconds in a minute, it simply adds the number in your minutes column.
I can definitely think of a hack-y way to program this, so there’s no urgency - but I was hoping something more elegant existed.
Here’s a rough attempt at a subset of the problem - advancing a .secStamp only.
For incrementing the entire date, I guess I’d have to add logic to test for a leap year, alongside referencing a ‘months’ array where each index held the maximum days for each month.
(
var incrementSecStamp = {|hhmmss seconds|
var wrapCount = {|n max| if (n > max) {n - (max + 1)} {n}};
var addLeadZero = {|n| if (n < 10) {"0" ++ n.asString} {n.asString}};
var hh, mm, ss, hrs, mins, secs, secPerHr = 3600;
// seconds values with a total greater than 24hrs are not needed for this
seconds = seconds % (secPerHr * 24);
// calculate the amounts to be added to each unit of time
hrs = (seconds / secPerHr).floor;
mins = ((seconds - (hrs * secPerHr)) / 60).floor;
secs = seconds - ((mins * 60) + (hrs * secPerHr));
// extract values from the input secStamp and sum with the calculated amounts
ss = hhmmss[4..5].asInteger + secs;
mm = (hhmmss[2..3].asInteger + mins) + (ss / 60).floor;
hh = (hhmmss[0..1].asInteger + hrs) + (mm / 60).floor;
// wrap each value to be within the correct range for 24hr time
// and convert to a formatted string
[wrapCount.(hh, 23), wrapCount.(mm, 59), (ss % 60)]
.collect({|n| addLeadZero.(n.asInteger)}).join;
};
a = Date.getDate.secStamp;
incrementSecStamp.(a, 3660);
)
d = Date.gmtime;
-> Tue Sep 27 00:23:32 2022
d.rawSeconds;
-> 1664238212.6025
// 'fromRawSeconds' is from wslib
e = Date.fromRawSeconds(d.rawSeconds + 10, daylightSavings: 0)
-> Tue Sep 28 00:23:42 2022
gmtime doesn’t take time zone into account. AFAICS, to correct for that, you would need to get an hour offset Date.getDate.hour - Date.gmtime.hour (you could do this once at the beginning of the session, because it’s extremely unlikely to switch time zones in the middle of a session), and then add this offset to the hour (which might require rolling over a day/month/year too).
If you read the code, you’ll see that fromRawSeconds makes a SMPTE object from the seconds value, then does Date.fromSMPTE on that. Follow the chain to fromSMPTE it looks like some of the logic could be fragile.
I’m not the author of wslib, though, so maybe the best way is to log a bug report on the github repository (linked above).
There are likely any number of algorithms online to turn a raw timestamp (seconds) into a date. If Excel can do it, so could we… you or someone else could find one of these and implement it.