Odyssey
Public Member Functions | Protected Member Functions | Protected Attributes | List of all members
DateConvert Class Reference

Public Member Functions

 __construct ()
 
 ConvertDateFromFormat ($date, $requested_format='', $century=0)
 

Protected Member Functions

 SetRequestedFormat ($format)
 
 IsValidRequestedFormat ($format='')
 
 SetRequestedCentury ($century=0)
 
 FindValidFormat ($date)
 
 IsValidDate ($format, $date)
 
 ConfirmFourDigitYear ($format, $date)
 
 SplitDateOnDelimiter ($format, $date)
 
 DetectDelimiter ($format)
 
 SetFourDigitYear ($year=0)
 
 ValidateYearInput ($year=0)
 
 SetYear ($year=0)
 
 CreateRequestedDate ($current_format, $date)
 

Protected Attributes

 $requested_format = 'Y-m-d'
 
 $century = 0
 
 $formats
 

Detailed Description

Reference this class to validate and update the most used date string formats into (almost) any format compatible with PHP DateTime. This class came to life from a request to allow two digit year inputs for birthdate. Two digits . . . in what century? Think about that a second . . . combined with the IBM solution and some input options, this class proposes a solid reusable solution.

Usage: $dc = new DateConvert(); echo $dc->convertDateFromFormat([string $date], [optional string $requested_format], [optional int century);

Supports slash, dash, or dot separators in American and European formats. $requested_format will default to Y-m-d SQL format if omitted. Four digit year will be used in any case (won't be overridden.) For two digit year input, century will override the "40 year algo" used by IBM, see comments at setYear().

Definition at line 21 of file DateConvert.i.

Constructor & Destructor Documentation

◆ __construct()

DateConvert::__construct ( )

DateConvert constructor. Nothing to do

Returns
void

Definition at line 69 of file DateConvert.i.

69  {
70 
71  }

Member Function Documentation

◆ ConfirmFourDigitYear()

DateConvert::ConfirmFourDigitYear (   $format,
  $date 
)
protected

Make sure our years are expressed in 4 digits, and hopefully in the right century. We're going to get a little messy now, like rubbing two sticks together messy. The DateTime class assumes we aren't going to do something dumb like ask it for a two digit year date. If asked, a string of 10/13/60 will return 10/13/2060. So what we have to do is deconstruct the date string old school and determine which century we need it in using the IBM algo. If that doesn't suffice, we also have the century param that will override the century calculation if needed.

Parameters
string$format
string$date
Returns
string
Exceptions
Exception

Definition at line 186 of file DateConvert.i.

186  {
187 
188  $date_arr = $this->SplitDateOnDelimiter($format, $date);
189 
190  if (strlen($date_arr['y']) >= 4) {
191  return $date;
192  }
193 
194  $y = $this->SetFourDigitYear($date_arr['y']);
195  // Breaks DateTime format if it's still a y after changing to 4 digits.
196  $format = preg_replace('/y/', 'Y', $format);
197  $obj = new DateTime;
198 
199  $obj->setDate($y, $date_arr['m'], $date_arr['d']);
200 
201  return $obj->format($format);
202  }
SetFourDigitYear($year=0)
Definition: DateConvert.i:264
SplitDateOnDelimiter($format, $date)
Definition: DateConvert.i:214

◆ ConvertDateFromFormat()

DateConvert::ConvertDateFromFormat (   $date,
  $requested_format = '',
  $century = 0 
)

Entry point, see calling consumers/clients/apps for examples.

Parameters
string$date
string$requested_format
int$century,overrides40 year rule documented at setYear() below.
Returns
string
Exceptions
Exception

Definition at line 81 of file DateConvert.i.

81  {
82 
83  return $this
84  ->SetRequestedFormat($requested_format)
85  ->SetRequestedCentury($century)
86  ->FindValidFormat($date);
87  }

◆ CreateRequestedDate()

DateConvert::CreateRequestedDate (   $current_format,
  $date 
)
protected

Leverage the DateTime object and create a date string formatted as requested from original.

Parameters
string$current_format
string$date
Returns
bool

Definition at line 340 of file DateConvert.i.

340  {
341 
342  $obj = DateTime::createFromFormat($current_format, $date);
343  return $obj->format($this->requested_format);
344  }

◆ DetectDelimiter()

DateConvert::DetectDelimiter (   $format)
protected

Extract the delimiter from the format string we found from the input date.

Parameters
$format
Returns
string|null

Definition at line 243 of file DateConvert.i.

243  {
244 
245  $supported = ['/', '.', '-'];
246 
247  foreach ($supported as $delim) {
248  if (preg_match("|$delim|", $format)) {
249  return $delim;
250  }
251  }
252 
253  return null;
254  }

◆ FindValidFormat()

DateConvert::FindValidFormat (   $date)
protected

Look up from a list of date formats and see if the input date string matches on any of them. This kind of sucks, should be built dynamically but it works (and allows us control over the date formats we want to support.)

Parameters
string$date
Returns
string|false
Exceptions
Exception

Definition at line 142 of file DateConvert.i.

142  {
143 
144  foreach ($this->formats as $fmt) {
145 
146  if ($this->IsValidDate($fmt, $date)) {
147 
148  // Ensure we have a 4 digit year, even if two is input.
149  $date = $this->ConfirmFourDigitYear($fmt, $date);
150  // Once it's 4 digit, it will break DateTime if the year format is still 'y'
151  $fmt = preg_replace('/y/', 'Y', $fmt);
152 
153  return $this->CreateRequestedDate($fmt, $date);
154  }
155  }
156 
157  return false;
158  }
ConfirmFourDigitYear($format, $date)
Definition: DateConvert.i:186
IsValidDate($format, $date)
Definition: DateConvert.i:166
CreateRequestedDate($current_format, $date)
Definition: DateConvert.i:340

◆ IsValidDate()

DateConvert::IsValidDate (   $format,
  $date 
)
protected

Core date validation, leverage the DateTime object.

Parameters
string$format
string$date
Returns
bool

Definition at line 166 of file DateConvert.i.

166  {
167 
168  $obj = DateTime::createFromFormat($format, $date);
169  return $obj && $obj->format($format) == $date;
170  }

◆ IsValidRequestedFormat()

DateConvert::IsValidRequestedFormat (   $format = '')
protected

If there is an optional requested output format, validate it and return a message if it's not in the cool kids' club (formats property array.)

Parameters
string$format
Returns
bool

Definition at line 111 of file DateConvert.i.

111  {
112 
113  if (empty($format) || ! in_array($format, $this->formats)) {
114  return false;
115  }
116 
117  return true;
118  }

◆ SetFourDigitYear()

DateConvert::SetFourDigitYear (   $year = 0)
protected

Set a four digit year from a two digit year. If you're parsing a four digit year, you shouldn't be here, it will have already returned the date, but including the guard pattern anyway. :-)

Parameters
int$year
Returns
int|string
Exceptions
Exception

Definition at line 264 of file DateConvert.i.

264  {
265 
266  $this->ValidateYearInput($year);
267 
268  if (strlen($year) >= 4) {
269  return $year;
270  }
271 
272  return $this->SetYear(intval($year));
273  }
ValidateYearInput($year=0)
Definition: DateConvert.i:281
SetYear($year=0)
Definition: DateConvert.i:317

◆ SetRequestedCentury()

DateConvert::SetRequestedCentury (   $century = 0)
protected

Set the optional requested century to override the 40 year rule documented at setYear() below.

Parameters
intcentury
Returns
$this

Definition at line 125 of file DateConvert.i.

125  {
126 
127  if (intval($century) > 0) {
128  $this->century = intval($century);
129  }
130 
131  return $this;
132  }

◆ SetRequestedFormat()

DateConvert::SetRequestedFormat (   $format)
protected

Set the requested format into the class property.

Parameters
string$format
Returns
$this

Definition at line 94 of file DateConvert.i.

94  {
95 
96  if (! $this->IsValidRequestedFormat($format)) {
97  return $this;
98  }
99 
100  $this->requested_format = $format;
101 
102  return $this;
103  }
IsValidRequestedFormat($format='')
Definition: DateConvert.i:111

◆ SetYear()

DateConvert::SetYear (   $year = 0)
protected

Set a four digit year from a two digit year based on an optional provided century or year alone. From IBM:

"When you are moving or comparing 2-digit years to 4-digit years or centuries, or comparing 4-digit years or centuries to 2-digit years, ILE COBOL first converts the 2-digit year using a windowing algorithm. The default windowing algorithm used is as follows:

"If a 2-digit year is moved to a 4-digit year, the century (1st 2 digits of the year) are chosen as follows: If the 2-digit year is greater than or equal to 40, the century used is

  1. In other words, 19 becomes the first 2 digits of the 4-digit year.

If the 2-digit year is less than 40, the century used is 2000. In other words, 20 becomes the first 2 digits of the 4-digit year."

https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_72/rzase/yrconv.htm

In our case we have added the optional param $century [int > 1000] so it can be customized by consumers (consumers being the apps calling this class.)

Parameters
int$year
Returns
int

Definition at line 317 of file DateConvert.i.

317  {
318 
319  // PHP will automagically cast these, but in the interest of specificity . . .
320  $year = intval($year);
321  $century = intval($this->century);
322 
323  if ($century < 1) {
324  $century = ($year >= 40)? 1900 : 2000;
325  }
326 
327  // Probably fine but guard against typos or other abuse. We want
328  // a round century number. Nuke a sloppy century.
329  $century = intdiv($century, 100) * 100;
330 
331  return $year += $century;
332  }

◆ SplitDateOnDelimiter()

DateConvert::SplitDateOnDelimiter (   $format,
  $date 
)
protected

The rubbing two sticks together part. Split up a date string and create an array associated with keys m, d, y. Why are we doing this? To ensure a two digit year gets properly assigned as a 4 digit year. DateTime alone would always return 2060 if provided the year "60" which in most cases means 1960.

Parameters
string$format
string$date
Returns
array

Definition at line 214 of file DateConvert.i.

214  {
215 
216  $arr = [];
217  $delim = $this->DetectDelimiter($format);
218 
219  $keys = explode($delim, $format);
220  $vals = explode($delim, $date);
221 
222  for ($i = 0; $i < count($keys); $i++) {
223 
224  if (in_array(strtolower($keys[$i]), ['m', 'n'])) {
225  $arr['m'] = $vals[$i];
226  }
227  if (in_array(strtolower($keys[$i]), ['d', 'j'])) {
228  $arr['d'] = $vals[$i];
229  }
230  if (strtolower($keys[$i]) == 'y') {
231  $arr['y'] = $vals[$i];
232  }
233  }
234 
235  return $arr;
236  }
DetectDelimiter($format)
Definition: DateConvert.i:243

◆ ValidateYearInput()

DateConvert::ValidateYearInput (   $year = 0)
protected

Validate year

Parameters
int$year
Returns
string|null
Exceptions
Exception

Definition at line 281 of file DateConvert.i.

281  {
282 
283  if (intval($year) < 0) {
284  throw new Exception("Please enter a numeric year." . PHP_EOL);
285  }
286 
287  if (intval($year) > 99) {
288  throw new Exception("Please enter a two digit year." . PHP_EOL);
289  }
290 
291  return null;
292  }

Member Data Documentation

◆ $formats

DateConvert::$formats
protected
Initial value:
= [
'm/d/Y', 'n/d/Y', 'n/j/Y', 'm/j/Y',
'm/d/y', 'n/d/y', 'n/j/y', 'm/j/y',
'm.d.Y', 'n.d.Y', 'n.j.Y', 'm.j.Y',
'm.d.y', 'n.d.y', 'n.j.y', 'm.j.y',
'm-d-Y', 'n-d-Y', 'n-j-Y', 'm-j-Y',
'm-d-y', 'n-d-y', 'n-j-y', 'm-j-y',
'Y/m/d', 'Y/n/d', 'Y/n/j', 'Y/m/j',
'y/m/d', 'y/n/d', 'y/n/j', 'y/m/j',
'Y.m.d', 'Y.n.d', 'Y.n.j', 'Y.m.j',
'y.m.d', 'y.n.d', 'y.n.j', 'y.m.j',
'm.d.Y', 'n.d.Y', 'm.j.Y', 'n.j.Y',
'm.d.y', 'n.d.y', 'm.j.y', 'n.j.y',
'Y-m-d', 'Y-n-d', 'Y-n-j', 'Y-m-j',
'y-m-d', 'y-n-d', 'y-n-j', 'y-m-j',
]

Definition at line 33 of file DateConvert.i.


The documentation for this class was generated from the following file: