XeroAPI / xero-php-oauth2

Xero PHP SDK for oAuth 2 generated from Xero API OpenAPI Spec 3.0
MIT License
91 stars 65 forks source link

Correct timezone conversion. #220

Closed bradleybensmith closed 3 years ago

bradleybensmith commented 3 years ago

The convertStringToDate function doesn't handle the timezone difference. For example, for April 1, 2021, you incorrectly get March 31, 2021.

>>> $seconds = 1617235200000 / 1000
=> 1617235200
>>> $dateString = date("d-m-Y", $seconds)
=> "31-03-2021"
>>> $dateFormat = new \DateTime($dateString)
=> DateTime @1617163200 {#3743
     date: 2021-03-31 00:00:00.0 America/New_York (-04:00),
   }

This change corrects the behavior:

>>> $seconds = 1617235200000 / 1000
=> 1617235200
>>> $dateString = (new \DateTime('@' . $seconds))->format('d-m-Y')
=> "01-04-2021"
>>> $dateFormat = new \DateTime($dateString)
=> DateTime @1617249600 {#3744
     date: 2021-04-01 00:00:00.0 America/New_York (-04:00),
   }
SidneyAllen commented 3 years ago

@bradleybensmith - Thanks for the suggestions. I'd like to get more details around this requested change.

I notice your example response has New York Timezone. Xero's Dates and DateTimes are stored as UTC. Not sure if this is the problem you are attempting to solve.

We use the method convertStringToDate for some of our APIs which store date and date-time objects in MSJSON format.

I tested out the StringToDate conversion with the following code snippet to create an invoice with an Invoice Date of 4/1/2021.

$dateValue = new DateTime('2021-04-01');
$dueDateValue = new DateTime('2021-04-08');
$summarizeErrors = false;
$unitdp = 2;
$contact = new XeroAPI\XeroPHP\Models\Accounting\Contact;
$contact->setContactID($contactID);

$lineItem = new XeroAPI\XeroPHP\Models\Accounting\LineItem;
$lineItem->setDescription('HelloWorld');
$lineItem->setQuantity(1.0);
$lineItem->setUnitAmount(20.0);
$lineItem->setAccountCode('400');
$lineItems = [];
array_push($lineItems, $lineItem);

$invoice = new XeroAPI\XeroPHP\Models\Accounting\Invoice;
$invoice->setType(XeroAPI\XeroPHP\Models\Accounting\Invoice::TYPE_ACCREC);
$invoice->setContact($contact);
$invoice->setDate($dateValue);
$invoice->setDueDate($dueDateValue);
$invoice->setLineItems($lineItems);
$invoice->setReference('Website Design 123');
$invoice->setStatus(XeroAPI\XeroPHP\Models\Accounting\Invoice::STATUS_DRAFT);

$invoices = new XeroAPI\XeroPHP\Models\Accounting\Invoices;
$arr_invoices = [];
array_push($arr_invoices, $invoice);
$invoices->setInvoices($arr_invoices);

try {
    $result = $apiInstance->createInvoices($xeroTenantId, $invoices, $summarizeErrors, $unitdp);
    $message = $result->getInvoices()[0]->getDate() . " AND " . $result->getInvoices()[0]->getDateAsDate()->format('m/d/Y') . " END"; 
            echo($message );
} catch (Exception $e) {
    echo 'Exception when calling AccountingApi->createInvoices: ', $e->getMessage(), PHP_EOL;
}

The JSON API Response is

{
  "Id": "3a00ffb1-ae01-4945-826b-d610413bc437",
  "Status": "OK",
  "ProviderName": “TestApp”,
  "DateTimeUTC": "/Date(1618337050341)/",
  "Invoices": [
    {
      "Type": "ACCREC",
      "InvoiceID": "cab7422c-eee0-4d84-852d-5e9010515fad",
      "InvoiceNumber": "INV-0513",
      "Reference": "Website Design 123",
      "Prepayments": [],
      "Overpayments": [],
      "AmountDue": 21.9,
      "AmountPaid": 0,
      "SentToContact": false,
      "CurrencyRate": 1,
      "IsDiscounted": false,
      "HasErrors": false,
      "Contact": {
        "ContactID": "1270bf7c-5d18-473a-9231-1e36c4bd33ed",
        … TRUNCATED …
      },
      "DateString": "2021-04-01T00:00:00",
      "Date": "/Date(1617235200000+0000)/",
      "DueDateString": "2021-04-08T00:00:00",
      "DueDate": "/Date(1617840000000+0000)/",
      "BrandingThemeID": "dabc7637-62c1-4941-8a6e-ee44fa5090e7",
      "Status": "DRAFT",
      "LineAmountTypes": "Exclusive",
      "LineItems": [
        {
          "Description": "HelloWorld",
          "UnitAmount": 20,
          "TaxType": "TAX001",
          "TaxAmount": 1.9,
          "LineAmount": 20,
          "AccountCode": "400",
          "Tracking": [],
          "Quantity": 1,
          "LineItemID": "28707f7e-ca51-488f-9e01-0c4c73a8c5b4",
          "ValidationErrors": []
        }
      ],
      "SubTotal": 20,
      "TotalTax": 1.9,
      "Total": 21.9,
      "UpdatedDateUTC": "/Date(1618337050293+0000)/",
      "CurrencyCode": "USD",
      "StatusAttributeString": "OK"
    }
  ]
}

The $message variable is ... $message = $result->getInvoices()[0]->getDate() . " AND " . $result->getInvoices()[0]->getDateAsDate()->format('m/d/Y') . " END"; output is ... /Date(1617235200000+0000)/ AND 04/01/2021 END

Taking the seconds "1617235200000" and using this converter website, we see that the UTC time is correct April 1, 2021 00:00:000 https://currentmillis.com/

SidneyAllen commented 3 years ago

@bradleybensmith - didn't hear back from you regarding my explanation around timezones and UTC dates so going to close this PR.