Server Time on local environment vs. Server Time on production environment


#1

Hi all.

I’m using the same SyncedCron script on Meteor Startup when I develop locally as what’s on the production server (Ubuntu 16.04). I’m in America/New_York TZ and using Moment.js.

I also have the server configured to be in the U.S. Eastern timezone; set via:
$ sudo dpkg-reconfigure tzdata

When the app runs locally, it detects that I have an “event” correctly on server side console output:

I20180527-23:31:20.518(-4)? ===========================
I20180527-23:31:20.520(-4)? Event found for today. Event ID: ghnvLbbKFwSACg9J2
I20180527-23:31:20.555(-4)? Current time: 2018-05-27T23:31:20-04:00
I20180527-23:31:20.557(-4)? Event start: 2018-05-27T23:01:00-04:00
I20180527-23:31:20.558(-4)? Event end: 2018-05-27T23:03:00-04:00
I20180527-23:31:20.559(-4)? Event active? false
I20180527-23:31:20.561(-4)? This event occurs today but it is not active.
I20180527-23:31:20.562(-4)? ===========================

Locally, if an event is not detected for the day:

I20180527-23:33:30.187(-4)? ===========================
I20180527-23:33:30.190(-4)? Current day: Sun May 27 2018 00:00:00 GMT-0400 (EDT)
I20180527-23:33:30.193(-4)? No event scheduled for today.
I20180527-23:33:30.213(-4)? ===========================

On production, at the same time as the app running locally (mup logs --tail -f):

[66.228.42.205]===========================
[66.228.42.205]Current day: Mon May 28 2018 00:00:00 GMT+0000 (UTC)
[66.228.42.205]No event scheduled for today.
[66.228.42.205]===========================

The current day was still May 27 and not yet May 28, but due to as late as it was in the evening (11:30ish PM EST) in the example above, UTC is in May 28.

So, you can see in the "Current day: " output that the server is reading the timestamp as UTC but locally, the server is correctly reading the time as EDT.

Here’s the code used:
startup.js:

SyncedCron.add({
  name: 'Event Checker Image Poller',
  schedule(parser) {
    return parser.text('every 30 seconds');
  },
  job: function() {
    var today = moment().startOf('day'),
      today = moment(today).toDate();

    var query = {
      "eventDate" : today
    }


    var result = Event.findOne(query);

    if (result) {
      console.log('===========================');
      console.log("Event found for today. Event ID: " + result._id);

      var eventId = result._id,
        gallery = Gallery.findOne({
        galleryToEvent: eventId
        }),
        localCurrent = moment.tz("America/New_York").format(),
        eventStart = moment(result.eventStartTime).format(),
        eventEnd = moment(result.eventEndTime).format(),

        eventActive = moment(localCurrent).isBetween(eventStart, eventEnd);

        console.log("Current time: " + localCurrent);
        console.log("Event start: " + eventStart);
        console.log("Event end: " + eventEnd);
        console.log("Event active? " + eventActive);

      ////////////////////////////////////
      /////////// CHECK B2 ///////////////
      ////////////////////////////////////
      if (eventActive) {
        // do my stuff
      } else {
        console.log("This event occurs today but it is not active.");
        console.log('===========================');
      }
      ////////////////////////////////////
      ///////// END CHECK B2 /////////////
      ////////////////////////////////////
    } else {
      console.log('===========================');
      console.log("Current day: " + today);
      console.log("No event scheduled for today.");
      console.log('===========================');
    }
  }
});
SyncedCron.start();

Any suggestions or thoughts here to get production to read in EDT like it does locally?

Thanks!


#2

I do the opposite - I set my development environment to UTC so it matches the production environment. I do this by setting the environment variable: TZ=UTC before running meteor.


#3

As @wildhart says, the best way to handle time zones is to ensure everything uses UTC, with the exception of the interface with the user. For that, use moment.js to ensure that “input” and “output” dates are in the user’s local timezone.


#4

@robfallows, you are right, but I had a problem when a smartphone get timezone wrong. It was in -3, but app get local, that cause a big problem in my app.


#5

Thanks, guys.

The small business the app is for will only ever operate in the EST. timezone, so poor oversight on my part when planning to just try and design for 1 timezone.

I opt’d to do things the “right way” and use Moment’s .local() function in my calls.

So, stored in the database as UTC; and then when presented as a helper on the client:

...
var event = Event.findOne({
      _id : eventId
    });
var eventDateFormat = moment(event.eventDate).local().format("YYYY-MM-DD");

return eventDateFormat;
...

Question, though. If I’m using .local() on the server side in production (such as in my startup.js), would that be looking at the configured server’s timezone? I haven’t tested it yet, just trying to understand how .local() works in a production environment server side. This kind of circles back to my original question – why in local dev does it read on Meteor’s server side correctly as EDT but on the production server, despite the production server being configured for EST., read in UTC.

As I understand it, Moment looks at the browser’s time. There’s probably a better node-y way to handle it best server side on a remote environment. .local() works great on the client, especially factoring in Daylight Savings here on the U.S. east coast.


#6

I figured out what’s going on; at least on the server side with time.

I’m a docker nub, and using mup, the app is running in a container; I didn’t even think about the container having it’s own time configuration independent of the server itself:

$ docker exec -it [my container] date
: Sat Jun 2 20:30:07 UTC 2018

$ date (server itself)
: Sat Jun 2 16:30:10 EDT 2018

Container images are running by default in UTC.

There’s a great guide on configuring the timezone used in a docker image here.

I have after-deploy commands set up to configure this to match EDT.

Not really recommended unless you’re doing something purposeful specific to one timezone in a small app like mine; should use UTC and interpret the results as necessary using moment or moment tz.