Skip to content

Commit 88b905a

Browse files
committed
add fake Timestamps to encode Duration
1 parent 65d9a02 commit 88b905a

File tree

1 file changed

+105
-23
lines changed

1 file changed

+105
-23
lines changed

source/mir/timestamp.d

Lines changed: 105 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ struct Timestamp
162162
/++
163163
Fraction
164164
165-
The `fraction_exponent` and `fraction_coefficient` denote the fractional seconds of the timestamp as a decimal value
165+
The `fractionExponent` and `fractionCoefficient` denote the fractional seconds of the timestamp as a decimal value
166166
The fractional seconds’ value is `coefficient * 10 ^ exponent`.
167167
It must be greater than or equal to zero and less than 1.
168168
A missing coefficient defaults to zero.
@@ -382,6 +382,53 @@ struct Timestamp
382382
assert(st == cast(SysTime) ts);
383383
}
384384

385+
/++
386+
Creates a fake timestamp from a Duration using `total!"hnsecs"` method.
387+
For positive and zero timestamps the format is
388+
`wwww-dd-88Thh:mm:ss.nnnnnnn`
389+
and for negative timestamps
390+
`wwww-dd-99Thh:mm:ss.nnnnnnn`.
391+
+/
392+
this(Duration)(const Duration duration)
393+
if (Duration.stringof == "Duration")
394+
{
395+
auto hnsecs = duration.total!"hnsecs";
396+
ulong abs = hnsecs < 0 ? -hnsecs : hnsecs;
397+
precision = Precision.fraction;
398+
day = hnsecs >= 0 ? 88 : 99;
399+
fractionExponent = -7;
400+
fractionCoefficient = abs % 10_000_000U;
401+
abs /= 10_000_000U;
402+
second = abs % 60;
403+
abs /= 60;
404+
minute = abs % 60;
405+
abs /= 60;
406+
hour = abs % 24;
407+
abs /= 24;
408+
month = abs % 7;
409+
abs /= 7;
410+
year = cast(typeof(year)) abs;
411+
}
412+
413+
///
414+
version (mir_test)
415+
@safe unittest {
416+
import core.time : Duration, weeks, days, hours, minutes, seconds, hnsecs;
417+
418+
auto duration = 5.weeks + 2.days + 7.hours + 40.minutes + 4.seconds + 9876543.hnsecs;
419+
Timestamp ts = duration;
420+
421+
assert(ts.toISOExtString == `0005-02-88T07:40:04.9876543Z`);
422+
assert(duration == cast(Duration) ts);
423+
424+
duration = -duration;
425+
ts = Timestamp(duration);
426+
assert(ts.toISOExtString == `0005-02-99T07:40:04.9876543Z`);
427+
assert(duration == cast(Duration) ts);
428+
429+
assert(Timestamp(Duration.zero).toISOExtString == `0000-00-88T00:00:00.0000000Z`);
430+
}
431+
385432
/++
386433
Decomposes Timestamp to an algebraic type.
387434
Supported types up to T.stringof equivalence:
@@ -414,6 +461,11 @@ struct Timestamp
414461
if (precision == precision.month)
415462
return T(opCast!AT);
416463

464+
foreach (AT; T.AllowedTypes)
465+
static if (AT.stringof == "Duration")
466+
if (isDuration)
467+
return T(opCast!AT);
468+
417469
foreach (AT; T.AllowedTypes)
418470
static if (AT.stringof == "YearMonthDay" || AT.stringof == "Date" || AT.stringof == "date")
419471
if (precision == precision.day)
@@ -494,6 +546,7 @@ struct Timestamp
494546
|| T.stringof == "Date"
495547
|| T.stringof == "date"
496548
|| T.stringof == "TimeOfDay"
549+
|| T.stringof == "Duration"
497550
|| T.stringof == "DateTime"
498551
|| T.stringof == "SysTime")
499552
{
@@ -523,29 +576,47 @@ struct Timestamp
523576
import std.datetime.date: DateTime;
524577
import std.datetime.systime: SysTime;
525578
import std.datetime.timezone: UTC, SimpleTimeZone;
526-
auto ret = SysTime(DateTime(year, month, day, hour, minute, second), UTC());
527-
if (fractionCoefficient)
528-
{
529-
long coeff = fractionCoefficient;
530-
int exp = fractionExponent;
531-
while (exp > -7)
532-
{
533-
exp--;
534-
coeff *= 10;
535-
}
536-
while (exp < -7)
537-
{
538-
exp++;
539-
coeff /= 10;
540-
}
541-
ret.fracSecs = coeff.hnsecs;
542-
}
579+
auto ret = SysTime(DateTime(year, month, day, hour, minute, second), getPhobosFraction.hnsecs, UTC());
543580
if (offset)
544581
{
545582
ret = ret.toOtherTZ(new immutable SimpleTimeZone(offset.minutes));
546583
}
547584
return ret;
548585
}
586+
else
587+
static if (T.stringof == "Duration")
588+
{
589+
if (!isDuration)
590+
throw ExpectedDuration;
591+
auto coeff = ((((long(year) * 7 + month) * 24 + hour) * 60 + minute) * 60 + second) * 10_000_000 + getPhobosFraction;
592+
if (isNegativeDuration)
593+
coeff = -coeff;
594+
595+
import mir.conv: to;
596+
import core.time: hnsecs;
597+
return coeff.hnsecs.to!T;
598+
}
599+
}
600+
601+
private long getPhobosFraction() @property const @safe pure nothrow @nogc
602+
{
603+
long coeff;
604+
if (fractionCoefficient)
605+
{
606+
coeff = fractionCoefficient;
607+
int exp = fractionExponent;
608+
while (exp > -7)
609+
{
610+
exp--;
611+
coeff *= 10;
612+
}
613+
while (exp < -7)
614+
{
615+
exp++;
616+
coeff /= 10;
617+
}
618+
}
619+
return coeff;
549620
}
550621

551622
/++
@@ -617,7 +688,7 @@ struct Timestamp
617688
}
618689

619690
/++
620-
Converts this $(LREF Timestamp) to a string with the format `YYYY-MM-DDThh:mm:ss±hh:mm`.
691+
Converts this $(LREF Timestamp) to a string with the format `yyyy-mm-ddThh:mm:ss[.mmm]±hh:mm`.
621692
622693
If `w` writer is set, the resulting string will be written directly
623694
to it.
@@ -637,7 +708,7 @@ struct Timestamp
637708
assert(Timestamp(0, 1, 5).toString == "0000-01-05");
638709
assert(Timestamp(-4, 1, 5).toString == "-0004-01-05");
639710

640-
// YYYY-MM-DDThh:mm:ss±hh:mm
711+
// yyyy-mm-ddThh:mm:ss[.mmm]±hh:mm
641712
assert(Timestamp(2021).toString == "2021T");
642713
assert(Timestamp(2021, 01).toString == "2021-01T", Timestamp(2021, 01).toString);
643714
assert(Timestamp(2021, 01, 29).toString == "2021-01-29");
@@ -763,7 +834,7 @@ struct Timestamp
763834
// if (isOutputRange!(W, char))
764835
{
765836
import mir.format: printZeroPad;
766-
// YYYY-MM-DDThh:mm:ss±hh:mm
837+
// yyyy-mm-ddThh:mm:ss[.mmm]±hh:mm
767838
Timestamp t = this;
768839

769840
if (t.offset)
@@ -956,10 +1027,9 @@ struct Timestamp
9561027
}
9571028

9581029
/++
959-
Creates a $(LREF Timestamp) from a string with the format `YYYY-MM-DDThh:mm:ss±hh:mm`
1030+
Creates a $(LREF Timestamp) from a string with the format `yyyy-mm-ddThh:mm:ss[.mmm]±hh:mm`
9601031
or its leading part allowed by the standard.
9611032
962-
9631033
Params:
9641034
str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) formats dates.
9651035
value = (optional) result value.
@@ -1296,4 +1366,16 @@ struct Timestamp
12961366
return true;
12971367
}
12981368
}
1369+
1370+
///
1371+
bool isDuration() const @safe pure nothrow @nogc @property
1372+
{
1373+
return day == 88 || day == 99;
1374+
}
1375+
1376+
///
1377+
bool isNegativeDuration() const @safe pure nothrow @nogc @property
1378+
{
1379+
return day == 99;
1380+
}
12991381
}

0 commit comments

Comments
 (0)