~turminal/hare

928f2c7ad3e8daa66e513293567f2e7490d01290 — Byron Torres 6 months ago a251895
time::date: add zflag enum, zfunresolved error

This commit begins the introduction of zone-offset resolution to the
time::date module.
3 files changed, 109 insertions(+), 2 deletions(-)

M encoding/asn1/decoder.ha
M time/date/error.ha
M time/date/virtual.ha
M encoding/asn1/decoder.ha => encoding/asn1/decoder.ha +1 -1
@@ 759,7 759,7 @@ export fn read_gtime(d: *decoder) (date::date | error) = {
	match (date::from_str("%Y%m%d%H%M%S.%N", strings::fromutf8(time)!)) {
	case let d: date::date =>
		return d;
	case let e: date::error =>
	case =>
		return invalid;
	};
};

M time/date/error.ha => time/date/error.ha +7 -1
@@ 5,7 5,7 @@ use fmt;
use strings;

// All possible errors returned from [[date]].
export type error = !(insufficient | invalid | parsefail);
export type error = !(insufficient | invalid | zfunresolved | parsefail);

// Converts an [[error]] into a human-friendly string. The result may be
// statically allocated.


@@ 26,6 26,12 @@ export fn strerror(err: error) const str = {
		));
	case invalid =>
		return "Invalid date information";
	case let lap: zfunresolved =>
		if (lap) {
			return "Failed to resolve zone-offset in a timezone transition overlap";
		} else {
			return "Failed to resolve zone-offset in a timezone transition gap";
		};
	case let pf: parsefail =>
		const (bi, rn) = pf;
		def FMTMSG = "Date parsing failure for layout rune '{}' at byteindex {}";

M time/date/virtual.ha => time/date/virtual.ha +101 -0
@@ 4,6 4,107 @@
use time;
use time::chrono;

// Flags for resolving an absent zone-offset. Handles timezone transitions.
//
// The [[realize]] function, as well as other date creation functions (like
// [[new]], [[truncate]], [[reckon]]...) accept zflags. If zflags are provided,
// these functions normally calculate an intermediate date with a best-guess
// numerical zone-offset. This intermediate date can be [[invalid]] if it falls
// within the observed overlap or gap of a timezone transition, where such dates
// are ambiguous or nonexistent. In this case, the provided zflags are
// consulted, and a final calculation takes place before the final resultant
// date (or [[zfunresolved]]) is returned.
//
// Timezone transitions create gaps and overlaps, the two causes of [[invalid]]
// intermediate dates. Passing one "GAP_" and one "LAP_" flag covers both cases.
//
// 	let zf = date::zflag::LAP_EARLY | date::zflag::GAP_END;
// 	date::new(loc, zf, fields...)!; // will never return [[zfunresolved]]
//
// Note that usage of "GAP_" flags will cause the resultant date to be different
// to what is originally specified if the intermediate date falls within a gap.
// Flags with greater value take precedent.
//
// The following figures exist to help understand the effect of these flags.
//
// 	Fig A                    2000 October 29th
// 	                              -1 hour
//
// 	                                 f=02:30+0200
// 	                                 g=02:30+0100
// 	                             lp  | lq
// 	                 +0200        |  |  |       +0100
// 	  Observed time: 00    01    02  | 03    04    05
// 	      Amsterdam:  |-----|-----|==*==|-----|-----|
// 	                  .     .     .\ :: |.     .
// 	                  .     .     . \: :| .     .
// 	                  .     .     .  :  :  .     .
// 	                  .     .     .  :\ |:  .     .
// 	                  .     .     .  : \| :  .     .
// 	            UTC:  |-----|-----|--*--|--*--|-----|
// 	Contiguous time: 22    23    00  | 01  | 02    03
// 	                                 |  |  |
// 	                                 a tx  b
//
// Fig A -- A backjump timezone transition in the Europe/Amsterdam locality.
// The transition is marked by "tx". There is an overlap in the chronology,
// marked by "lp" and "lq". The specified local time 02:30 falls within the
// observed overlap, and so has two valid zone-offsets and can be observed
// twice, as dates "f" and "g". When localized to UTC, these two observations
// correspond to UTC dates "a" and "b" respectively.
//
// 	Fig B                     2000 March 26th
// 	                              +1 hour
//
// 	                                 f~02:30+!!!!
// 	                             gp  | gq
// 	                 +0100        |  |  |       +0200
// 	  Observed time: 00    01    02  | 03    04    05
// 	      Amsterdam:  |-----|-----|  *  |-----|-----|
// 	                  .     .     |    /     .     .
// 	                  .     .     |   /     .     .
// 	                  .     .     |  /     .     .
// 	                  .     .     | /     .     .
// 	                  .     .     |/     .     .
// 	            UTC:  |-----|-----|-----|-----|-----|
// 	Contiguous time: 23    00    01    02    03    04
// 	                              |
// 	                             tx
//
// Fig B -- A forejump timezone transition in the Europe/Amsterdam locality.
// The transition is marked by "tx". There is a gap in the chronology, marked by
// "gp" and "gq". The specified local time 02:30 falls within the observed gap,
// and so cannot be observed and is [[invalid]].
export type zflag = enum u8 {
	// Assume a contiguous chronology with no observed gaps or overlaps.
	// Upon encountering an observed gap or overlap, fail with [[invalid]].
	// In other words, accept one and only one zone-offset.
	CONTIG    = 0b00000000,

	// Upon encountering an observed overlap, select the earliest possible
	// date (Fig A "f") using the most positive (eastmost) zone-offset.
	LAP_EARLY = 0b00000001,
	// Upon encountering an observed overlap, select the latest possible
	// date (Fig A "g") using the most negative (westmost) zone-offset.
	LAP_LATE  = 0b00000010,

	// Upon encountering an observed gap, disregard the specified date and
	// select the date at the start boundary of the observed gap (Fig B
	// "gp"), corresponding to the contiguous time just before the
	// transition (Fig B "tx").
	GAP_START = 0b00000100,
	// Upon encountering an observed gap, disregard the specified date and
	// select the date at the end boundary of the observed gap (Fig B "gq"),
	// corresponding to the contiguous time at the transition (Fig B "tx").
	GAP_END   = 0b00001000,
};

// Failed to resolve an absent zone-offset. The provided [[zflag]]s failed to
// account for some timezone effect and could not produce a valid zone-offset.
// A false value signifies the occurence of a timezone transition gap.
// A true value signifies the occurence of a timezone transition overlap.
export type zfunresolved = !bool;

// A [[virtual]] date does not have enough information from which to create a
// valid [[date]].
export type insufficient = !lack; // TODO: drop alias workaround