So first you need to ask yourself what you are looking for ultimately. It's really in your best interest to keep all your timestamps in standard UTC format and, if you are storing strings, use the ISO8601 standard. For fun: XKCD on ISO8601
It will make your life so much easier to pass around dates in only one format (UTC timezone / ISO8601 format if a string) until just before you display them to a user.
Only then should you format the date as a user would expect it to be formatted to represent the local time, and you should write a date formatter to ensure this conversion is always done consistently.
On the client side, convert the date to this standardized format the moment it is captured by the user and before it is sent to the server (effectively a reverse of the formatter described above).
All of your server date logic beyond that will typically be calculating time differences without needing to worry about timezone conversions. /rant
Now to discuss your question in a bit more detail and why you are facing issues.
According to moment.js docs:
By default, moment parses and displays in local time.
So when you do this:
moment.unix();
Know that this is relative to the local time. Also unix time is the number of seconds relative to one singular point in time, an epoch which happens to be January 1st, 1970 00:00:00 (when put into the UTC timezone).
To make this hit home. Use your PHP date function and input "0" as the unix timestamp. What do you get? It will depend on the default timezone set in your PHP environment.
For me, the output of the online PHP sandbox I used:
<?php echo date('Y-m-d H:i:s', 0); ?>
// 1969-12-31 16:00:00
Uh oh. What's going on here? That's before epoch time, and I definitely just put a big fat ZERO there.
So this is the importance of keeping your date formats consistent across the board. It will make your life a lot easier.
Here is what you need to do and why:
1. Get the local date's midnight. This is the first piece of information you need to capture in a moment variable because it represents what you are interested in. Once you have that, the rest is just understanding how to format that date how you want.
var lastLocalMidnight = moment().startOf('day')
var lastUTCMidnight = moment.utc().startOf('day')
For the sake of argument and your understanding, I have included another variable which captures and converts "now" into the UTC timezone before getting "midnight".
So what's the difference here? Read the moment.js docs on UTC further:
Any moment created with moment.utc() will be in UTC mode, and any moment created with moment() will not.
To switch from UTC to local time, you can use moment#utc or
moment#local.
moment.js variables have timezone information self-contained within them when you perform other operations on them. It is important to distinguish the difference between this and the standard PHP Date behavior which treats dates the same (unless you explicitly say otherwise).
The timezone being used in every case needs to be known or set. Not understanding these two behaviors completely is most likely the source of your confusion.
A "date is a date is a date". A moment in time, hence the library name "moment.js". It needs to be stated or set what timezone that date is in. There are different formats to capture this information, but just understand that "2015-10-29 04:50:06" doesn't tell you anything until you know the timezone. It's basically the same as how 12:00 doesn't tell you anything until you know AM or PM (assuming you are not using military time).
2. Now that you have a midnight representation (whichever you deem is the correct one for your purposes), you can convert that to a UNIX timestamp because it is relative to a timezone you know you are interested in and represents the exact moment you want.
console.log(lastLocalMidnight.format());
// e.g. - 2015-10-28T00:00:00-06:00
console.log(lastUTCMidnight.format());
// e.g. - 2015-10-29T00:00:00+00:00
console.log(lastLocalMidnight.unix());
// e.g. - 1446012000
console.log(lastUTCMidnight.unix());
// e.g. - 1446076800
$timezoneBeforeSet = date_default_timezone_get();
echo $timezoneBeforeSet."\n";
Notice how moment.js's format() function displays the UTC offset by default. So even printing in that way looks like the midnight you want, but you know it wouldn't end in 00:00:00 if you formatted it to be in UTC (or any timezone other than the one you requested the date in originally).
For the moment converted to UTC ahead of time, there is no offset. If you later formatted that one in your local timezone, it would not be at 00:00:00, and the output of moment.js format() would indicate a timezone offset based on the timezone you set (-06:00, for example).
3. On the PHP side of things, you have a default environmental timezone set, as we demonstrated by seeing what PHP thought of the unix timestamp "0". So, knowing a little more about what they represent now, let's take the unix timestamps we obtained from moment.js and try to format them with PHP's date.
// Timestamps obtained from moment.js
$unixTimestampLocalMidnight = 1446012000;
$unixTimestampUTCMidnight = 1446076800;
// Relative to untampered timezone set in your PHP environment
$phpLocalMidnightUnchangedTZ = date('Y-m-d H:i:s', $unixTimestampLocalMidnight);
echo $phpLocalMidnightUnchangedTZ."\n";
// 2015-10-27 23:00:00
$phpUTCMidnightUnchangedTZ = date('Y-m-d H:i:s', $unixTimestampUTCMidnight);
echo $phpUTCMidnightUnchangedTZ."\n";
// 2015-10-28 17:00:00
So yeah. Those are totally not what we were wanting, right? Actually they are. It's 23:00:00 somewhere and 17:00:00 somewhere when it's midnight somewhere else, right?
We need to be sure where PHP thinks we are right now. We could output that timezone information using Date, but we don't care to see it in our final output (understandable). So how do we find out, otherwise? Glad you asked:
$timezoneBeforeSet = date_default_timezone_get();
echo $timezoneBeforeSet."\n";
// US/Pacific
I'm in Colorado which is US/Mountain, so what's the deal? Well, this must mean the online PHP sandbox I was using is hosted over in California or something. Makes sense. Know where your code is running.
But don't get too caught up worrying about that. This is why we capture and convert to a consistent format upfront and worry about formatting only at which point we really need it. That way, if you did your conversion wrong, you only need to change it in one or two places at most.
For a complete reference to PHP timezones, click here.
4. If you have any control of your PHP environment, you should be setting the default timezone to UTC (one of the very first things you do in your project bootstrapping or in your PHP environmental config file).
// Explicitly set PHP default timezone to UTC
date_default_timezone_set('UTC');
$timezoneAfterSet = date_default_timezone_get();
echo $timezoneAfterSet."\n";
// UTC
$phpLocalMidnight = date('Y-m-d H:i:s', $unixTimestampLocalMidnight);
echo $phpLocalMidnight."\n";
// 2015-10-28 06:00:00
$phpUTCMidnight = date('Y-m-d H:i:s', $unixTimestampUTCMidnight);
echo $phpUTCMidnight."\n";
// 2015-10-29 00:00:00
So this makes sense. We are showing a time relative to UTC now. If I had originally captured a midnight in US/Mountain time, I would expect to see a time of day that corresponds to that over in Greenwich, England.
When you know a date is formatted with a timezone, think of reading that date-time in the place the timezone indicates. We know PHP is spitting out UTC stuff, so I can imagine I'm in Greenwich, England when I see the output.
If it's 06:00 in the morning and I am in Greenwich, it should be 6 hours earlier in Colorado (-6:00) <-- remember that useful timezone suffix moment.js outputs with .format()? The sun hasn't quite risen yet in Colorado because the sun rises in the East and sets in the West. So what time is 6 hours before 6AM? Midnight!
And of course the date we explicitly converted to UTC upfront comes back out looking like an actual midnight. Why? Because PHP is set to format dates in UTC. Also notice it's a day ahead. You should know enough now to explain that yourself =)
Make sense?