How to make strtotime parse dates in Australian (i.e. UK) format: dd/mm/yyyy?
Asked Answered
T

6

11

I can't beleive I've never come across this one before.

Basically, I'm parsing the text in human-created text documents and one of the fields I need to parse is a date and time. Because I'm in Australia, dates are formatted like dd/mm/yyyy but strtotime only wants to parse it as a US formatted date. Also, exploding by / isn't going to work because, as I mentioned, these documents are hand-typed and some of them take the form of d M yy.

I've tried multiple combinations of setlocale but no matter what I try, the language is always set to US English.

I'm fairly sure setlocale is the key here, but I don't seem to be able to strike upon the right code. Tried these:

  • au
  • au-en
  • en_AU
  • australia
  • aus

Anything else I can try?

For clarity: I'm running on IIS with a Windows box.

Thanks so much :)

Iain

Example:

$mydatetime = strtotime("9/02/10 2.00PM");
echo date('j F Y H:i', $mydatetime);

Produces

2 September 2010 14:00

I want it to produce:

9 February 2010 14:00

My solution

I'm giving the tick to one of the answers here as it is a much easier-to-read solution to mine, but here's what I've come up with:

$DateTime = "9/02/10 2.00PM";
$USDateTime = preg_replace('%([0-3]?[0-9]{1})\s*?[\./ ]\s*?((?:1[0-2])|0?[0-9])\s*?[./ ]\s*?(\d{4}|\d{2})%', '${2}/${1}/${3}', $DateTime);  
echo date('j F Y H:i',strtotime($USDateTime));

Because I can't rely on users to be consistent with their date entry, I've made my regex a bit more complex:

  • 0 or 1 digit between 0 and 3
  • 1 digit between 0 and 9 -- yes this will match 37 as a valid date but I think the regex is already big enough!
  • Could be some whitespace
  • Delimiting character (a '.', a '/' or a ' ')
  • Could be some whitespace
  • Either:
    • A number between 10 and 12 OR
    • A number between 1 and 9 with an optional leading 0
  • Could be some whitespace
  • Delimiting character (a '.', a '/' or a ' ')
  • Could be some whitespace
  • Either:
    • A number 2 digits long OR
    • A number 4 digits long

Hopefully this will match most styles of date writing...

Tamarra answered 15/3, 2010 at 2:53 Comment(3)
Don't you want it to produce 9 February 2010 14:00? Or do you guys only have 11 months in the year?Anguish
Yes, noticed this and changed it. Coffee hasn't kicked in yet. :)Tamarra
This question might help you out #5598333Terraqueous
P
20

There's a quick fix that seems to force PHP's strtotime into using the UK date format. That is: to replace all of the '/' in the incoming string with a '-'.

Example:

date('Y-m-d', strtotime('01/04/2011'));

Would produce: 2011-01-04

date('Y-m-d', strtotime('01-04-2011'));

Would produce: 2011-04-01

You could use str_replace to achieve this.

Example:

str_replace('/', '-', '01/04/2011');

EDIT:

As stated in the comments, this works because PHP interprets slashes as American and dots/dashes as European.

I've used this trick extensively and had no problems so far.

If anyone has any good reasons not to use this, please comment.

Perfectionism answered 11/4, 2011 at 10:13 Comment(7)
solidgone.org/… describes the "reasoning" behind this. Hyphens suggest ISO date format, dots suggest European, and slashes are American. Next question: but why is THAT?Austral
Question was about different order of month and day in the date not about dots and dashes.Ipa
@VladimirSerykh Read the question again. It clearly states "dates are formatted like dd/mm/yyyy" meaning the questioner's dates were formatted using slashes but the order was wrong. I suggested to convert those slashes into dashes which corrects the order.Perfectionism
Manipulating strings is not the best solution to get correct date. It's conceptually wrong. You'd better use date_parse_from_format ($format , $date ) or DateTime::createFromFormat($format , $date) I think.Ipa
@ChrisHarrison the first example produces 1970-01-01 for PHPv5.6Lovelace
@Lovelace I've just tested it on PHP 5.6.29 and it produces the stated result.Perfectionism
@Lovelace sandbox.onlinephpfunctions.com/code/…Perfectionism
I
3

The problem is that strtotime doesn't take a format argument. What about strptime?

Invocate answered 15/3, 2010 at 3:5 Comment(1)
Dang, would have loved to have tried this but I'm in a WIMP environment :(Tamarra
P
1

setlocale() sucks for exactly the reason you describe: You never know what you're going to get. Best to process the string manually.

Zend Framework's Zend_Date is one alternative promising more exact and consistent date handling. I don't have experience with it myself yet, just beginning to work with it, but so far, I like it.

Phoenix answered 15/3, 2010 at 3:2 Comment(1)
Yeah, that's kind of what I did in the end. Well, actually I just reformatted the string so UK dates become US dates and so far it has worked on all my test cases - yay!Tamarra
B
1

Ah, the old problem us lucky Australians get.

What I've done in the past is something like this

public static function getTime($str) { // 3/12/2008

       preg_match_all('/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/', $str, $matches);

       return (isset($matches[0][0])) ? strtotime($matches[3][0] . '-' . $matches[2][0] . '-' . $matches[1][0]) : NULL;

    }

Though this relies on dates in this format dd/mm/yyyy.

You can probably use another regex or so to convert from d M yy or use a modified one. I don't know if this would be correct but it may be a start:

/^(\d{1,2})(?:\/|\s)(\d{1,2})(?:\/|\s)(\d{2,4})$/

Behlke answered 15/3, 2010 at 3:20 Comment(4)
Thanks for the effort mate. Not sure why you got down-voted though. I guess my only criticism of this method is that it's very specific given the nature of possible inputs - but that's my problem, I'm sure this is some generic code you have lying around in your toolbox for problems with more consistant inputs. My answer is similar to yours in that I try to match using regex, but rather than return a formatted match, I just convert my Australian string into US format.Tamarra
Also, to the person that downvoted... I'd be interested to know why? Not trolling, I'd just be interested to know - I might learn something :)Tamarra
Yeah, it does rely on one format, except it came from a helper function I wrote that only is given the one format. In the example above, I'm not returning a formatted match, but a unix time from the date once passed as an ISO standard (the same MySQL uses, and PHP can easily use in strtotime()). I'm not sure of the downvote, maybe it's cause I used a regex when some people would of preferred explode().Behlke
Which is silly because when you're dealing with user input, you can't rely on there being a consistent delimeter to explode on... hence; regex!Tamarra
B
0
$date = date_create_from_format('d/m/Y', $date_string);
$unix_timestamp = $date->getTimestamp();

or

$date = DateTime::createFromFormat('d/m/Y', $date_string);
$unix_timestamp = $date->getTimestamp();
Bend answered 24/7, 2015 at 13:34 Comment(0)
H
0

For future readers, try the following for parsing dates from unstrusted user input, for which strptime() is too rigid. It outputs ISO date strings which you can pass to new Date(), strtotime(), etc., or false for invalid dates, or null for no usable input. You'd have to adapt it to process times. It also accepts 2-digit year and ISO date input.

function parseUserDate($input, $allow_zero = false) {
    if (!is_string($input)) {
        return $input === null ? null : false;
    }
    $input = preg_split('/\\D+/', $input, null, PREG_SPLIT_NO_EMPTY);
    if (!$input) {
        return null;# no date
    }
    if (count($input) == 3) {
        list($od, , $oy) = $input;
        if (strlen($od) > 2 && strlen($oy) < 3) {# assume ISO
            $oy = $od;
            $input = array_reverse($input);
        }
        $intify = function($dig) {
            return (int) ltrim($dig, '0');# prevent octals
        };
        list($d, $m, $y) = array_map($intify, $input);
        if (strlen($oy) < 3) {
            $current_year = date('Y');
            $current_year_2 = $intify(substr($current_year, -2));
            $diff = $y - $current_year_2;
            if ($diff > 50) {
                $diff -= 100;
            }
            elseif ($diff < -50) {
                $diff += 100;
            }
            $y = $diff + $current_year;
        }
        if (
            ($allow_zero && $d == 0 && $m == 0 && $y == 0)
            || checkdate($m, $d, $y)# murrican function
        ) {
            return sprintf('%04d-%02d-%02d', $y, $m, $d);
        }
    }
    return false;# invalid date
}
Hawkie answered 9/2, 2018 at 2:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.