0

Here is the situation:

I have occurred_at dates stored in my database in UTC. I'm using laravel to write an API method that returns records that contain these occurred_at fields. They are currently being returned via JSON in a string format (what the PHP Carbon library outputs), ex:

"occurred_at":"2015-04-14 00:25:20" This date "occurred" at 00:25 (12:25 AM) in UTC timezone, but actually at 2015-04-13 17:25 (5:25 PM) in Pacific (PST) timezone.

I am using angular to consume the JSON, and figured I would use the angular-moment library to convert the UTC to local. But it's not working. Both the angular-moment and native angular filters output the same thing:

<span>{{evt.occurred_at | amDateFormat:'L LT'}}</span> <span>{{evt.occurred_at | date:'MM/dd/yyyy @ h:mma'}}</span>

Output:

2015-04-14 00:25:20

Still in UTC. In my angular app code I even have the UTC preprocessor setup to read them in as UTC, but they don't display as local times:

.constant('angularMomentConfig', { preprocess: 'utc' })

There HAS to be a standard way of going about this - storing datetime values in UTC in the database but displaying them as a local time to the user. Do I need make some changes server side? Client side? What is best practice to utilize the "it just works" methods, without writing a ton of code for each date I want to display?

Currently, in non-angular projects, I am using what I consider to be a 'hack' by applying a css class to every span that I want to convert the time to local:

$('.utc-dttm').each(function () {
    var t = moment.utc($(this).text()).local();
    $(this).text(t.format("L") + ' ' + t.format("LT"));
});

But I'd argue this is the wrong way of going about it.

Steven M
  • 574
  • 3
  • 18
  • 1
    I have no clue how Carbon or Angular or Moment work, but basic javascript will accept an ISO 8601 formatted date with timezone and just *know* that it is in UTC if you format it like : `2015-04-14T00:25:20Z`, which is probably simply a matter of changing the Carbon settings to output with that format. (or adding a `T` between the date and time and a `Z` at the end of your datetime strings. – Anthony Apr 14 '15 at 02:11
  • 1
    It might also be worth noting that the native `Date::toJSON` method for javascript also formats a date object in ISO 8601, so this format would be considered the most "valid" way of serving up date strings in your ajax JSON responses – Anthony Apr 14 '15 at 02:13
  • Yeah, I'm starting to think this is more of a laravel/carbon question than a client side issue – Steven M Apr 14 '15 at 02:20
  • Have you seen this? https://github.com/urish/angular-moment/issues/104 – Anthony Apr 14 '15 at 02:42
  • I don't think that the issue – Steven M Apr 14 '15 at 03:40
  • @Anthony, that did the trick - I needed to format the Carbon date as an ISO 8601 on the server side, which made the client side recognize it as a UTC date natively. Feel free to answer the question and I'll accept it. – Steven M Apr 14 '15 at 04:35
  • Sweet. In a perfect world, dates would always be stored as UTC, handled as objects, and transported as ISO8601 or similar standard, with time zones and locale formatting only applied when output to user. So good question – Anthony Apr 14 '15 at 04:52

2 Answers2

1

Since a valid JSON datetime string is formatted as ISO-8601 format, you should have Carbon convert the date values to this format before returning it to the client via toIso8601String, like:

$occurred_at->toIso8601String();

This will output the date string as

2015-04-14T00:25:20Z

which javascript and any date extension libraries should natively parse as being in UTC timezone and adjust to client timezone when output to user.

Anthony
  • 36,459
  • 25
  • 97
  • 163
  • Here's what I had to do for those interested: `Carbon\Carbon::setToStringFormat(Carbon\Carbon::ISO8601);` – Steven M Apr 14 '15 at 05:04
0

Can't you just make another function that gets the time for you? Like, first get the function to save the time:

localStorage.setItem('time', +new Date);

To convert it:

function toTimeZone(time, zone) {
var format = 'YYYY/MM/DD HH:mm:ss ZZ';
return moment(time, format).tz(zone).format(format);

}

When you want to retrieve it:

new Date(parseInt(localStorage.getItem('time')));

Sources: Convert date to another timezone in JavaScript + Store Date and Retrieve from Local Storage

Community
  • 1
  • 1
Mart N
  • 133
  • 10
  • I'm trying to avoid all that. I don't believe I should have to specify a "zone" anywhere, because the server should know it's handling UTC dates, and the client's browser should know what timezone it is in. The rest should happen seamlessly – Steven M Apr 14 '15 at 01:36
  • @StevenM Oh yea I agree with you on that. Maybe this is something more like it then? (https://bitbucket.org/pellepim/jstimezonedetect) – Mart N Apr 14 '15 at 01:42
  • This should be possible with moment.js, not another third party library. See my edit for the manual way I've been doing it in non-angular worlds. – Steven M Apr 14 '15 at 01:50
  • "the server should know it's handling UTC dates, and the client's browser should know what timezone it is in" - But how do you expect the client to know that the date being served up is in UTC without indicating a zone? – Anthony Apr 14 '15 at 01:57
  • By telling it - i.e. in the angular constant section, `preprocess` – Steven M Apr 14 '15 at 02:16