@@ -37,8 +37,9 @@ version(D_Exceptions)
3737/+ +
3838Timestamp
3939
40- Note: The component values in the binary encoding are always in UTC, while components in the text encoding are in the local time!
41- This means that transcoding requires a conversion between UTC and local time.
40+ Note: The component values in the binary encoding are always in UTC or local time with unknown offset,
41+ while components in the text encoding are in a some timezone with known offset.
42+ This means that transcoding requires a conversion between UTC and a timezone.
4243
4344`Timestamp` precision is up to picosecond (second/10^12).
4445+/
@@ -81,45 +82,52 @@ struct Timestamp
8182 assert (Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 + 30 ) == Timestamp.fromISOString(" 20210129T201244+0730" ));
8283 static assert (Timestamp(2021 , 01 , 29 , 4 , 42 , 44 ).withOffset(- (7 * 60 + 30 )) == Timestamp.fromISOExtString(" 2021-01-28T21:12:44-07:30" ));
8384
84- assert (Timestamp(" T0740Z " ) == Timestamp.onlyTime(7 , 40 ));
85- assert (Timestamp(" T074030Z" ) == Timestamp.onlyTime(7 , 40 , 30 ));
86- assert (Timestamp(" T074030.056Z " ) == Timestamp.onlyTime(7 , 40 , 30 , - 3 , 56 ));
85+ assert (Timestamp(" T0740 " ) == Timestamp.onlyTime(7 , 40 ));
86+ assert (Timestamp(" T074030Z" ) == Timestamp.onlyTime(7 , 40 , 30 ).withOffset( 0 ) );
87+ assert (Timestamp(" T074030.056 " ) == Timestamp.onlyTime(7 , 40 , 30 , - 3 , 56 ));
8788
88- assert (Timestamp(" 07:40Z " ) == Timestamp.onlyTime(7 , 40 ));
89- assert (Timestamp(" 07:40:30Z " ) == Timestamp.onlyTime(7 , 40 , 30 ));
90- assert (Timestamp(" T07:40:30.056Z" ) == Timestamp.onlyTime(7 , 40 , 30 , - 3 , 56 ));
89+ assert (Timestamp(" 07:40 " ) == Timestamp.onlyTime(7 , 40 ));
90+ assert (Timestamp(" 07:40:30 " ) == Timestamp.onlyTime(7 , 40 , 30 ));
91+ assert (Timestamp(" T07:40:30.056Z" ) == Timestamp.onlyTime(7 , 40 , 30 , - 3 , 56 ).withOffset( 0 ) );
9192 }
9293
93- version (all )
94- {
95- short offset;
96- }
97- else
98- /+
94+ private short _offset = short .min;
95+
96+ /+ +
9997 If the time in UTC is known, but the offset to local time is unknown, this can be represented with an offset of “-00:00”.
10098 This differs semantically from an offset of “Z” or “+00:00”, which imply that UTC is the preferred reference point for the specified time.
10199 RFC2822 describes a similar convention for email.
102- private short _offset;
103100 +/
101+ /+ +
102+ Timezone offset in minutes
103+ +/
104+ short offset ()() const @safe pure nothrow @nogc @property
104105 {
106+ return isLocalTime ? 0 : _offset;
107+ }
105108
106- /+ +
107- Timezone offset in minutes
108- +/
109- short offset () const @safe pure nothrow @nogc @property
110- {
111- return _offset >> 1 ;
112- }
109+ // /
110+ void offset ()(ushort offset) @safe pure nothrow @nogc @property
111+ {
112+ _offset = offset;
113+ }
113114
114- // /++
115- // Returns: true if timezone has offset
116- // +/
117- // bool hasOffset() const @safe pure nothrow @nogc @property
118- // {
119- // return _offset & 1;
120- // }
115+ /+ +
116+ Returns: true if it is a local time
117+ +/
118+ bool isLocalTime ()() const @safe pure nothrow @nogc @property
119+ {
120+ return _offset == _offset.min;
121+ }
122+
123+ /+ +
124+ +/
125+ void setLocalTimezone ()() @safe pure nothrow @nogc @property
126+ {
127+ _offset = _offset.min;
121128 }
122129
130+
123131 /+ +
124132 Year
125133 +/
@@ -337,7 +345,7 @@ struct Timestamp
337345 import std.datetime.date : TimeOfDay ;
338346 auto dt = TimeOfDay (7 , 14 , 30 );
339347 Timestamp ts = dt;
340- assert (dt.toISOExtString ~ " Z " == ts.toString);
348+ assert (dt.toISOExtString == ts.toString);
341349 assert (dt == cast (TimeOfDay ) ts);
342350 }
343351
@@ -354,20 +362,23 @@ struct Timestamp
354362 import std.datetime.date : DateTime ;
355363 auto dt = DateTime (1982 , 4 , 1 , 20 , 59 , 22 );
356364 Timestamp ts = dt;
357- assert (dt.toISOExtString ~ " Z " == ts.toString);
365+ assert (dt.toISOExtString == ts.toString);
358366 assert (dt == cast (DateTime ) ts);
359367 }
360368
361369 // /
362370 this (SysTime)(const SysTime systime)
363371 if (SysTime.stringof == " SysTime" )
364372 {
365- auto utcTime = systime.toUTC;
366- this = fromUnixTime(utcTime.toUnixTime);
373+ import std.datetime.timezone : LocalTime;
374+ auto isLocal = systime.timezone is LocalTime();
375+ auto thisTimes = isLocal ? systime : systime.toUTC;
376+ this = fromUnixTime(thisTimes.toUnixTime);
367377 this .fractionExponent = - 7 ;
368- this .fractionCoefficient = utcTime .fracSecs.total! " hnsecs" ;
378+ this .fractionCoefficient = thisTimes .fracSecs.total! " hnsecs" ;
369379 this .precision = Precision.fraction;
370- this .offset = cast (short ) systime.utcOffset.total! " minutes" ;
380+ if (! isLocal)
381+ this .offset = cast (short ) systime.utcOffset.total! " minutes" ;
371382 }
372383
373384 // /
@@ -423,15 +434,15 @@ struct Timestamp
423434 auto duration = 5. weeks + 2. days + 7. hours + 40. minutes + 4. seconds + 9876543. hnsecs;
424435 Timestamp ts = duration;
425436
426- assert (ts.toISOExtString == ` 0005-02-88T07:40:04.9876543Z ` );
437+ assert (ts.toISOExtString == ` 0005-02-88T07:40:04.9876543 ` );
427438 assert (duration == cast (Duration) ts);
428439
429440 duration = - duration;
430441 ts = Timestamp(duration);
431- assert (ts.toISOExtString == ` 0005-02-99T07:40:04.9876543Z ` );
442+ assert (ts.toISOExtString == ` 0005-02-99T07:40:04.9876543 ` );
432443 assert (duration == cast (Duration) ts);
433444
434- assert (Timestamp(Duration.zero).toISOExtString == ` 0000-00-88T00:00:00.0000000Z ` );
445+ assert (Timestamp(Duration.zero).toISOExtString == ` 0000-00-88T00:00:00.0000000 ` );
435446 }
436447
437448 /+ +
@@ -580,13 +591,9 @@ struct Timestamp
580591 import core.time : hnsecs, minutes;
581592 import std.datetime.date : DateTime ;
582593 import std.datetime.systime : SysTime;
583- import std.datetime.timezone : UTC , SimpleTimeZone;
584- auto ret = SysTime.fromUnixTime(toUnixTime, UTC ()) + getFraction! 7. hnsecs;
585- if (offset)
586- {
587- ret = ret.toOtherTZ(new immutable SimpleTimeZone(offset.minutes));
588- }
589- return ret;
594+ import std.datetime.timezone : UTC , LocalTime, SimpleTimeZone;
595+ auto timezone = isLocalTime ? LocalTime() : ! offset ? UTC () : new immutable SimpleTimeZone(offset.minutes);
596+ return SysTime.fromUnixTime(toUnixTime, timezone) + getFraction! 7. hnsecs;
590597 }
591598 else
592599 static if (T.stringof == " Duration" )
@@ -710,23 +717,24 @@ struct Timestamp
710717 version (mir_test)
711718 @safe pure nothrow unittest
712719 {
713- assert (Timestamp.init.toString == " 0000T" );
714- assert (Timestamp(2010 , 7 , 4 ).toString == " 2010-07-04" );
715- assert (Timestamp(1998 , 12 , 25 ).toString == " 1998-12-25" );
716- assert (Timestamp(0 , 1 , 5 ).toString == " 0000-01-05" );
717- assert (Timestamp(- 4 , 1 , 5 ).toString == " -0004-01-05" );
720+ import mir.test;
721+ Timestamp.init.toString.should == " 0000T" ;
722+ Timestamp(2010 , 7 , 4 ).toString.should == " 2010-07-04" ;
723+ Timestamp(1998 , 12 , 25 ).toString.should == " 1998-12-25" ;
724+ Timestamp(0 , 1 , 5 ).toString.should == " 0000-01-05" ;
725+ Timestamp(- 4 , 1 , 5 ).toString.should == " -0004-01-05" ;
718726
719727 // yyyy-mm-ddThh:mm:ss[.mmm]±hh:mm
720- assert ( Timestamp(2021 ).toString == " 2021T" ) ;
721- assert ( Timestamp(2021 , 01 ).toString == " 2021-01T" , Timestamp( 2021 , 01 ).toString) ;
722- assert ( Timestamp(2021 , 01 , 29 ).toString == " 2021-01-29" ) ;
723- assert ( Timestamp(2021 , 01 , 29 , 19 , 42 ).toString == " 2021-01-29T19:42Z" ) ;
724- assert ( Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 ).toString == " 2021-01-29T19:42:44+07" , Timestamp( 2021 , 01 , 29 , 12 , 42 , 44 ).withOffset( 7 * 60 ).toString) ;
725- assert ( Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 + 30 ).toString == " 2021-01-29T20:12:44+07:30" ) ;
726-
727- assert ( Timestamp.onlyTime(7 , 40 ).toString == " 07:40Z " ) ;
728- assert ( Timestamp.onlyTime(7 , 40 , 30 ).toString == " 07:40:30Z " ) ;
729- assert ( Timestamp.onlyTime(7 , 40 , 30 , - 3 , 56 ).toString == " 07:40:30.056Z" ) ;
728+ Timestamp(2021 ).toString.should == " 2021T" ;
729+ Timestamp(2021 , 01 ).toString.should == " 2021-01T" ;
730+ Timestamp(2021 , 01 , 29 ).toString.should == " 2021-01-29" ;
731+ Timestamp(2021 , 01 , 29 , 19 , 42 ).withOffset( 0 ). toString.should == " 2021-01-29T19:42Z" ;
732+ Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 ).toString.should == " 2021-01-29T19:42:44+07" ;
733+ Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 + 30 ).toString.should == " 2021-01-29T20:12:44+07:30" ;
734+
735+ Timestamp.onlyTime(7 , 40 ).toString.should == " 07:40 " ;
736+ Timestamp.onlyTime(7 , 40 , 30 ).toString.should == " 07:40:30 " ;
737+ Timestamp.onlyTime(7 , 40 , 30 , - 3 , 56 ).withOffset( 0 ). toString.should == " 07:40:30.056Z" ;
730738 }
731739
732740 // /
@@ -748,9 +756,9 @@ struct Timestamp
748756 assert (Timestamp(- 9999 , 7 , 4 ).toISOExtString == " -9999-07-04" );
749757 assert (Timestamp(- 10000 , 10 , 20 ).toISOExtString == " -10000-10-20" );
750758
751- assert (Timestamp.onlyTime(7 , 40 ).toISOExtString == " 07:40Z " );
752- assert (Timestamp.onlyTime(7 , 40 , 30 ).toISOExtString == " 07:40:30Z " );
753- assert (Timestamp.onlyTime(7 , 40 , 30 , - 3 , 56 ).toISOExtString == " 07:40:30.056Z " );
759+ assert (Timestamp.onlyTime(7 , 40 ).toISOExtString == " 07:40 " );
760+ assert (Timestamp.onlyTime(7 , 40 , 30 ).toISOExtString == " 07:40:30 " );
761+ assert (Timestamp.onlyTime(7 , 40 , 30 , - 3 , 56 ).toISOExtString == " 07:40:30.056 " );
754762
755763 const cdate = Timestamp(1999 , 7 , 6 );
756764 immutable idate = Timestamp(1999 , 7 , 6 );
@@ -786,14 +794,14 @@ struct Timestamp
786794 assert (Timestamp(2021 ).toISOString == " 2021T" );
787795 assert (Timestamp(2021 , 01 ).toISOString == " 2021-01T" ); // always extended
788796 assert (Timestamp(2021 , 01 , 29 ).toISOString == " 20210129" );
789- assert (Timestamp(2021 , 01 , 29 , 19 , 42 ).toISOString == " 20210129T1942Z " );
797+ assert (Timestamp(2021 , 01 , 29 , 19 , 42 ).toISOString == " 20210129T1942 " );
790798 assert (Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 ).toISOString == " 20210129T194244+07" );
791799 assert (Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 + 30 ).toISOString == " 20210129T201244+0730" );
792800 static assert (Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 + 30 ).toISOString == " 20210129T201244+0730" );
793801
794- assert (Timestamp.onlyTime(7 , 40 ).toISOString == " T0740Z " );
795- assert (Timestamp.onlyTime(7 , 40 , 30 ).toISOString == " T074030Z " );
796- assert (Timestamp.onlyTime(7 , 40 , 30 , - 3 , 56 ).toISOString == " T074030.056Z " );
802+ assert (Timestamp.onlyTime(7 , 40 ).toISOString == " T0740 " );
803+ assert (Timestamp.onlyTime(7 , 40 , 30 ).toISOString == " T074030 " );
804+ assert (Timestamp.onlyTime(7 , 40 , 30 , - 3 , 56 ).toISOString == " T074030.056 " );
797805 }
798806
799807 // /
@@ -957,6 +965,9 @@ struct Timestamp
957965 }
958966 }
959967
968+ if (t.isLocalTime)
969+ return ;
970+
960971 if (t.offset == 0 )
961972 {
962973 w.put(' Z' );
@@ -1011,9 +1022,9 @@ struct Timestamp
10111022 // assert(Timestamp(2021, 01) == Timestamp.fromISOString("2021-01T"));
10121023 assert (Timestamp(2021 , 01 , 29 ) == Timestamp.fromISOString(" 20210129" ));
10131024 assert (Timestamp(2021 , 01 , 29 , 19 , 42 ) == Timestamp.fromISOString(" 20210129T1942" ));
1014- assert (Timestamp(2021 , 01 , 29 , 19 , 42 ) == Timestamp.fromISOString(" 20210129T1942Z" ));
1025+ assert (Timestamp(2021 , 01 , 29 , 19 , 42 ).withOffset( 0 ) == Timestamp.fromISOString(" 20210129T1942Z" ));
10151026 assert (Timestamp(2021 , 01 , 29 , 19 , 42 , 12 ) == Timestamp.fromISOString(" 20210129T194212" ));
1016- assert (Timestamp(2021 , 01 , 29 , 19 , 42 , 12 , - 3 , 67 ) == Timestamp.fromISOString(" 20210129T194212.067Z" ));
1027+ assert (Timestamp(2021 , 01 , 29 , 19 , 42 , 12 , - 3 , 67 ).withOffset( 0 ) == Timestamp.fromISOString(" 20210129T194212.067Z" ));
10171028 assert (Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 ) == Timestamp.fromISOString(" 20210129T194244+07" ));
10181029 assert (Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 + 30 ) == Timestamp.fromISOString(" 20210129T201244+0730" ));
10191030 static assert (Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 + 30 ) == Timestamp.fromISOString(" 20210129T201244+0730" ));
@@ -1127,9 +1138,9 @@ struct Timestamp
11271138 assert (Timestamp(2021 , 01 ) == Timestamp.fromISOExtString(" 2021-01T" ));
11281139 assert (Timestamp(2021 , 01 , 29 ) == Timestamp.fromISOExtString(" 2021-01-29" ));
11291140 assert (Timestamp(2021 , 01 , 29 , 19 , 42 ) == Timestamp.fromISOExtString(" 2021-01-29T19:42" ));
1130- assert (Timestamp(2021 , 01 , 29 , 19 , 42 ) == Timestamp.fromISOExtString(" 2021-01-29T19:42Z" ));
1141+ assert (Timestamp(2021 , 01 , 29 , 19 , 42 ).withOffset( 0 ) == Timestamp.fromISOExtString(" 2021-01-29T19:42Z" ));
11311142 assert (Timestamp(2021 , 01 , 29 , 19 , 42 , 12 ) == Timestamp.fromISOExtString(" 2021-01-29T19:42:12" ));
1132- assert (Timestamp(2021 , 01 , 29 , 19 , 42 , 12 , - 3 , 67 ) == Timestamp.fromISOExtString(" 2021-01-29T19:42:12.067Z" ));
1143+ assert (Timestamp(2021 , 01 , 29 , 19 , 42 , 12 , - 3 , 67 ).withOffset( 0 ) == Timestamp.fromISOExtString(" 2021-01-29T19:42:12.067Z" ));
11331144 assert (Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 ) == Timestamp.fromISOExtString(" 2021-01-29T19:42:44+07" ));
11341145 assert (Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 + 30 ) == Timestamp.fromISOExtString(" 2021-01-29T20:12:44+07:30" ));
11351146 static assert (Timestamp(2021 , 01 , 29 , 12 , 42 , 44 ).withOffset(7 * 60 + 30 ) == Timestamp.fromISOExtString(" 2021-01-29T20:12:44+07:30" ));
@@ -1469,7 +1480,10 @@ struct Timestamp
14691480 }
14701481
14711482 if (str == " Z" )
1483+ {
1484+ value.offset = 0 ;
14721485 return true ;
1486+ }
14731487
14741488 int hour;
14751489 int minute;
@@ -1532,6 +1546,6 @@ unittest
15321546 ts.fractionCoefficient = nanosec;
15331547 ts.fractionExponent = - 9 ;
15341548 }
1535- auto ts2 = " 1900-01-01T00:00:00.000000000Z " .Timestamp;
1549+ auto ts2 = " 1900-01-01T00:00:00.000000000 " .Timestamp;
15361550 assert (ts == ts2);
15371551}
0 commit comments