New Issue: 'DateTime' module stabilization - time zones

18941, "aconsroe-hpe", "'DateTime' module stabilization - time zones", "2022-01-07T20:29:28Z"

As part of the module review for DateTime, we identified some issues with timezones.

Modeled after the Python datetime time zone information, the current class TZInfo is an abstract base class and must be subclassed with a concrete implementation per time zone. This prevents users from easily updating the time zones that would hypothetically be distributed with Chapel in the future. (Note that currently there are no concrete subclasses in the standard modules.) Timezones do change and is common to get these from the system like tzdata, which would fit better with a single class where each timezone was an instance.

In addition, we currently have multiple halts in operations on two datetimes like operator < because comparing one with a timezone and one without is non-sensical, but is not caught at compile time. See #9941

Our conclusion from earlier discussions was: let's not worry about implementing everything about time zones today, but let's make sure we leave the door open to implement them well in the future.

Inspired by a conversation with @bradcray, the below sketches a nice way to have record time be generic over whether it has a timezone without resorting to something like naiveTime or the like present in other libraries to differentiate.

/* hypothetical future implementation of timezone */
record timezone {
  var id:string;
  var dst:int;
  var offset:int;
}

record time {
  type tzType = nothing;
  var hours:int;
  var minutes:int;
  var tz:tzType;

  proc init(hours:int, minutes:int) {
    this.hours = hours;
    this.minutes = minutes;
  }

  /* I would expect us not to implement this today, but is how we would add them in the future */
  proc init(hours:int, minutes:int, tz:timezone) {
    this.tzType = timezone;
    this.hours = hours;
    this.minutes = minutes;
    this.tz = tz;
  }
}

operator time.<(t1:time(?tz), t2:time(tz)): bool {
  if t1.hours < t2.hours then return true;
  if t1.hours == t2.hours then return t1.minutes < t2.minutes;
  return false;
}

var utc = new timezone("utc", 0, 0);

var t1 = new time(10, 30);
var t2 = new time(11, 30);
var t3 = new time(10, 30, utc);
var t4 = new time(11, 30, utc);

writeln(t1 < t2);
writeln(t3 < t4);
writeln(t3.tz.id);
/* This is a compile error! */
/* writeln(t1 < t4); */

This looks nice to me, but raises issues in how to deprecate the old DateTime.time record because it has:

proc time.init(hour=0, minute=0, second=0, microsecond=0, in tzinfo: shared TZInfo? = nil) { /*...*/ }

where you might be able to deprecate this init and forward it appropriately, but without going through the whole excercise, its not clear this is viable.