Working with datetime in Elixir - time

I need to find a difference in minutes between the time from a database retrieved by Ecto and current time, in UTC. As far as I know, timing operating on Elixir aren't trivial without using third-party libraries such as Timex. I, however, want to avoid using third-party dependencies. So how can I find a time difference? I know I can get the current time by DateTime.utc_now(), but what's next, how to subtract a date-time from a database, which is in Ecto.DateTime format, from it?

I believe there are plans soon for Ecto to use the native Elixir Datetime format for the time being, I know your pain.
One solution is to convert the ecto date time to the erlang date time format:
{{YYYY, MM, DD}, {HH, MM, SS}}
And then compare that using the erlang calendar library. For example, say we had a Post model and we wanted to know how long ago it was updated:
Repo.Post.get!(%Post{}, 1).created_at
|> Ecto.DateTime.to_erl
|> :calendar.time_difference(:calendar.universal_time)
So let's say this post was created roughly 1 month ago (2016-10-25T10:24:23).
Running it through the above function would return:
{30, {17, 30, 53}}
Meaning 30 days, 17 hours, 30 minutes and 53 seconds ago.
You can easily from there destructure the tuple and take only the components you need (in your case the minutes).
E.g.
{_, {_h, minutes, _s}} = time_diff

Related

Convert Apple Cocoa Core Data timestamp in Excel

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)

Date logic puzzle: calculating UTC equivalent to yesterday local time at 8 AM

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();

Convert Jira's time tracking field format to hours

I am using JIRA api to extract timespent by users on issues. The time spent is in the format:
"12w 1d 2h 5m" or "12h" or "12m" etc
The API isn't exporting the number of hours.
Is there a quick (requires no effort on my part) way to convert this to hours (or seconds). I suppose this is some sort of a standard format, is there a name for it?
I know how to do this myself, just don't want to reinvent the wheel
The chronic_duration gem can parse that into seconds:
ChronicDuration.parse("12w 1d 2h 5m") # => 7351500
As far as I know that format isn't a standard. ISO 8601 does include a format for durations, but that ain't it.
Take a look at JiraDurationUtils. It will use the Time Tracking configuration to convert properly.

Parse DateTime when given as a Number/Double

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.

Exporting to Excel - What to do with timestamps?

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.

Resources