I've got Time objects that I'm writing to an Excel file. I'm using the axlsx library. The class that converts dates to the cell data is DateTimeConverter, which turns it into a float timestamp.
The times are displayed as mm/DD/YYYY HH:MM:SS as expected, but the values are in GMT time.
Is there a way to make Excel format the times for a particular time zone, or the reader's local timezone? My current solution is to export the formatted time as a string, and I am dissatisfied with this.
Is there a way to do this without adding a VBA macro? (Please note that I'm not trying to convert the local time to GMT with a VBA macro as per the linked "duplicate" question, but rather display the GMT time to a local time - preferably without a VBA macro, if possible.)
Short answer
Excel timestamps don't know anything about time zones. If you want to export a value and display it in local time, you need to add the utc_offset to the time before you send it to Axlsx.
t = ...
excel_time = Time.at(t.to_f + t.utc_offset)
sheet.add_row ["Time:", excel_time]
Long answer
In Ruby (and many other programming languages) timestamps are represented as the number of seconds since Jan 1, 1970 00:00:00 UTC (the epoch). The Ruby Time object also retains a time zone, so that when you print it out it can display the local time. If you change the time zone, the time will be printed out differently, but the underlying number stays the same.
In Excel, timestamps are represented as the number of days since Jan 1, 1900 (or 1904, depending on the workbook settings); time is indicated as a fractional part of a day. If today's date is 41262, then 12am is 41262.0 and noon is 41262.5. There are no time zones in this scheme, time is simply a number you read off your watch.
When Axlsx exports a Ruby Time object to Excel, it calls to_f to get the time value in seconds since the epoch, then does some math to convert that to the serial number that Excel likes. Great! But it threw away the utc_offset, which is why the times are appearing in Excel as UTC.
The solution is to simply add the UTC offset to the times in your code, before you hand them over to Axlsx. For example, if you are on Eastern Standard Time, you must subtract five hours. Technically, the new time object you are creating is incorrect as far as Ruby is concerned, but we're just doing this to please Excel.
Related
I'm trying to convert the Cocoa timestamps to human-readable dates with time. For context, these numbers are coming from the history.db file within ~/Library/ for Safari. I can convert individual numbers with the awesome tool at epochconverter.com, but they do not offer a batch converter for Cocoa.
As an example, 638490901.575263 should convert to Friday, March 26, 2021 3:35:01 PM GMT-07:00. In Excel, I'm using:
=("cell reference"/86400000) + DATE(2001,1,1), but getting 1/8/01 9:21 AM. Looks like I need to add time for the Cocoa Epoch delta, but unsure how to do that.
Thanks for any help!
You have too many 0 in the formula, the number is the number of seconds since the start of 1/1/2001. There are 86400 seconds in a day. Then add the start date so we get the correct number of dates from 12/31/1899 which is what Excel uses. Then we need to subtract 7 hours to get the correct time zone difference.
=(A1/86400) + DATE(2001,1,1) - TIME(7,0,0)
Banging my head on this simple date logic problem:
I know the user's time zone offset relative to UTC.
My server is in UTC. It knows the time now.
How do I calculate UTC equivalent of 'yesterday local time at 8am'?
IN JS, I tried to do it this, but it seems to not quite work.
var yesterday_am=moment(moment(new Date()).subtract(24,'hour')).format('YYYY-MM-DD'); // same local time previous day;
// when is 8am local time in utc?
var am=8 - (user.tz_offset/60);
if (am<0) { yesterday_am=moment(yesterday_am).subtract(Math.abs(am), 'hour')};
if (am>0) { yesterday_am=moment(yesterday_am).add(am, 'hour')};
For example, if UTC is 2016-01-10 0235 and local time is -7 hours, it would output 2016-1-09 1500 (8am local).
The logic you are trying to perform is impossible. Without knowing the user's actual time zone, you cannot know the UTC equivalent of 'yesterday at 8 am' local time. Daylight saving time transitions may make the offset yesterday different than the offset today. See the time zone tag wiki, particularly the Time Zone != Offset section for more information.
If you do know the user's time zone, then you can perform this calculation using the moment timezone add-on library for Moment.js. The code would be as follows:
moment().tz('America/Los_Angeles').subtract(1, 'day')
.set({hours:8, minutes:0, seconds:0, milliseconds:0}).utc()
Breaking this down so it makes sense, we are doing the following things:
moment() //get the current time
.tz('America/Los_Angeles') //put the moment in the Los Angeles time zone
//the Los Angeles time zone is one of several that are UTC-7 right now
.subtract(1, 'day') //go to yesterday
.set({hours:8, minutes:0, seconds:0, milliseconds:0}) //set the time to exactly 8 am
.utc() //convert back to UTC
Do not add 8 hours to the start of the day. This will be 9 AM if the clocks 'sprang forward' that day, and 7 am if they 'fell back'.
It sounds like your code is running in Node on the server. If it is running in the browser, then the browser knows the user's time zone rules, and you could use the following code to get the time at 8 am yesterday:
moment().subtract(1, 'day').set({hours:8, minutes:0, seconds:0, milliseconds:0}).utc()
This code does not require the moment timezone add on because the browser knows the time zone rules for the user's local time.
If you need to get the user's time zone, you have a few options. The most reliable is to use a timezone picker built into a map to let the user choose. There are a few of these floating around the internet.
If you do not want to do that, you can use the moment.tz.guess() function to have moment timezone make an educated guess about the user's time zone using some heuristics. This function is good, but due to limitations of the browser it is impossible to make it 100% accurate.
For a whole bunch of information about handling date and time and time zones in JavaScript, you can try this talk I did at JavaScript MN a few months ago.
In addition, you might like this really excellent Pluralsight course by Matt Johnson.
This code is not DST aware (in case if you've not already figure out based on the comments below). Thank you Maggie. Please refer to complete answer from Maggie.
You can do something like this
var localTime = moment.utc("2016-06-19").utcOffset('-07:00');
var utcAt8PrevDay = moment(localTime).subtract(1,'days').startOf('day').add(8, 'hour').utc();
I am playing around with timezone and noticed something wierd.
I am currently in the BST timezone which is an hour ahead of GMT.
now := time.Now()
location, _ := time.LoadLocation("Atlantic/Cape_Verde")
timeAtZone := now.In(location)
fmt.Println(timeAtZone)
timestamp = timeAtZone.Unix()
fmt.Println(timestamp)
fmt.Println(now.Add(-time.Hour).UTC().Unix())
fmt.Println(now.UTC().Unix())
You will notice that the timestamp is that of BST my current timezone.
How do I get the timestamp of GMT???
http://play.golang.org/p/oq0IRYa0h7
Unix time is absolute. There is no "BST Unix time." There is no "Atlantic/Cape_Verde" Unix time." There is only Unix time. It is the number of seconds since a specific moment (00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, not counting leap seconds).
Time zones are related to the representation of time, not time itself. It is the same moment for you as it is for me, wherever we are in the world (leaving Einstein aside for the moment). We just happen to call that moment different things. Setting the location on a *Time just indicates how you would like to display the time. So if by "timestamp" you mean "string representing the time," you can get the UTC timestamp with time.Now().UTC().String().
Make sure you check your errors, I assume it's telling you there's an issue.
Have you checked: http://golang.org/pkg/time/#LoadLocation
Is your timezone in: $GOROOT/lib/time/zoneinfo.zip?
For me:
time.LoadLocation("CDT") // my time zone
time.LoadLocation("CST")
Both result in an error.
To get my time zone, I must do:
time.LoadLocation("America/Chicago")
Make sure f.Timezone is valid.
I am writing a python script which mainly involves decoration of console output.
I have some value called expiration_time which is in milliseconds. When I display expiration_time on console it is hard to find how much time exactly left to expire. User needs to do some calculation to know how much time is left.
So I decided to print epoch time instead. I want to do something like this:
epoch_time_at_which_expiration_will_happen = current_epoch_time + expiration_time_in_milliseconds
I want to output epoch_time_at_which_expiration_will_happen. How can I do it?
Not really sure what you are trying to output, the time at which expiration happens or the time until the expiration?
Either way, I would use a datetime object instead of the epoch time and convert the milliseconds to microseconds. The datetime objects allow you to do the math you want and to display the output formatted nicely.
https://docs.python.org/2/library/datetime.html
I am working with some date times from a piece of weather hardware which logs to a .dbf file. I can pull this up from the ruby script/web server I am using, but I get numbers such as
41836.532638889
I am unsure how the date time is represented so having a hard time of knowing how to parse it. I would preferably like to parse it in Ruby, so code would be a plus, but I could figure out how to do it if I knew how it was represented.
The date is an OLE Automation formatted date which is common in Excel. As Google says it is
An OLE Automation date is implemented as a floating-point number whose integral component is the number of days before or after midnight, 30 December 1899, and whose fractional component represents the time on that day divided by 24.
The corresponding Ruby code is
def self.convert_time(t)
Time.at((t - 25569) * 86400).utc
end
The subtraction shifts the time to the day unix time starts (1 January 1970). Unix time is in seconds so converting days to seconds: 24*60*60=86400. Converting to utc time for convenience.
Try Time.at(your_number/double). You can then format it as required.
Time.at(41836.532638889)
=> 1970-01-01 12:37:16 +0100
41836 - number of days passed from 12/31/1899 (zero-date), 532638889 - number of seconds passed from the midnight of the date mentioned.