1

In my ASP.Net app, I am receiving a date string from PayPal and when I try to convert it to UTC date it always fails. I have tried this conversion in LINQPad tool but I always get that the date string could not be successfully parsed.

The date string that my ASP.Net page receives from PayPal is like this: 08:45:29 May 25, 2016 PDT

Question: How can I successfully convert the PayPal received date to UTC date in C#? The code that I have tried so far is given below.

Code in LINQPad for conversion of PayPal date to UTC date

string payPalDateString = "08:45:29 May 25, 2016 PDT";
DateTime date = DateTime.Now;

if( DateTime.TryParse(payPalDateString, out date) == true) {
      "payPalDateString was successfully parsed".Dump();
      DateTime dateUTC =  date.ToUniversalTime();
      dateUTC.Dump("payPalDateString in UTC is as below");
  } else {
      payPalDateString.Dump("payPalDateString was NOT successfully parsed");
 }

PayPal documentation says the following about the date it sends.

Time/Date stamp generated by PayPal, in the following format: HH:MM:SS DD Mmm YY, YYYY PST

The above date format mentioned in PayPal documentation should actually have been: HH:mm:ss MMM dd, yyyy PST

Sunil
  • 20,653
  • 28
  • 112
  • 197
  • This gives a solution/work around http://stackoverflow.com/questions/6941839/how-to-convert-pdt-time-string-to-datetime – Tim Freese May 25 '16 at 20:34
  • Is your data _always_ in `PDT`? – D Stanley May 25 '16 at 20:41
  • @DStanley, At least for now, but I am not sure if it will always be like this. PayPal is based in California so may be for US based requests it sends out this date. But if request comes from Asia then I am not sure how it will be formatted. – Sunil May 25 '16 at 20:44
  • Well, you might add a check for that before hard-coding it into your parse format as others are suggesting. Time zone abbreviations are not unique, so there's not a 100% reliable way to convert them to an offset to get UTC. – D Stanley May 25 '16 at 20:50
  • @DStanley, I think the modified answer provides a flexible way of parsing the time zone part of the date string. – Sunil May 25 '16 at 21:11
  • @Sunil - you may need to edit your question to include appropriate format specifiers; `M` and `m` are two separate specifiers represent completely different parts of `DateTime` – techspider May 25 '16 at 21:12
  • @Stanley, I quoted the PayPal documentation for the date string that I receive. I think their documentation is not correct. – Sunil May 25 '16 at 21:15
  • The documentation you posted doesn't match the sample data - it has the day and month reversed and is in `PDT` instead of `PST`. Certainly the documentation could be wrong but it doesn't inspire confidence that the date will _always_ be in one time zone. – D Stanley May 25 '16 at 21:16
  • @Stanley, I looked this up at https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNandPDTVariables/, and then do a find in yopur browswe for `subscr_date`, but I will mention this fact in my OP. – Sunil May 25 '16 at 21:18
  • It's a shame that a service as technically advanced as PayPal doesn't handle times more appropriately. The documentation uses goofy formats (`HH:MM:SS DD Mmm YY, YYYY PST`) and is inconsistent between PST and PDT. – D Stanley May 25 '16 at 21:21
  • @Stanley, Yes, PayPal is not good at this at all as I am discovering while trying to write payment code for a website selling subscriptions through PayPal. – Sunil May 25 '16 at 21:22

2 Answers2

3

Please refer to the below logic to parse the DateTime exactly the way it was coming from Paypal

DateTime newDate;
DateTime.TryParseExact("08:45:29 May 25, 2016 PDT", 
                       "HH:mm:ss MMM dd, yyyy PDT", 
                        CultureInfo.InvariantCulture, 
                        DateTimeStyles.None, out newDate);
var utcDateTime = newDate.ToUniversalTime();

I have added dotnetfiddle code


Since OP is not sure about the incoming time zone format to be in PDT, he was suggested to dynamically substitute time zone in the conversion format. He came up with this logic.

String.Format("HH:mm:ss MMM dd, yyyy {0}", 
               payPalDateString.Substring(payPalDateString.Length - 3))

Please note that there could be a number of other alternative ways to substitute the time zone at run time. This solution doesn't cover any error handling like if the input string doesn't come up with any time zone!!

techspider
  • 3,370
  • 13
  • 37
  • 61
  • Yes, that worked. Can I replace PDT in format string with some format specifier, since I may get the PayPal date in another time zone if I was sending a request from Asia or Australia? – Sunil May 25 '16 at 20:49
  • i can't think of any specifier to replace timezone – techspider May 25 '16 at 20:52
  • This will only work as long as you are sure that you are dealing explicitly with this format. If you expect of dynamic time zones, I believe you end up writing a little bit of logic on top of this code to just extract timezone from your incoming string and replace timezone in the target – techspider May 25 '16 at 21:02
  • So, it's best to get the last three characters in the PayPal date string and then set the second parameter to `string.Format("HH:mm:ss MMM dd, yyyy {0}", last#Chars)`. – Sunil May 25 '16 at 21:04
  • that's exactly I perceived :) – techspider May 25 '16 at 21:05
  • I tried `string.Format("HH:mm:ss MMM dd, yyyy {0}", payPalDateString.Substring(payPalDateString.Length - 3))` for second parameter and it works. May be you can update your answer with this. Thanks for your help. – Sunil May 25 '16 at 21:06
3

Is you want to be able to support, say PST and PDT in your strings, I would create some sort of mapping from the abbreviations to an offset. Time zone abbreviations are not unique so you will need to include only the ones that are relevant to your data (and hope that PayPal doesn't use non-unique offsets like CST). You could start with what you know and add more as necessary:

Dictionary<string, string> TimeZones = new Dictionary<string, string> 
{
   {"PST", "-08:00"},
   {"PDT", "-07:00"},
   // more as needed 
};

string s = "08:45:29 May 25, 2016 PDT";

StringBuilder sb = new StringBuilder(s);

foreach(var kvp in TimeZones)
    sb = sb.Replace(kvp.Key,kvp.Value);

s = sb.ToString();

DateTime dt;
bool success = DateTime.TryParseExact(s, 
                                      "HH:mm:ss MMM dd, yyyy zzz",
                                      CultureInfo.InvariantCulture,
                                      DateTimeStyles.AdjustToUniversal,
                                      out dt);
D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • Thanks.That makes the solution highly configurable. I could just store the TimeZones as a string in database or as a web config appSetting. – Sunil May 25 '16 at 21:34