Repository: boto/botocore (python)

Issue:
RefreshableCredentials serializing without timezone info
Hey boto folks -

I'm currently implementing my own CredentialsProvider that provides RefreshableCredentials. I'm having issues with code that has been working, but recently broke. I have to wonder if either one of my dependencies got bumped (although I can't prove it) or because we just entered daylight savings.

Basically, I'm always hitting this condition: 

> Credentials were refreshed, but the refreshed credentials are still expired.

To create my meta-data, I'm using the same(?) code used in botocore:
```
from datetime import datetime, timedelta
from dateutil.tz import tzlocal
expiration = datetime.now(tzlocal()) + timedelta(minutes=5)
expiration.strftime('%Y-%m-%dT%H:%M:%SZ')
```
> '2016-04-07T10:26:04Z'

This doesn't encode my local time zone information, as %Z is needed.

I can demonstrate this inconsistency using some of the internal boto funcs
```
from botocore.credentials import _local_now, _serialize_if_needed, _parse_if_needed
now = _local_now()
print now
serialized = _serialize_if_needed(now)
print serialized
deserialized = _parse_if_needed(serialized)
print deserialized
print now - deserialized
```
> 2016-04-07 10:24:12.336556-04:00
> 2016-04-07T10:24:12Z
> 2016-04-07 10:24:12+00:00
> 4:00:00.336556

I can fix this issue on my end by encoding the timezone information in my format string `'%Y-%m-%dT%H:%M:%S%Z'`, but I'm convinced botocore should do the same in its default time serialization code

TL;DR
It seems like your `time_fetcher` is using a timezone aware (tzlocal) datetime, but you're losing the timezone information upon serialization, defaulting to UTC. So in my case, it's comparing EST to UTC when determining expiration time.

Pull request description:
Add missing % in datetime format string
Fixes #869 

cc @jamesls @kyleknap 

Patch:
diff --git a/botocore/credentials.py b/botocore/credentials.py
index 76fa52f29..f5f6ff5a1 100644
--- a/botocore/credentials.py
+++ b/botocore/credentials.py
@@ -121,7 +121,7 @@ def _parse_if_needed(value):
 
 def _serialize_if_needed(value):
     if isinstance(value, datetime.datetime):
-        return value.strftime('%Y-%m-%dT%H:%M:%SZ')
+        return value.strftime('%Y-%m-%dT%H:%M:%S%Z')
     return value
 
 


Test patch:
diff --git a/tests/unit/test_credentials.py b/tests/unit/test_credentials.py
index 76a90e84d..1ca586997 100644
--- a/tests/unit/test_credentials.py
+++ b/tests/unit/test_credentials.py
@@ -15,10 +15,10 @@ from datetime import datetime, timedelta
 import mock
 import os
 
-from dateutil.tz import tzlocal
+from dateutil.tz import tzlocal, tzutc
 
 from botocore import credentials
-from botocore.credentials import EnvProvider
+from botocore.credentials import EnvProvider, create_assume_role_refresher
 import botocore.exceptions
 import botocore.session
 from tests import unittest, BaseEnvVar, IntegerRefresher
@@ -746,6 +746,23 @@ class TestAssumeRoleCredentialProvider(unittest.TestCase):
         self.assertEqual(creds.secret_key, 'bar')
         self.assertEqual(creds.token, 'baz')
 
+    def test_assume_role_refresher_serializes_datetime(self):
+        client = mock.Mock()
+        time_zone = tzutc()
+        expiration = datetime(
+            year=2016, month=11, day=6, hour=1, minute=30, tzinfo=time_zone)
+        client.assume_role.return_value = {
+            'Credentials': {
+                'AccessKeyId': 'foo',
+                'SecretAccessKey': 'bar',
+                'SessionToken': 'baz',
+                'Expiration': expiration,
+            }
+        }
+        refresh = create_assume_role_refresher(client, {})
+        expiry_time = refresh()['expiry_time']
+        self.assertEqual(expiry_time, '2016-11-06T01:30:00UTC')
+
     def test_assume_role_retrieves_from_cache(self):
         date_in_future = datetime.utcnow() + timedelta(seconds=1000)
         utc_timestamp = date_in_future.isoformat() + 'Z'