[SalesForce] Datetime won’t save using correct time zone

Ok so I've been trying to sort this out all day and I've not made a single bit of progress.

I have a set up a site on Salesforce as a web service endpoint. A client's system sends an HTTP request with a few bits of information as GET parameters and then our salesforce updates a record.

I noticed that the date was saving an hour later than it was and I cannot get it to save correctly no matter what I have tried.

The Custom_Object__c has two datetime fields, one which is set to Datetime.now() when the record is created, we'll call that Datetime_1__c. This one saves correctly. The other field, Datetime_2__c is set when the HTTP request comes through with a date string in the GET parameters. The date string is +01:00 time zone, and requires a bit of reformatting before converting.

The apex code is below:

 String dtStr = g_datetime.substring(0, 19).replaceAll('T', ' ') + '+01:00';
 System.debug('Date/Time string to save:' + dtStr);
 Datetime dt = Datetime.valueOf(dtStr);
 System.debug('Date/Time to save:' + String.valueOf(dt));
 relatedRecord.Receipt_Received_At__c = dt;

The debug log shows these two entries:

13:58:07.038 (38233077)|USER_DEBUG|[49]|DEBUG|Date/Time string to save:2014-06-19 14:44:00+01:00
13:58:07.038 (38416001)|USER_DEBUG|[51]|DEBUG|Date/Time to save:2014-06-19 14:44:00

But when I go on the record detail page, the time shows as 15:44. My user time zone is BST (+01:00) and so is the company default, but when I set these to any other time zones it STILL puts the date an hour ahead.

What do I need to add or remove from my code to make this appear correctly?

Best Answer

The DateTime.valueOf(string) method has the following Usage note in the docs:

The specified string should use the standard date format “yyyy-MM-dd HH:mm:ss” in the local time zone.

It is ignoring the +1 UTC offset.

If I run the following as anonymous Apex:

String dtStr = '2014-06-19 14:44:00+01:00';
System.debug('Date/Time string to save:' + dtStr);
Datetime dt = Datetime.valueOf(dtStr);
System.debug('Date/Time to save:' + String.valueOf(dt));
System.debug('hour(): ' + dt.hour());
System.debug('hourGmt(): ' + dt.hourGmt());

I get:

12:33:15.258 (258899135)|USER_DEBUG|[4]|DEBUG|Date/Time string to save:2014-06-19 14:44:00+01:00
12:33:15.259 (259067845)|USER_DEBUG|[6]|DEBUG|Date/Time to save:2014-06-19 14:44:00
12:33:15.259 (259158685)|USER_DEBUG|[8]|DEBUG|hour(): 14
12:33:15.259 (259232093)|USER_DEBUG|[9]|DEBUG|hourGmt(): 21

In this particular instance my Salesforce users timezone is set to (GMT-07:00) Pacific Daylight Time (America/Tijuana).

As an alternative, try formatting the input string in the IS0 8601 format. You can then convert it to a UTC DateTime with:

String dtStr = '2014-06-19T14:44:00.000Z';
System.debug('Date/Time string to save:' + dtStr);
Datetime dt = (DateTime)json.deserialize('"' + dtStr + '"', datetime.class);
System.debug('Date/Time to save:' + String.valueOf(dt));
System.debug('hour(): ' + dt.hour());
System.debug('hourGmt(): ' + dt.hourGmt());

Which gives:

12:43:52.272 (272399372)|USER_DEBUG|[4]|DEBUG|Date/Time string to save:2014-06-19T14:44:00.000Z
12:43:52.273 (273942997)|USER_DEBUG|[7]|DEBUG|Date/Time to save:2014-06-19 07:44:00
12:43:52.274 (274047548)|USER_DEBUG|[9]|DEBUG|hour(): 7
12:43:52.274 (274124974)|USER_DEBUG|[10]|DEBUG|hourGmt(): 7

You can encode the timezone offset in the input date time using:

String dtStr = '2014-06-19T14:44:00.000+01:00';

If you can, transfer the value in UTC. It will make your task easier. I.e. what happens if/when BST changes due to daylight savings time.