Skip to content

@JesseKPhillips /dateparse.d
Created

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
std.dateparse braught to use std.datetime
// Written in the D programming language.
/**
* $(RED Deprecated. It will be removed in February 2012.
* Please use std.datetime instead.)
*
* dateparse module.
*
* Copyright: Copyright Digital Mars 2000 - 2009.
* License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
* Authors: $(WEB digitalmars.com, Walter Bright)
* Source: $(PHOBOSSRC std/_dateparse.d)
*/
/*
* Copyright Digital Mars 2000 - 2009.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
module dateparse;
private
{
import std.algorithm, std.string;
import std.c.stdlib;
import std.datetime;
}
//debug=dateparse;
class DateParseException : Exception
{
this(string s)
{
super("Invalid date string: " ~ s);
}
}
auto dateParse(string s, out SysTime date) {
DateParse dp;
dp.parse(s, date);
return date;
}
struct DateParse
{
void parse(string s, out SysTime date)
{
this = DateParse.init;
//version (Win32)
buffer = (cast(char *)alloca(s.length))[0 .. s.length];
//else
//buffer = new char[s.length];
debug(dateparse) printf("DateParse.parse('%.*s')\n", s);
if (!parseString(s))
{
goto Lerror;
}
/+
if (year == year.init)
year = 0;
else
+/
debug(dateparse)
printf("year = %d, month = %d, day = %d\n%02d:%02d:%02d.%03d\nweekday = %d, tzcorrection = %d\n",
year, month, day,
hours, minutes, seconds, ms,
weekday, tzcorrection);
if (
year == year.init ||
(month < 1 || month > 12) ||
(day < 1 || day > 31) ||
(hours < 0 || hours > 23) ||
(minutes < 0 || minutes > 59) ||
(seconds < 0 || seconds > 59) ||
(tzcorrection != int.min &&
((tzcorrection < -2300 || tzcorrection > 2300) ||
(tzcorrection % 10)))
)
{
Lerror:
throw new DateParseException(s);
}
if (ampm)
{ if (hours > 12)
goto Lerror;
if (hours < 12)
{
if (ampm == 2) // if P.M.
hours += 12;
}
else if (ampm == 1) // if 12am
{
hours = 0; // which is midnight
}
}
// if (tzcorrection != tzcorrection.init)
// tzcorrection /= 100;
if (year >= 0 && year <= 99)
year += 1900;
if(tzcorrection == int.min)
date = SysTime(DateTime(year,month,day,hours,minutes,seconds),
FracSec.from!"msecs"(ms));
else {
auto tz =
new SimpleTimeZone(tzcorrection/100*60+tzcorrection%100);
date = SysTime(DateTime(year,month,day,hours,minutes,seconds),
FracSec.from!"msecs"(ms), tz);
}
debug(dateparse) printf("year = %d, month = %d, day = %d\n%02d:%02d:%02d.%03d\ntzcorrection = %d\n",
date.year, date.month, date.day,
date.hour, date.minute, date.second, date.fracSec,
date.timezone);
}
private:
int year = int.min; // our "nan" Date value
Month month; // 1..12
int day; // 1..31
int hours; // 0..23
int minutes; // 0..59
int seconds; // 0..59
int ms; // 0..999
int weekday; // 1..7
int ampm; // 0: not specified
// 1: AM
// 2: PM
int tzcorrection = int.min; // -1200..1200 correction in hours
string s;
int si;
int number;
char[] buffer;
enum DP : byte
{
err,
weekday,
month,
number,
end,
colon,
minus,
slash,
ampm,
plus,
tz,
dst,
dsttz,
}
DP nextToken()
{ int nest;
uint c;
int bi;
DP result = DP.err;
//printf("DateParse::nextToken()\n");
for (;;)
{
assert(si <= s.length);
if (si == s.length)
{ result = DP.end;
goto Lret;
}
//printf("\ts[%d] = '%c'\n", si, s[si]);
switch (s[si])
{
case ':': result = DP.colon; goto ret_inc;
case '+': result = DP.plus; goto ret_inc;
case '-': result = DP.minus; goto ret_inc;
case '/': result = DP.slash; goto ret_inc;
case '.':
version(DATE_DOT_DELIM)
{
result = DP.slash;
goto ret_inc;
}
else
{
si++;
break;
}
ret_inc:
si++;
goto Lret;
case ' ':
case '\n':
case '\r':
case '\t':
case ',':
si++;
break;
case '(': // comment
nest = 1;
for (;;)
{
si++;
if (si == s.length)
goto Lret; // error
switch (s[si])
{
case '(':
nest++;
break;
case ')':
if (--nest == 0)
goto Lendofcomment;
break;
default:
break;
}
}
Lendofcomment:
si++;
break;
default:
number = 0;
for (;;)
{
if (si == s.length)
// c cannot be undefined here
break;
c = s[si];
if (!(c >= '0' && c <= '9'))
break;
result = DP.number;
number = number * 10 + (c - '0');
si++;
}
if (result == DP.number)
goto Lret;
bi = 0;
bufloop:
while (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
{
if (c < 'a') // if upper case
c += cast(uint)'a' - cast(uint)'A'; // to lower case
buffer[bi] = cast(char)c;
bi++;
do
{
si++;
if (si == s.length)
break bufloop;
c = s[si];
} while (c == '.'); // ignore embedded '.'s
}
result = classify(buffer[0 .. bi].idup);
goto Lret;
}
}
Lret:
//printf("-DateParse::nextToken()\n");
return result;
}
DP classify(string buf)
{
struct DateID
{
string name;
DP tok;
short value;
}
static immutable DateID dateidtab[] =
[
{ "january", DP.month, 1},
{ "february", DP.month, 2},
{ "march", DP.month, 3},
{ "april", DP.month, 4},
{ "may", DP.month, 5},
{ "june", DP.month, 6},
{ "july", DP.month, 7},
{ "august", DP.month, 8},
{ "september", DP.month, 9},
{ "october", DP.month, 10},
{ "november", DP.month, 11},
{ "december", DP.month, 12},
{ "jan", DP.month, 1},
{ "feb", DP.month, 2},
{ "mar", DP.month, 3},
{ "apr", DP.month, 4},
{ "jun", DP.month, 6},
{ "jul", DP.month, 7},
{ "aug", DP.month, 8},
{ "sep", DP.month, 9},
{ "sept", DP.month, 9},
{ "oct", DP.month, 10},
{ "nov", DP.month, 11},
{ "dec", DP.month, 12},
{ "sunday", DP.weekday, 1},
{ "monday", DP.weekday, 2},
{ "tuesday", DP.weekday, 3},
{ "tues", DP.weekday, 3},
{ "wednesday", DP.weekday, 4},
{ "wednes", DP.weekday, 4},
{ "thursday", DP.weekday, 5},
{ "thur", DP.weekday, 5},
{ "thurs", DP.weekday, 5},
{ "friday", DP.weekday, 6},
{ "saturday", DP.weekday, 7},
{ "sun", DP.weekday, 1},
{ "mon", DP.weekday, 2},
{ "tue", DP.weekday, 3},
{ "wed", DP.weekday, 4},
{ "thu", DP.weekday, 5},
{ "fri", DP.weekday, 6},
{ "sat", DP.weekday, 7},
{ "am", DP.ampm, 1},
{ "pm", DP.ampm, 2},
{ "gmt", DP.tz, +000},
{ "ut", DP.tz, +000},
{ "utc", DP.tz, +000},
{ "wet", DP.tz, +000},
{ "z", DP.tz, +000},
{ "wat", DP.tz, +100},
{ "a", DP.tz, +100},
{ "at", DP.tz, +200},
{ "b", DP.tz, +200},
{ "c", DP.tz, +300},
{ "ast", DP.tz, +400},
{ "d", DP.tz, +400},
{ "est", DP.tz, +500},
{ "e", DP.tz, +500},
{ "cst", DP.tz, +600},
{ "f", DP.tz, +600},
{ "mst", DP.tz, +700},
{ "g", DP.tz, +700},
{ "pst", DP.tz, +800},
{ "h", DP.tz, +800},
{ "yst", DP.tz, +900},
{ "i", DP.tz, +900},
{ "ahst", DP.tz, +1000},
{ "cat", DP.tz, +1000},
{ "hst", DP.tz, +1000},
{ "k", DP.tz, +1000},
{ "nt", DP.tz, +1100},
{ "l", DP.tz, +1100},
{ "idlw", DP.tz, +1200},
{ "m", DP.tz, +1200},
{ "cet", DP.tz, -100},
{ "fwt", DP.tz, -100},
{ "met", DP.tz, -100},
{ "mewt", DP.tz, -100},
{ "swt", DP.tz, -100},
{ "n", DP.tz, -100},
{ "eet", DP.tz, -200},
{ "o", DP.tz, -200},
{ "bt", DP.tz, -300},
{ "p", DP.tz, -300},
{ "zp4", DP.tz, -400},
{ "q", DP.tz, -400},
{ "zp5", DP.tz, -500},
{ "r", DP.tz, -500},
{ "zp6", DP.tz, -600},
{ "s", DP.tz, -600},
{ "wast", DP.tz, -700},
{ "t", DP.tz, -700},
{ "cct", DP.tz, -800},
{ "u", DP.tz, -800},
{ "jst", DP.tz, -900},
{ "v", DP.tz, -900},
{ "east", DP.tz, -1000},
{ "gst", DP.tz, -1000},
{ "w", DP.tz, -1000},
{ "x", DP.tz, -1100},
{ "idle", DP.tz, -1200},
{ "nzst", DP.tz, -1200},
{ "nzt", DP.tz, -1200},
{ "y", DP.tz, -1200},
{ "bst", DP.dsttz, 000},
{ "adt", DP.dsttz, +400},
{ "edt", DP.dsttz, +500},
{ "cdt", DP.dsttz, +600},
{ "mdt", DP.dsttz, +700},
{ "pdt", DP.dsttz, +800},
{ "ydt", DP.dsttz, +900},
{ "hdt", DP.dsttz, +1000},
{ "mest", DP.dsttz, -100},
{ "mesz", DP.dsttz, -100},
{ "sst", DP.dsttz, -100},
{ "fst", DP.dsttz, -100},
{ "wadt", DP.dsttz, -700},
{ "eadt", DP.dsttz, -1000},
{ "nzdt", DP.dsttz, -1200},
{ "dst", DP.dst, 0},
];
//message(DTEXT("DateParse::classify('%s')\n"), buf);
// Do a linear search. Yes, it would be faster with a binary
// one.
for (uint i = 0; i < dateidtab.length; i++)
{
if (cmp(dateidtab[i].name, buf) == 0)
{
number = dateidtab[i].value;
return dateidtab[i].tok;
}
}
return DP.err;
}
int parseString(string s)
{
int n1;
int dp;
int sisave;
int result;
//message(DTEXT("DateParse::parseString('%ls')\n"), s);
this.s = s;
si = 0;
dp = nextToken();
for (;;)
{
//message(DTEXT("\tdp = %d\n"), dp);
switch (dp)
{
case DP.end:
result = 1;
Lret:
return result;
case DP.err:
case_error:
//message(DTEXT("\terror\n"));
default:
result = 0;
goto Lret;
case DP.minus:
break; // ignore spurious '-'
case DP.weekday:
weekday = number;
break;
case DP.month: // month day, [year]
month = cast(Month)(number);
dp = nextToken();
if (dp == DP.number)
{
day = number;
sisave = si;
dp = nextToken();
if (dp == DP.number)
{
n1 = number;
dp = nextToken();
if (dp == DP.colon)
{ // back up, not a year
si = sisave;
}
else
{ year = n1;
continue;
}
break;
}
}
continue;
case DP.number:
n1 = number;
dp = nextToken();
switch (dp)
{
case DP.end:
year = n1;
break;
case DP.minus:
case DP.slash: // n1/ ? ? ?
dp = parseCalendarDate(n1);
if (dp == DP.err)
goto case_error;
break;
case DP.colon: // hh:mm [:ss] [am | pm]
dp = parseTimeOfDay(n1);
if (dp == DP.err)
goto case_error;
break;
case DP.ampm:
hours = n1;
minutes = 0;
seconds = 0;
ampm = number;
break;
case DP.month:
day = n1;
month = cast(Month)(number);
dp = nextToken();
if (dp == DP.number)
{ // day month year
year = number;
dp = nextToken();
}
break;
default:
year = n1;
break;
}
continue;
}
dp = nextToken();
}
// @@@ bug in the compiler: this is never reachable
assert(0);
}
int parseCalendarDate(int n1)
{
int n2;
int n3;
int dp;
debug(dateparse) printf("DateParse.parseCalendarDate(%d)\n", n1);
dp = nextToken();
if (dp == DP.month) // day/month
{
day = n1;
month = cast(Month)(number);
dp = nextToken();
if (dp == DP.number)
{ // day/month year
year = number;
dp = nextToken();
}
else if (dp == DP.minus || dp == DP.slash)
{ // day/month/year
dp = nextToken();
if (dp != DP.number)
goto case_error;
year = number;
dp = nextToken();
}
return dp;
}
if (dp != DP.number)
goto case_error;
n2 = number;
//message(DTEXT("\tn2 = %d\n"), n2);
dp = nextToken();
if (dp == DP.minus || dp == DP.slash)
{
dp = nextToken();
if (dp != DP.number)
goto case_error;
n3 = number;
//message(DTEXT("\tn3 = %d\n"), n3);
dp = nextToken();
// case1: year/month/day
// case2: month/day/year
int case1, case2;
case1 = (n1 > 12 ||
(n2 >= 1 && n2 <= 12) &&
(n3 >= 1 && n3 <= 31));
case2 = ((n1 >= 1 && n1 <= 12) &&
(n2 >= 1 && n2 <= 31) ||
n3 > 31);
if (case1 == case2)
goto case_error;
if (case1)
{
year = n1;
month = cast(Month) (n2);
day = n3;
}
else
{
month = cast(Month) (n1);
day = n2;
year = n3;
}
}
else
{ // must be month/day
month = cast(Month) (n1);
day = n2;
}
return dp;
case_error:
return DP.err;
}
int parseTimeOfDay(int n1)
{
int dp;
int sign;
// 12am is midnight
// 12pm is noon
//message(DTEXT("DateParse::parseTimeOfDay(%d)\n"), n1);
hours = n1;
dp = nextToken();
if (dp != DP.number)
goto case_error;
minutes = number;
dp = nextToken();
if (dp == DP.colon)
{
dp = nextToken();
if (dp != DP.number)
goto case_error;
seconds = number;
dp = nextToken();
}
else
seconds = 0;
if (dp == DP.ampm)
{
ampm = number;
dp = nextToken();
}
else if (dp == DP.plus || dp == DP.minus)
{
Loffset:
sign = (dp == DP.minus) ? -1 : 1;
dp = nextToken();
if (dp != DP.number)
goto case_error;
tzcorrection = -sign * number;
dp = nextToken();
}
else if (dp == DP.tz)
{
tzcorrection = number;
dp = nextToken();
if (number == 0 && (dp == DP.plus || dp == DP.minus))
goto Loffset;
if (dp == DP.dst)
{ tzcorrection += 100;
dp = nextToken();
}
}
else if (dp == DP.dsttz)
{
tzcorrection = number;
dp = nextToken();
}
return dp;
case_error:
return DP.err;
}
}
unittest
{
DateParse dp;
SysTime d;
import std.stdio;
import std.conv;
dp.parse("March 10, 1959 12:00 -800", d);
assert(d.year == 1959);
assert(d.month == Month.mar);
assert(d.day == 10);
assert(d.hour == 12);
assert(d.minute == 0);
assert(d.second == 0);
assert(d.fracSec == FracSec.from!"msecs"(0));
//assert(d.timezone == new SimpleTimeZone(-800));
dp.parse("Tue Apr 02 02:04:57 GMT-0800 1996", d);
assert(d.year == 1996);
assert(d.month == 4);
assert(d.day == 2);
assert(d.hour == 2);
assert(d.minute == 4);
assert(d.second == 57);
assert(d.fracSec == FracSec.from!"msecs"(0));
//assert(d.timezone == new SimpleTimeZone(-800));
dp.parse("March 14, -1980 21:14:50", d);
assert(d.year == 1980);
assert(d.month == 3);
assert(d.day == 14);
assert(d.hour == 21);
assert(d.minute == 14);
assert(d.second == 50);
assert(d.fracSec == FracSec.from!"msecs"(0));
//assert(d.timezone == new SimpleTimeZone(int.min));
dp.parse("Tue Apr 02 02:04:57 1996", d);
assert(d.year == 1996);
assert(d.month == 4);
assert(d.day == 2);
assert(d.hour == 2);
assert(d.minute == 4);
assert(d.second == 57);
assert(d.fracSec == FracSec.from!"msecs"(0));
//assert(d.timezone == new SimpleTimeZone(int.min));
dp.parse("Tue, 02 Apr 1996 02:04:57 G.M.T.", d);
assert(d.year == 1996);
assert(d.month == 4);
assert(d.day == 2);
assert(d.hour == 2);
assert(d.minute == 4);
assert(d.second == 57);
assert(d.fracSec == FracSec.from!"msecs"(0));
//assert(d.timezone == new SimpleTimeZone(0));
dp.parse("December 31, 3000", d);
assert(d.year == 3000);
assert(d.month == 12);
assert(d.day == 31);
assert(d.hour == 0);
assert(d.minute == 0);
assert(d.second == 0);
assert(d.fracSec == FracSec.from!"msecs"(0));
//assert(d.timezone == new SimpleTimeZone(int.min));
dp.parse("Wed, 31 Dec 1969 16:00:00 GMT", d);
assert(d.year == 1969);
assert(d.month == 12);
assert(d.day == 31);
assert(d.hour == 16);
assert(d.minute == 0);
assert(d.second == 0);
assert(d.fracSec == FracSec.from!"msecs"(0));
//assert(d.timezone == new SimpleTimeZone(0));
dp.parse("1/1/1999 12:30 AM", d);
assert(d.year == 1999);
assert(d.month == 1);
assert(d.day == 1);
assert(d.hour == 0);
assert(d.minute == 30);
assert(d.second == 0);
assert(d.fracSec == FracSec.from!"msecs"(0));
//assert(d.timezone == new SimpleTimeZone(int.min));
dp.parse("Tue, 20 May 2003 15:38:58 +0530", d);
assert(d.year == 2003);
assert(d.month == 5);
assert(d.day == 20);
assert(d.hour == 15);
assert(d.minute == 38);
assert(d.second == 58);
assert(d.fracSec == FracSec.from!"msecs"(0));
//assert(d.timezone == new SimpleTimeZone(-530));
debug(dateparse) printf("year = %d, month = %d, day = %d\n%02d:%02d:%02d.%03d\ntzcorrection = %d\n",
d.year, d.month, d.day,
d.hour, d.minute, d.second, d.fracSec,
d.timezone);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.