1 // =================================
   2 // Copyright (c) 2021 Seppo Laakko
   3 // Distributed under the MIT license
   4 // =================================
   5 
   6 using System.Concepts;
   7 
   8 namespace System
   9 {
  10     public class ConversionException : Exception
  11     {
  12         public nothrow ConversionException(const string& message) : base(message)
  13         {
  14         }
  15     }
  16 
  17     public void ThrowConversionException(const string& message)
  18     {
  19         throw ConversionException(message);
  20     }
  21 
  22     public nothrow string ToString<IU>(I x) where I is SignedInteger and U is UnsignedInteger and ExplicitlyConvertible<IU> and ExplicitlyConvertible<Ubyte>
  23     {
  24         string s;
  25         U u = 0u;
  26         bool neg = x < 0;
  27         if (neg)
  28         {
  29             u = -cast<U>(x);
  30         }
  31         else
  32         {
  33             u = cast<U>(x);
  34         }
  35         do
  36         {
  37             s.Append(cast<char>(cast<byte>('0') + cast<byte>(u % 10u)));
  38             u = u / 10u;
  39         }
  40         while (u != 0u);
  41         if (neg)
  42         {
  43             s.Append('-');
  44         }
  45         Reverse(s.Begin()s.End());
  46         return s;
  47     }
  48 
  49     public nothrow string ToString<U>(U x) where U is UnsignedInteger and ExplicitlyConvertible<Ubyte>
  50     {
  51         string s;
  52         do
  53         {
  54             s.Append(cast<char>(cast<byte>('0') + cast<byte>(x % 10u)));
  55             x = x / 10u;
  56         }
  57         while (x != 0u);
  58         Reverse(s.Begin()s.End());
  59         return s;
  60     }
  61 
  62     public nothrow string ToString(sbyte x)
  63     {
  64         return ToString(cast<int>(x));
  65     }
  66 
  67     public nothrow string ToString(byte x)
  68     {
  69         return ToString(cast<uint>(x));
  70     }
  71 
  72     public nothrow string ToString(short x)
  73     {
  74         return ToString(cast<int>(x));
  75     }
  76 
  77     public nothrow string ToString(ushort x)
  78     {
  79         return ToString(cast<uint>(x));
  80     }
  81 
  82     public nothrow string ToString(int x)
  83     {
  84         return ToString<intuint>(x);
  85     }
  86 
  87     public nothrow string ToString(uint x)
  88     {
  89         return ToString<uint>(x);
  90     }
  91 
  92     public nothrow string ToString(long x)
  93     {
  94         return ToString<longulong>(x);
  95     }
  96 
  97     public nothrow string ToString(ulong x)
  98     {
  99         return ToString<ulong>(x);
 100     }
 101 
 102     public nothrow string ToString(float f)
 103     {
 104         return ToString(cast<double>(f));
 105     }
 106 
 107     public nothrow string ToString(double xint maxNumDecimals)
 108     {
 109         return ToString(x0maxNumDecimals);
 110     }
 111 
 112     public nothrow string ToString(double x)
 113     {
 114         return ToString(x15);
 115     }
 116 
 117     public nothrow string ToString(double xint minNumDecimalsint maxNumDecimals)
 118     {
 119         string result;
 120         if (x < 0)
 121         {
 122             x = -x;
 123             result.Append('-');
 124         }
 125         result.Append(ToString(cast<int>(x)));
 126         double d = x - cast<int>(x);
 127         if (d > 0 || minNumDecimals > 0)
 128         {
 129             result.Append('.');
 130             for (int i = 0; (d > 0 || i < minNumDecimals) && i < maxNumDecimals; ++i;)
 131             {
 132                 d = 10 * d;
 133                 int digit = cast<int>(d) % 10;
 134                 result.Append(cast<char>(cast<int>('0') + digit));
 135                 d = d - cast<int>(d);
 136             }
 137         }
 138         return result;
 139     }
 140 
 141     public nothrow string ToString(char c)
 142     {
 143         return string(c);
 144     }
 145 
 146     public string ToString(wchar c)
 147     {
 148         wstring s(c);
 149         return ToUtf8(s);
 150     }
 151 
 152     public string ToString(uchar c)
 153     {
 154         ustring s(c);
 155         return ToUtf8(s);
 156     }
 157 
 158     public nothrow string ToString(bool b)
 159     {
 160         if (b)
 161         {
 162             return "true";
 163         }
 164         return "false";
 165     }
 166 
 167     public inline char HexChar(byte nibble)
 168     {
 169         if ((nibble & ~0x0Fu) != 0u)
 170         {
 171             ThrowInvalidParameterException();
 172         }
 173         const char* hex = "0123456789ABCDEF";
 174         return hex[nibble];
 175     }
 176 
 177     public nothrow string ToHexString<U>(U x) where U is UnsignedInteger and ExplicitlyConvertible<Ubyte>
 178     {
 179         string s;
 180         long n = sizeof(x);
 181         for (long i = 0; i < n; ++i;)
 182         {
 183             byte b = cast<byte>(x & 0xFFu);
 184             s.Append(HexChar(b & 0x0Fu));
 185             s.Append(HexChar(b >> 4u));
 186             x = x >> 8u;
 187         }
 188         Reverse(s.Begin()s.End());
 189         return s;
 190     }
 191 
 192     public nothrow string ToHexString(byte b)
 193     {
 194         string s;
 195         s.Append(HexChar(b >> 4u));
 196         s.Append(HexChar(b & 0x0Fu));
 197         return s;
 198     }
 199 
 200     public nothrow string ToHexString(ushort u)
 201     {
 202         return ToHexString<ushort>(u);
 203     }
 204 
 205     public nothrow string ToHexString(uint u)
 206     {
 207         return ToHexString<uint>(u);
 208     }
 209 
 210     public nothrow string ToHexString(ulong u)
 211     {
 212         return ToHexString<ulong>(u);
 213     }
 214 
 215     public nothrow string ToOctalString(ulong x)
 216     {
 217         return ToOctalString(x0);
 218     }
 219 
 220     public nothrow string ToOctalString(ulong xint minDigits)
 221     {
 222         if (minDigits < 0)
 223         {
 224             ThrowInvalidParameterException();
 225         }
 226         string s;
 227         ++minDigits;
 228         do
 229         {
 230             s.Append(cast<char>(cast<int>(x & 7u) + cast<int>('0')));
 231             x = x >> 3u;
 232             if (minDigits > 0)
 233             {
 234                 --minDigits;
 235             }
 236         }
 237         while (minDigits != 0 || x != 0u);
 238         if (s[s.Length() - 1] != '0')
 239         {
 240             s.Append('0');
 241         }
 242         Reverse(s.Begin()s.End());
 243         return s;
 244     }
 245 
 246     public nothrow bool ParseSigned<T>(const string& sT& x) where T is SignedInteger
 247     {
 248         x = cast<T>(0);
 249         if (s.IsEmpty()) return false;
 250         bool negative = false;
 251         int state = 0;
 252         for (char c : s)
 253         {
 254             switch (state)
 255             {
 256                 case 0:
 257                 {
 258                     if (c == '+')
 259                     {
 260                         state = 1;
 261                     }
 262                     else if (c == '-')
 263                     {
 264                         negative = true;
 265                         state = 1;
 266                     }
 267                     else if (c >= '0' && c <= '9')
 268                     {
 269                         x = cast<T>(c) - cast<T>('0');
 270                         state = 1;
 271                     }
 272                     else
 273                     {
 274                         return false;
 275                     }
 276                     break;
 277                 }
 278                 case 1:
 279                 {
 280                     if (c >= '0' && c <= '9')
 281                     {
 282                         x = 10 * x + cast<T>(c) - cast<T>('0');
 283                     }
 284                     else
 285                     {
 286                         return false;
 287                     }
 288                     break;
 289                 }
 290             }
 291         }
 292         if (state != 1)
 293         {
 294             return false;
 295         }
 296         else
 297         {
 298             if (negative)
 299             {
 300                 x = -x;
 301             }
 302             return true;
 303         }
 304     }
 305 
 306     public nothrow bool ParseUnsigned<T>(const string& sT& x) where T is UnsignedInteger
 307     {
 308         x = cast<T>(0u);
 309         if (s.IsEmpty())
 310         {
 311             return false;
 312         }
 313         int state = 0;
 314         for (char c : s)
 315         {
 316             switch (state)
 317             {
 318                 case 0:
 319                 {
 320                     if (c == '+')
 321                     {
 322                         state = 1;
 323                     }
 324                     else if (c >= '0' && c <= '9')
 325                     {
 326                         x = cast<T>(c) - cast<T>('0');
 327                         state = 1;
 328                     }
 329                     else
 330                     {
 331                         return false;
 332                     }
 333                     break;
 334                 }
 335                 case 1:
 336                 {
 337                     if (c >= '0' && c <= '9')
 338                     {
 339                         x = 10u * x + cast<T>(c) - cast<T>('0');
 340                     }
 341                     else
 342                     {
 343                         return false;
 344                     }
 345                     break;
 346                 }
 347             }
 348         }
 349         if (state != 1)
 350         {
 351             return false;
 352         }
 353         return true;
 354     }
 355 
 356     public nothrow bool ParseHex<T>(const string& sT& x) where T is UnsignedInteger
 357     {
 358         x = cast<T>(0u);
 359         if (s.IsEmpty())
 360         {
 361             return false;
 362         }
 363         for (char c : s)
 364         {
 365             if (c >= '0' && c <= '9')
 366             {
 367                 x = 16u * x + (cast<T>(c) - cast<T>('0'));
 368             }
 369             else if (c >= 'a' && c <= 'f')
 370             {
 371                 x = 16u * x + 10u + (cast<T>(c) - cast<T>('a'));
 372             }
 373             else if (c >= 'A' && c <= 'F')
 374             {
 375                 x = 16u * x + 10u + (cast<T>(c) - cast<T>('A'));
 376             }
 377             else
 378             {
 379                 return false;
 380             }
 381         }
 382         return true;
 383     }
 384 
 385     public nothrow bool ParseFloating<T>(const string& sT& x)
 386     {
 387         x = cast<T>(0.0);
 388         if (s.IsEmpty())
 389         {
 390             return false;
 391         }
 392         bool negative = false;
 393         int start = 0;
 394         if (s[0] == '+')
 395         {
 396             ++start;
 397         }
 398         else if (s[0] == '-')
 399         {
 400             negative = true;
 401             ++start;
 402         }
 403         int state = 0;
 404         T d = cast<T>(10.0);
 405         int exponent = 0;
 406         bool negativeExponent = false;
 407         long n = s.Length();
 408         for (long i = start; i < n; ++i;)
 409         {
 410             char c = s[i];
 411             switch (state)
 412             {
 413                 case 0:
 414                 {
 415                     if (c >= '0' && c <= '9')
 416                     {
 417                         x = 10 * x + (cast<int>(c) - cast<int>('0'));
 418                     }
 419                     else if (c == '.')
 420                     {
 421                         state = 1;
 422                     }
 423                     else if (c == 'e' || c == 'E')
 424                     {
 425                         state = 2;
 426                     }
 427                     else
 428                     {
 429                         return false;
 430                     }
 431                     break;
 432                 }
 433                 case 1:
 434                 {
 435                     if (c >= '0' && c <= '9')
 436                     {
 437                         x = x + (cast<int>(c) - cast<int>('0')) / d;
 438                         d = 10 * d;
 439                     }
 440                     else if (c == 'e' || c == 'E')
 441                     {
 442                         state = 2;
 443                     }
 444                     else
 445                     {
 446                         return false;
 447                     }
 448                     break;
 449                 }
 450                 case 2:
 451                 {
 452                     if (c == '+')
 453                     {
 454                         state = 3;
 455                     }
 456                     else if (c == '-')
 457                     {
 458                         negativeExponent = true;
 459                         state = 3;
 460                     }
 461                     else if (c >= '0' && c <= '9')
 462                     {
 463                         exponent = cast<int>(c) - cast<int>('0');
 464                         state = 3;
 465                     }
 466                     else
 467                     {
 468                         return false;
 469                     }
 470                     break;
 471                 }
 472                 case 3:
 473                 {
 474                     if (c >= '0' && c <= '9')
 475                     {
 476                         exponent = 10 * exponent + (cast<int>(c) - cast<int>('0'));
 477                     }
 478                     else
 479                     {
 480                         return false;
 481                     }
 482                     break;
 483                 }
 484             }
 485         }
 486         if (negative)
 487         {
 488             x = -x;
 489         }
 490         if (exponent != 0)
 491         {
 492             if (negativeExponent)
 493             {
 494                 exponent = -exponent;
 495             }
 496             x = x * cast<T>(Pow(10.0exponent));
 497         }
 498         return true;
 499     }
 500 
 501     public nothrow bool ParseSByte(const string& ssbyte& x)
 502     {
 503         return ParseSigned<sbyte>(sx);
 504     }
 505 
 506     public sbyte ParseSByte(const string& s)
 507     {
 508         sbyte x;
 509         if (ParseSByte(sx))
 510         {
 511             return x;
 512         }
 513         else
 514         {
 515             ThrowConversionException("cannot parse sbyte from string '" + s + "'");
 516         }
 517         return 0;
 518     }
 519 
 520     public nothrow bool ParseByte(const string& sbyte& x)
 521     {
 522         return ParseUnsigned<byte>(sx);
 523     }
 524 
 525     public byte ParseByte(const string& s)
 526     {
 527         byte x;
 528         if (ParseByte(sx))
 529         {
 530             return x;
 531         }
 532         else
 533         {
 534             ThrowConversionException("cannot parse byte from string '" + s + "'");
 535         }
 536         return 0u;
 537     }
 538 
 539     public nothrow bool ParseShort(const string& sshort& x)
 540     {
 541         return ParseSigned<short>(sx);
 542     }
 543 
 544     public short ParseShort(const string& s)
 545     {
 546         short x;
 547         if (ParseShort(sx))
 548         {
 549             return x;
 550         }
 551         else
 552         {
 553             ThrowConversionException("cannot parse short from string '" + s + "'");
 554         }
 555         return 0;
 556     }
 557 
 558     public nothrow bool ParseUShort(const string& sushort& x)
 559     {
 560         return ParseUnsigned<ushort>(sx);
 561     }
 562 
 563     public ushort ParseUShort(const string& s)
 564     {
 565         ushort x;
 566         if (ParseUShort(sx))
 567         {
 568             return x;
 569         }
 570         else
 571         {
 572             ThrowConversionException("cannot parse ushort from string '" + s + "'");
 573         }
 574         return 0u;
 575     }
 576 
 577     public nothrow bool ParseInt(const string& sint& x)
 578     {
 579         return ParseSigned<int>(sx);
 580     }
 581 
 582     public int ParseInt(const string& s)
 583     {
 584         int x;
 585         if (ParseInt(sx))
 586         {
 587             return x;
 588         }
 589         else
 590         {
 591             ThrowConversionException("cannot parse int from string '" + s + "'");
 592         }
 593         return 0;
 594     }
 595 
 596     public nothrow bool ParseUInt(const string& suint& x)
 597     {
 598         return ParseUnsigned<uint>(sx);
 599     }
 600 
 601     public uint ParseUInt(const string& s)
 602     {
 603         uint x;
 604         if (ParseUInt(sx))
 605         {
 606             return x;
 607         }
 608         else
 609         {
 610             ThrowConversionException("cannot parse uint from string '" + s + "'");
 611         }
 612         return 0u;
 613     }
 614 
 615     public nothrow bool ParseLong(const string& slong& x)
 616     {
 617         return ParseSigned<long>(sx);
 618     }
 619 
 620     public long ParseLong(const string& s)
 621     {
 622         long x;
 623         if (ParseLong(sx))
 624         {
 625             return x;
 626         }
 627         else
 628         {
 629             ThrowConversionException("cannot parse long from string '" + s + "'");
 630         }
 631         return 0;
 632     }
 633 
 634     public nothrow bool ParseULong(const string& sulong& x)
 635     {
 636         return ParseUnsigned<ulong>(sx);
 637     }
 638 
 639     public ulong ParseULong(const string& s)
 640     {
 641         ulong x;
 642         if (ParseULong(sx))
 643         {
 644             return x;
 645         }
 646         else
 647         {
 648             ThrowConversionException("cannot parse ulong from string '" + s + "'");
 649         }
 650         return 0u;
 651     }
 652 
 653     public nothrow bool ParseFloat(const string& sfloat& x)
 654     {
 655         return ParseFloating<float>(sx);
 656     }
 657 
 658     public float ParseFloat(const string& s)
 659     {
 660         float x;
 661         if (ParseFloat(sx))
 662         {
 663             return x;
 664         }
 665         else
 666         {
 667             ThrowConversionException("cannot parse float from string '" + s + "'");
 668         }
 669         return 0.0f;
 670     }
 671 
 672     public nothrow bool ParseDouble(const string& sdouble& x)
 673     {
 674         return ParseFloating<double>(sx);
 675     }
 676 
 677     public double ParseDouble(const string& s)
 678     {
 679         double x;
 680         if (ParseDouble(sx))
 681         {
 682             return x;
 683         }
 684         else
 685         {
 686             ThrowConversionException("cannot parse double from string '" + s + "'");
 687         }
 688         return 0.0;
 689     }
 690 
 691     public nothrow bool ParseBool(const string& sbool& x)
 692     {
 693         if (s == "true")
 694         {
 695             x = true;
 696             return true;
 697         }
 698         else if (s == "false")
 699         {
 700             x = false;
 701             return true;
 702         }
 703         else
 704         {
 705             x = false;
 706             return false;
 707         }
 708     }
 709 
 710     public bool ParseBool(const string& s)
 711     {
 712         bool x;
 713         if (ParseBool(sx))
 714         {
 715             return x;
 716         }
 717         else
 718         {
 719             ThrowConversionException("cannot parse bool from string '" + s + "'");
 720         }
 721         return false;
 722     }
 723 
 724     public nothrow bool ParseHexByte(const string& sbyte& x)
 725     {
 726         return ParseHex<byte>(sx);
 727     }
 728 
 729     public byte ParseHexByte(const string& s)
 730     {
 731         byte x;
 732         if (ParseHexByte(sx))
 733         {
 734             return x;
 735         }
 736         else
 737         {
 738             ThrowConversionException("cannot parse hex byte from string '" + s + "'");
 739         }
 740         return 0u;
 741     }
 742 
 743     public nothrow bool ParseHexUShort(const string& sushort& x)
 744     {
 745         return ParseHex<ushort>(sx);
 746     }
 747 
 748     public ushort ParseHexUShort(const string& s)
 749     {
 750         ushort x;
 751         if (ParseHexUShort(sx))
 752         {
 753             return x;
 754         }
 755         else
 756         {
 757             ThrowConversionException("cannot parse hex ushort from string '" + s + "'");
 758         }
 759         return 0u;
 760     }
 761 
 762     public nothrow bool ParseHexUInt(const string& suint& x)
 763     {
 764         return ParseHex<uint>(sx);
 765     }
 766 
 767     public uint ParseHexUInt(const string& s)
 768     {
 769         uint x;
 770         if (ParseHexUInt(sx))
 771         {
 772             return x;
 773         }
 774         else
 775         {
 776             ThrowConversionException("cannot parse hex uint from string '" + s + "'");
 777         }
 778         return 0u;
 779     }
 780 
 781     public nothrow bool ParseHexULong(const string& sulong& x)
 782     {
 783         return ParseHex<ulong>(sx);
 784     }
 785 
 786     public ulong ParseHexULong(const string& s)
 787     {
 788         ulong x;
 789         if (ParseHexULong(sx))
 790         {
 791             return x;
 792         }
 793         else
 794         {
 795             ThrowConversionException("cannot parse hex ulong from string '" + s + "'");
 796         }
 797         return 0u;
 798     }
 799 
 800     public nothrow bool ParseOctal(const string& sulong& x)
 801     {
 802         x = 0u;
 803         for (char c : s)
 804         {
 805             if (c >= '0' && c <= '7')
 806             {
 807                 x = 8u * x + cast<ulong>(cast<int>(c) - cast<int>('0'));
 808             }
 809             else
 810             {
 811                 return false;
 812             }
 813         }
 814         return true;
 815     }
 816 
 817     public nothrow ulong ParseOctal(const string& s)
 818     {
 819         ulong x = 0u;
 820         if (ParseOctal(sx))
 821         {
 822             return x;
 823         }
 824         else
 825         {
 826             ThrowConversionException("cannot parse octal from string '" + s + "'");
 827         }
 828         return 0u;
 829     }
 830 
 831     public nothrow bool ParseDate(const string& dateStrDate& date)
 832     {
 833         int dateEnd = 0;
 834         return ParseDate(dateStrdatedateEnd);
 835     }
 836 
 837     public nothrow bool ParseDate(const string& dateStrDate& dateint& dateEnd)
 838     {
 839         short year;
 840         if (dateStr.Length() < 4 + 2 + 2)
 841         {
 842             return false;
 843         }
 844         if (!ParseShort(dateStr.Substring(04)year))
 845         {
 846             return false;
 847         }
 848         int monthStart = 4;
 849         if (dateStr[4] == '-')
 850         {
 851             ++monthStart;
 852         }
 853         if (dateStr.Length() < monthStart + 2)
 854         {
 855             return false;
 856         }
 857         sbyte month;
 858         if (!ParseSByte(dateStr.Substring(monthStart2)month))
 859         {
 860             return false;
 861         }
 862         if (month < 1 || month > 12)
 863         {
 864             return false;
 865         }
 866         int dayStart = monthStart + 2;
 867         if (dateStr[dayStart] == '-')
 868         {
 869             ++dayStart;
 870         }
 871         if (dateStr.Length() < dayStart + 2)
 872         {
 873             return false;
 874         }
 875         sbyte day;
 876         if (!ParseSByte(dateStr.Substring(dayStart2)day))
 877         {
 878             return false;
 879         }
 880         if (day < 1 || day > 31)
 881         {
 882             return false;
 883         }
 884         dateEnd = dayStart + 2;
 885         date = Date(yearcast<Month>(month)day);
 886         return true;
 887     }
 888 
 889     public Date ParseDate(const string& s)
 890     {
 891         Date date;
 892         if (!ParseDate(sdate))
 893         {
 894             ThrowConversionException("cannot parse date from string '" + s + "': not in format YYYY-MM-DD or YYYYMMDD");
 895         }
 896         return date;
 897     }
 898 
 899     public bool ParseDateTime(const string& dateTimeStrDateTime& dateTime)
 900     {
 901         Date date;
 902         int dateEnd = 0;
 903         if (!ParseDate(dateTimeStrdatedateEnd))
 904         {
 905             return false;
 906         }
 907         int hours = 0;
 908         int mins = 0;
 909         int secs = 0;
 910         if (dateTimeStr.Length() > dateEnd)
 911         {
 912             if (dateTimeStr[dateEnd] == 'T')
 913             {
 914                 int hoursStart = dateEnd + 1;
 915                 if (!ParseInt(dateTimeStr.Substring(hoursStart2)hours))
 916                 {
 917                     return false;
 918                 }
 919                 if (hours < 0 || hours > 24)
 920                 {
 921                     return false;
 922                 }
 923                 if (dateTimeStr.Length() > hoursStart + 2)
 924                 {
 925                     int minsStart = hoursStart + 2;
 926                     if (dateTimeStr[minsStart] == ':')
 927                     {
 928                         ++minsStart;
 929                     }
 930                     if (!ParseInt(dateTimeStr.Substring(minsStart2)mins))
 931                     {
 932                         return false;
 933                     }
 934                     if (mins < 0 || mins >= 60)
 935                     {
 936                         return false;
 937                     }
 938                     if (dateTimeStr.Length() > minsStart + 2)
 939                     {
 940                         int secsStart = minsStart + 2;
 941                         if (dateTimeStr[secsStart] == ':')
 942                         {
 943                             ++secsStart;
 944                         }
 945                         if (!ParseInt(dateTimeStr.Substring(secsStart2)secs))
 946                         {
 947                             return false;
 948                         }
 949                         if (secs < 0 || secs > 60)
 950                         {
 951                             return false;
 952                         }
 953                     }
 954                 }
 955             }
 956         }
 957         int totalSecs = hours * 3600 + mins * 60 + secs;
 958         dateTime = DateTime(datetotalSecs);
 959         return true;
 960     }
 961 
 962     public DateTime ParseDateTime(const string& s)
 963     {
 964         DateTime dateTime;
 965         if (!ParseDateTime(sdateTime))
 966         {
 967             ThrowConversionException("cannot parse date time from string '" + s + "': not in format YYYY[-]MM[-]DD or YYYY[-]MM[-]DDTHH[[:]MM[[:]SS]]");
 968         }
 969         return dateTime;
 970     }
 971 
 972     public nothrow bool ParseTimestamp(const string& sTimestamp& timestamp)
 973     {
 974         if (s.Length() != 20 + 9)
 975         {
 976             return false;
 977         }
 978         DateTime dateTime;
 979         if (!ParseDateTime(s.Substring(019)dateTime))
 980         {
 981             return false;
 982         }
 983         int nanosecs = 0;
 984         if (!ParseInt(s.Substring(20)nanosecs))
 985         {
 986             return false;
 987         }
 988         timestamp = Timestamp(dateTimenanosecs);
 989         return true;
 990     }
 991 
 992     public Timestamp ParseTimestamp(const string& s)
 993     {
 994         if (s.Length() != 20 + 9)
 995         {
 996             ThrowConversionException("cannot parse timestamp from string '" + s + "': not in format YYYY-MM-DDTHH:MM:SS.nnnnnnnnn");
 997         }
 998         DateTime dateTime = ParseDateTime(s.Substring(019));
 999         int nanosecs = ParseInt(s.Substring(20));
1000         return Timestamp(dateTimenanosecs);
1001     }
1002 }