sproutsocial / walltime-js

A JavaScript library for easily translating a UTC time to a "Wall Time" for a particular time zone.
MIT License
121 stars 12 forks source link

Bug typo in timezonetime.coffee #16

Closed jacobg closed 11 years ago

jacobg commented 11 years ago
toISOString: -> @walltime.toISOString()

It should read 'wallTime' (camel case), not 'walltime'

jgable commented 11 years ago

Good find. Published to npm as version 0.0.8 and available in the new client files.

Noticed you also found the timezone-js lacking in correctness, have any problems with our library yet?

jacobg commented 11 years ago

Thanks Jacob.

I'm just starting to try out walltime, so I plan to follow up with more feedback. I am likely to drop timezone-js due to it DST issues. Its code is so disorganized that I don't even want to try to fix it. Walltime looks much cleaner, and I've heard good feedback on the web. The only issue I am having with walltime is figuring out how to use the API properly, but I think as I play around with it, I'll figure it out on my own. One of the main reasons I chose timezone-js originally is because it emulates the native Date object, which made it easy to integrate with Ext JS. I'm hoping integrating walltime with Ext JS won't be too difficult either.

jgable commented 11 years ago

Cool. Thanks for the feedback.

The two main entry points for the API right now are UTCToWallTime and WallTimeToUTC.

Here are some helper functions we use on sproutsocial:

// ** These are attached to a Ss.timezone object.

// A wrapper around WallTime that allows specifying a time zone.
localDate: function localDate(timestamp, region) {
    // Default to user php zone
    region || (region = Ss.user.time_zone_daylight_savings);

    return WallTime.UTCToWallTime(timestamp, region);
},
utcDate: function utcDate(region, year, month, day, hour, minute, second, millisecond) {
    region = region || Ss.user.time_zone_daylight_savings;
    year = year || new Date().getFullYear();

    return WallTime.WallTimeToUTC(region, year, month, day, hour, minute, second, millisecond);
},

// Get a specific time of day for a time zone, based on the day you pass in.
localDateTimeOfDay: function localDateTimeOfDay(timestamp, region, hour, minute, seconds) {
    // Default to user php zone
    region || (region = Ss.user.time_zone_daylight_savings);
    // Default everything to 0.
    hour || (hour = 0);
    minute || (minute = 0);
    seconds || (seconds = 0);

    // Get walltime of passed in timestamp
    var localToday = Ss.timezone.localDate(timestamp, region),
        // Get morning of todays walltime in UTC 
        utcTimeOfDay = Ss.timezone.utcDate(region, localToday.getFullYear(), localToday.getMonth(), localToday.getDate(), hour, minute, seconds, 0);

    // Return the localDate equivalent of the utc representation of midnight.
    return Ss.timezone.localDate(utcTimeOfDay.getTime(), region);
},

// Get the morning of a specific day
localDateMorning: function localDateMorning(timestamp, region) {
    return Ss.timezone.localDateTimeOfDay(timestamp, region, 0);
},

// Add hours, minutes or days to a day and get back the timezone specific date information
localDatePlus: function localDatePlus(timestamp, region, plusYears, plusMonths, plusDays, plusHours, plusMinutes, plusSeconds, plusMilliseconds) {
    // Default to user php zone
    region || (region = Ss.user.time_zone_daylight_savings);
    // Default everything to 0
    plusYears || (plusYears = 0);
    plusMonths || (plusMonths = 0);
    plusDays || (plusDays = 0);
    plusHours || (plusHours = 0);
    plusMinutes || (plusMinutes = 0);
    plusSeconds || (plusSeconds = 0);
    plusMilliseconds || (plusMilliseconds = 0);

    // Get walltime of passed in timestamp
    var localDate = Ss.timezone.localDate(timestamp, region),
        // Get utc version with the time added.
        utcWithAdded = Ss.timezone.utcDate(region, localDate.getFullYear() + plusYears, localDate.getMonth() + plusMonths, localDate.getDate() + plusDays, localDate.getHours() + plusHours, localDate.getMinutes() + plusMinutes, localDate.getSeconds() + plusSeconds, localDate.getMilliseconds() + plusMilliseconds);

    return Ss.timezone.localDate(utcWithAdded.getTime(), region);
}

From there you can kind of extrapolate things out to do things like date ranges in a safe timezone agnostic way.

// returns the beginning of the user's day in milliseconds
beginningOfToday : function(){
    return Ss.timezone.localDateMorning(new Date().getTime()).utc.getTime();
},
beginningOfYesterday : function(){
    var today = Ss.timezone.localDate(new Date().getTime()),
        yesterdayMorningUtc = Ss.timezone.utcDate(Ss.user.time_zone_daylight_savings, today.getFullYear(), today.getMonth(), today.getDate() - 1, 0);

    return yesterdayMorningUtc.getTime();
},
endOfYesterday : function(){
    var today = Ss.timezone.localDate(new Date().getTime()),
        yesterdayMidnightUtc = Ss.timezone.utcDate(Ss.user.time_zone_daylight_savings, today.getFullYear(), today.getMonth(), today.getDate() - 1, 23, 59, 59);

    return yesterdayMidnightUtc.getTime();
},
beginningOfThisWeek : function(){
    var beginningOfToday = Ss.timezone.localDateMorning(new Date().getTime()),
        beginDate = beginningOfToday.getDate() - beginningOfToday.getDay();

    return Ss.timezone.utcDate(Ss.user.time_zone_daylight_savings, beginningOfToday.getFullYear(), beginningOfToday.getMonth(), beginDate);
},
beginningOfLastWeek : function(){
    var beginningOfToday = Ss.timezone.localDateMorning(new Date().getTime()),
        // Beginning of the week, -7 to go back one more week.
        beginDate = (beginningOfToday.getDate() - beginningOfToday.getDay()) - 7;

    return Ss.timezone.utcDate(Ss.user.time_zone_daylight_savings, beginningOfToday.getFullYear(), beginningOfToday.getMonth(), beginDate);
},
endOfLastWeek : function(){
    // Beginning of this week minus 1 day, then specify 11:59 PM
    var beginningOfToday = Ss.timezone.localDateMorning(new Date().getTime()),
        beginDate = (beginningOfToday.getDate() - beginningOfToday.getDay()) - 1;

    return Ss.timezone.utcDate(Ss.user.time_zone_daylight_savings, beginningOfToday.getFullYear(), beginningOfToday.getMonth(), beginDate, 23, 59, 59);
},
beginningOfThisMonth : function(){
    var today = Ss.timezone.localDate(new Date().getTime());

    return Ss.timezone.utcDate(Ss.user.time_zone_daylight_savings, today.getFullYear(), today.getMonth(), 1);
},
beginningOfLastMonth : function(){
    // Today, minus one month and set the date to 1.
    var today = Ss.timezone.localDate(new Date().getTime());

    return Ss.timezone.utcDate(Ss.user.time_zone_daylight_savings, today.getFullYear(), today.getMonth() - 1, 1);
},
endOfLastMonth : function(){
    // This is tricky; beginning of the current month, minus one day, 11:59 PM
    var beginOfMonth = Ss.timezone.localDate(this.beginningOfThisMonth().getTime());

    return Ss.timezone.utcDate(Ss.user.time_zone_daylight_savings, beginOfMonth.getFullYear(), beginOfMonth.getMonth(), beginOfMonth.getDate() - 1, 23, 59, 59);
}

Hope that helps.

jacobg commented 11 years ago

Thanks a lot. A few thoughts on additions that would be valuable for me: (1) more date formatting options (example here: http://docs.sencha.com/ext-js/4-2/?print=/api/Ext.Date) (2) given a TimeZoneTime, get a native Date object whose local date-time equals the wall time) (3) allow WallTimeToUTC to accept a native Date object whose local date-time equals the wall time (inverse of # 2)

If 2-3 need more elaboration, please let me know.

jacobg commented 11 years ago

FYI, your localDatePlus() method has unexpected results (unexpected for me at least) when you add a month to Jan 31, or a year to Feb 29. It will overflow to the next month in that case (e.g., March 3 instead of Feb 28).

jgable commented 11 years ago

Interesting. Well, that actually imitates what Javascript would do as well.

var d = new Date(2013, 0, 31, 8);
// Thu Jan 31 2013 08:00:00 GMT-0600 (CST)
d.setMonth(1);
// Sun Mar 03 2013 08:00:00 GMT-0600 (CST)

Dates are hard :/

For the time being we're happy to just emulate native date oddities.

jacobg commented 11 years ago

True. I'm comparing to Ext JS, which has an Ext.Date.add function that handles overflow (see http://docs.sencha.com/ext-js/4-2/source/Date2.html#Ext-Date-method-add). It's very useful, for example, if an application needs to calculate monthly or yearly periods based on a starting date.