1 /**
2  * DDBC - D DataBase Connector - abstraction layer for RDBMS access, with interface similar to JDBC. 
3  * 
4  * Source file ddbc/drivers/mysqlddbc.d.
5  *
6  * DDBC library attempts to provide implementation independent interface to different databases.
7  * 
8  * Set of supported RDBMSs can be extended by writing Drivers for particular DBs.
9  * 
10  * JDBC documentation can be found here:
11  * $(LINK http://docs.oracle.com/javase/1.5.0/docs/api/java/sql/package-summary.html)$(BR)
12  *
13  * This module contains misc utility functions which may help in implementation of DDBC drivers.
14  * 
15  * Copyright: Copyright 2013
16  * License:   $(LINK www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
17  * Author:   Vadim Lopatin
18  */
19 module ddbc.drivers.utils;
20 
21 private import std.conv : ConvException;
22 private import std.datetime : Date, DateTime, TimeOfDay;
23 private import std.datetime.date;
24 private import std.datetime.systime : SysTime;
25 private import std.datetime.timezone : UTC;
26 private import std.format : formattedRead;
27 //private import std.traits : isSomeString;
28 private import std.algorithm : canFind;
29 
30 string copyCString(T)(const T* c, int actualLength = -1) if (is(T == char) || is (T == ubyte)) {
31     const(T)* a = c;
32     if(a is null)
33         return null;
34     
35     if(actualLength == -1) {
36         T[] ret;
37         while(*a) {
38             ret ~= *a;
39             a++;
40         }
41         return cast(string)ret;
42     } else {
43         return cast(string)(a[0..actualLength].idup);
44     }
45     
46 }
47 
48 SysTime parseSysTime(const string timestampString) @safe {
49     try {
50         import std.regex : match;
51         if(match(timestampString, r"\d{4}-\D{3}-\d{2}.*")) {
52             return SysTime.fromSimpleString(timestampString);
53         } else if(match(timestampString, r".*[\+|\-]\d{1,2}:\d{1,2}|.*Z")) {
54             return timestampString.canFind('-') ?
55                 SysTime.fromISOExtString(timestampString) :
56                 SysTime.fromISOString(timestampString);
57         } else {
58             return SysTime(parseDateTime(timestampString), UTC());
59         }
60     } catch (ConvException e) {
61         // static if(__traits(compiles, (){ import std.experimental.logger; } )) {
62         //     import std.experimental.logger : sharedLog; 
63         //     sharedLog.error("Could not parse " ~ timestampString ~ " to SysTime", e);
64         // }
65         throw new DateTimeException("Can not convert '" ~ timestampString ~ "' to SysTime");
66     }
67 }
68 
69 unittest {
70     // Accept valid (as per D language) systime formats
71     parseSysTime("2019-May-04 13:34:10.500Z");
72     parseSysTime("2019-Jan-02 13:34:10-03:00");
73     parseSysTime("2019-05-04T13:34:10.500Z");
74     parseSysTime("2019-06-14T13:34:10.500+01:00");
75     parseSysTime("2019-02-07T13:34:10Z");
76     parseSysTime("2019-08-12T13:34:10+01:00");
77     parseSysTime("2019-09-03T13:34:10");
78 
79     // Accept valid (as per D language) date & datetime timestamps (will default timezone as UTC)
80     parseSysTime("2010-Dec-30 00:00:00");
81     parseSysTime("2019-05-04 13:34:10");
82     // parseSysTime("2019-05-08");
83 
84     // Accept non-standard (as per D language) timestamp formats
85     //parseSysTime("2019-05-07 13:32"); // todo: handle missing seconds
86     //parseSysTime("2019/05/07 13:32"); // todo: handle slash instead of hyphen
87     //parseSysTime("2010-12-30 12:10:04.1+00"); // postgresql
88 }
89 
90 DateTime parseDateTime(const string timestampString) @safe {
91     try {
92         import std.regex : match;
93         if(match(timestampString, r"\d{8}T\d{6}")) {
94             // ISO String: 'YYYYMMDDTHHMMSS'
95             return DateTime.fromISOString(timestampString);
96         } else if(match(timestampString, r"\d{4}-\D{3}-\d{2}.*")) {
97             // Simple String 'YYYY-Mon-DD HH:MM:SS'
98             return DateTime.fromSimpleString(timestampString);
99         } else if(match(timestampString, r"\d{4}-\d{2}-\d{2}.*")) {
100             // ISO ext string 'YYYY-MM-DDTHH:MM:SS'
101             import std..string : translate;
102             return DateTime.fromISOExtString(timestampString.translate( [ ' ': 'T' ] ));
103         }
104         throw new DateTimeException("Can not convert " ~ timestampString);
105     } catch (ConvException e) {
106         // static if(__traits(compiles, (){ import std.experimental.logger; } )) {
107         //     import std.experimental.logger : sharedLog;
108         //     sharedLog.error("Could not parse " ~ timestampString ~ " to SysTime", e);
109         // }
110         throw new DateTimeException("Can not convert '" ~ timestampString ~ "' to DateTime");
111     }
112 }
113 unittest {
114     // Accept valid (as per D language) datetime formats
115     parseDateTime("20101230T000000");
116     parseDateTime("2019-May-04 13:34:10");
117     parseDateTime("2019-Jan-02 13:34:10");
118     parseDateTime("2019-05-04T13:34:10");
119 
120     // Accept non-standard (as per D language) timestamp formats
121     parseDateTime("2019-06-14 13:34:10"); // accept a non-standard variation (space instead of T)
122     //parseDateTime("2019-05-07 13:32"); // todo: handle missing seconds
123     //parseDateTime("2019/05/07 13:32"); // todo: handle slash instead of hyphen
124 }
125 
126 TimeOfDay parseTimeoid(const string timeoid)
127 {
128     string input = timeoid.dup;
129     int hour, min, sec;
130     formattedRead(input, "%s:%s:%s", &hour, &min, &sec);
131     return TimeOfDay(hour, min, sec);
132 }
133 
134 Date parseDateoid(const string dateoid)
135 {
136     string input = dateoid.dup;
137     int year, month, day;
138     formattedRead(input, "%s-%s-%s", &year, &month, &day);
139     return Date(year, month, day);
140 }