set datetime milliseconds precision - elixir - datetime

I am trying to get a datetime which has only 3 digits in the sub-second part.
Using timex I get the following result:
iex(12)>   {:ok, date} = Timex.format(Timex.shift(Timex.local, days: 16), "{ISO:Extended}")
{:ok, "2017-04-22T09:00:44.403879+03:00"}
How can I get something like this:
{:ok, "2017-04-22T09:00:44.403+03:00"} ?

Since Elixir 1.6.0 there is now the truncate/2 function present on modules Time, DateTime and NativeDateTime for this.
Here is an example using DateTime.truncate/2
iex(1)> dt = Timex.now()
#DateTime<2018-02-16 19:03:51.430946Z>
iex(2)> dt2 = DateTime.truncate(dt, :millisecond)
#DateTime<2018-02-16 19:03:51.430Z>

DateTime has a microsecond field which is a tuple containing the value and precision. If you change the precision to 3, you'll get 3 digits in the microsecond output. I couldn't find any function in Timex which does this, but you can modify the value manually:
iex(1)> dt = %{microsecond: {us, precision}} = Timex.now
#<DateTime(2017-04-06T08:26:24.041004Z Etc/UTC)>
iex(2)> precision
6
iex(3)> dt2 = %{dt | microsecond: {us, 3}}
#<DateTime(2017-04-06T08:26:24.041Z Etc/UTC)>
iex(4)> dt2 |> Timex.format!("{ISO:Extended}")
"2017-04-06T08:26:24.041+00:00"

Related

Create a Julia Datetime from a TimePeriod and StartDate

I want to convert an Int64 representing the number of microseconds passed since 12:00:00 midnight, January 1, 0001 (0:00:00 UTC on January 1, 0001, in the Gregorian calendar) into a Julia datetime.
julia> time = Dates.Microsecond(6369175082331949400)
julia> Dates.format(time, "yyyymmdd HH:MM:SS.sss")
If you need a DateTime, just make sure you have your Int64 correctly in milliseconds, and you can use the (undocumented) UTInstant constructor, and then later add back the fractional microseconds (comment: your example number, 6369175082331949400, seems big for recent Gregorian time in microseconds, it may be nanoseconds):
julia> using Dates
julia> t = now().instant
Dates.UTInstant{Millisecond}(63694318624788 milliseconds)
julia> dump(t)
Dates.UTInstant{Millisecond}
periods: Millisecond
value: Int64 63694318624788
julia> t2 = Dates.UTInstant(Millisecond(63691750823319))
Dates.UTInstant{Millisecond}(63691750823319 milliseconds)
julia> DateTime(t2)
2019-04-24T01:00:23.319
julia> t3 = DateTime(t2)+ Dates.Microsecond(494)
2019-04-24T01:00:23.319
You can get what you want using Dates.epochms2datetime and applying an adjustment to it for your case as shown below.
Lets take datetime_value as the date we are interested in getting:
datetime_value = Dates.DateTime(2019,1,1,0,0,0)
date_start = Dates.DateTime(1,1,1,0,0,0)
date_diff = datetime_value - date_start
This gives you a value of 63681897600000 milliseconds for date_diff. Now Dates.epochms2datetime considers start of epoch as 0000-01-01T00:00:00. So we need to add 1 Year and 1 Day to the result after using Dates.epochms2datetime to arrive at our datetime value from the milliseconds value:
julia> Dates.epochms2datetime(63681897600000) + Dates.Year(1) + Dates.Day(1)
2019-01-01T00:00:00
I'm not sure I completely understand the question, as Dates.Microsecond merely returns the Int64 value of a Date or Time. However, you can create the DateTime value from a specific date and then work from there. Subtraction is allowed for DateTime values and it returns the difference in milliseconds.
using Dates
dateThen = DateTime(1, 1, 1, 0, 0, 0)
dateNow = now(UTC)
diff = dateNow - dateThen
dump(diff * 1000)
Int64 63694261047549000 (or whatever time you run it.)
Using some of the ideas provided, I came up with:
function convert_datetime(time)::DateTime
num = div(time, 100000)
remainder = rem(time, 100000)
time = DateTime(Dates.UTInstant(Millisecond(num))) + Dates.Day(1)
# time = Dates.epochms2datetime(trade.date_time/100000) + Dates.Year(1) + Dates.Day(1)
time + Dates.Microsecond(remainder)
end

Elixir reduce to find first available date in range with Timex

I have a list of dates that represent bookings for a room. I want to be able to find out where the first available day is in the date range.
# let us assume today is the 1st Feb
today = #DateTime<2019-02-01 00:00:00Z>
# here are our booked days
booked_days = [
#DateTime<2019-02-08 00:00:00Z>,
#DateTime<2019-02-05 00:00:00Z>,
#DateTime<2019-02-03 00:00:00Z>,
#DateTime<2019-02-02 00:00:00Z>
]
What we’d like to have returned here is #DateTime<2019-02-04 00:00:00Z> because it’s the first available date.
I’ve looked at doing something like this using Enum.reduce_while in combination with Timex.Interval, but with no luck as reduce_while seems to return the interval after the first call.
today = Timex.now() |> Timex.beginning_of_day()
first_available =
Enum.reduce_while(booked_days, today, fn from, until ->
interval = Timex.Interval.new(from: from, until: until)
duration = Timex.Interval.duration(interval, :days)
if duration <= 1,
do: {:cont, from},
else: {:halt, interval}
end)
Although the answer by #Badu is correct, I’d post the solution with the desired Enum.reduce_while/3.
Elixir nowadays has great built-in support for dates in the first place, so I doubt I follow why would you need Timex. And you’d better deal with dates not with date times when it comes to booked days (unless you allow pay-per-hour bookings.) But if you want DateTimes, here you go:
# Please next time make your input available to copy-paste
[today | booked_days] =
[1, 8, 5, 3, 2]
|> Enum.map(& {{2019, 02, &1}, {0, 0, 0}}
|> NaiveDateTime.from_erl!()
|> DateTime.from_naive!("Etc/UTC"))
booked_days
|> Enum.sort(& Date.compare(&1, &2) == :lt)
|> Enum.reduce_while(today, fn d, curr ->
if Date.diff(d, curr) == 1,
do: {:cont, d},
else: {:halt, DateTime.add(curr, 3600 * 24)}
end)
#⇒ #DateTime<2019-02-04 00:00:00Z>
First, you can sort the dates in ascending order.
Then iterate the dates and check for empty intervals between dates and return the date if the date is greater than or equal to from date.
sorted_dates = Enum.sort(booked_days , fn a, b -> Timex.compare(a, b, :days)<0 end)
get_next_booking_date(sorted_dates, today)
def get_next_booking_date([], _from_date) do
nil
end
def get_next_booking_date([_last_booking_date], _from_date) do
# You can add a day to last booking date and return that date or return nil depending on your need
# Timex.add(_last_booking_date, Timex.Duration.from_days(1))
nil
end
def get_next_booking_date([next, next2 | rest], from_date) do
# add a day to the current day and check if there's an empty interval and that the empty slot is greater than from date
temp_next = Timex.add(next, Timex.Duration.from_days(1))
if Timex.compare(temp_next, next2, :days) == -1 and Timex.compare(temp_next, from_date) >= 0 do
temp_next
else
get_next_booking_date([next2 | rest], from)
end
end
Heres a version without Timex
Create an array of elements with [[1st date, 2nd date], [2nd date, 3rd date], .. [ith, (i-1)st]...] (using zip by offset 1) then find the position where the two differ by more than 1 day.
defmodule DateGetter do
def get_next_date(booked_dates) do
sorted = Enum.sort(booked_dates)
date = sorted
|> Enum.zip(Enum.drop sorted, 1) # or use Enum.chunk_every for large sizes
|> Enum.find(fn {d1, d2} -> DateTime.diff(d2, d1) > 86400 end)
case date do
nil ->
{:error, "no dates found"}
{date1, date2} ->
(DateTime.to_unix(date1) + 86400) |> DateTime.from_unix
end
end
end
# Sample input:
booked_dates =
[2,5,3,8]
|> Enum.map(fn d ->
DateTime.from_iso8601("2015-01-0#{d} 01:00:00Z")
|> elem(1)
end)
DateGetter.get_next_date booked_dates
#> {:ok, #DateTime<2015-01-04 01:00:00Z>}

How do I add xx seconds, minutes, or hours to a DateTime structure?

I'd like to add a certain period of time to an existing DateTime value (in {{Y,M,D},{H,m,s}} format), but don't see a function (such as in the Calendar module) that allows me to manipulate a DateTime value directly.
How can I add (for example) 10 seconds, 10 minutes, or 10 hours to such a value?
You can use the Calendar module to convert the DateTime to seconds, which makes it easier to add the desired seconds, minutes, hours, etc.
For example, to add 10 seconds:
Date = {{2018,8,14},{13,10,25}}.
DateInSeconds = calendar:datetime_to_gregorian_seconds(Date). % 63701471425
NewDateInSeconds = DateInSeconds + 10. % 63701471435
calendar:gregorian_seconds_to_datetime(NewDateInSeconds). % {{2018,8,14},{13,10,35}}
For 10 minutes or 10 hours, just perform a little math:
Date = {{2018,8,14},{13,10,25}}.
DateInSeconds = calendar:datetime_to_gregorian_seconds(Date). % 63701471425
NewDateInSeconds = DateInSeconds + (10 * 60 * 60). % 63701507425 (10 hours)
calendar:gregorian_seconds_to_datetime(NewDateInSeconds). % {{2018,8,14},{23,10,25}}
To make life easier, you could even create a function for this, to add additional time to (or subtract time from) an existing DateTime:
-type datetime() :: {{non_neg_integer(), pos_integer(), pos_integer()},
{non_neg_integer(), non_neg_integer(), non_neg_integer()}}.
-type timespan() :: {integer(), integer(), integer()}.
-spec add_time_to_datetime(datetime(), timespan()) -> datetime().
add_time_to_datetime(Date, {Hour, Min, Sec}) ->
DateInSeconds = calendar:datetime_to_gregorian_seconds(Date),
NewDateInSeconds = DateInSeconds + (Hour * 60 * 60) + (Min * 60) + Sec,
calendar:gregorian_seconds_to_datetime(NewDateInSeconds).
Also you can use special time managment libs such as qdate.
Example usage, adding a year, month and minute, and removing 3 days and 5 hours.
NewDate = qdate:add_date({{1, 2, -3}, {-5, 1, 0}}, {{2018, 8, 16}, {11, 0, 1}}).
If you want to accept two datetime structures and subtract the second from the first, conversion to Gregorian seconds, performing the subtraction, and then reconversion is the most common way:
sub_datetime(DT1, DT2) ->
Seconds1 = calendar:datetime_to_gregorian_seconds(DT1),
Seconds2 = calendar:datetime_to_gregorian_seconds(DT2),
Diff = Seconds1 - Seconds2,
calendar:gregorian_seconds_to_datetime(Diff).
Addition is the same thing, just with the opposite operation (and of course this becomes commutative as well).
add_datetime(DT1, DT2) ->
Seconds1 = calendar:datetime_to_gregorian_seconds(DT1),
Seconds2 = calendar:datetime_to_gregorian_seconds(DT2),
Sum = Seconds1 + Seconds2,
calendar:gregorian_seconds_to_datetime(Sum).
This works in all situations and doesn't require deciphering anything or math other than the single operation (on your behalf, anyway). You notice, of course, the opportunity here for pulling the one unique part of these two functions out -- but that sort of DRY isn't really called for with just two functions. Meh.
If you want a "list-of-args friendly" sort of way to call the above:
add_time(Years, Months, Days, Hours, Minutes, Seconds, Target) ->
AddedTime = {{Years, Months, Days}, {Hours, Minutes, Seconds}},
add_datetime(AddedTime, Target).

Go time comparison

I'm trying to create simple function just to change time zone of a time to another (Lets assume UTC to +0700 WIB). Here is the source code. I have 2 functions, first GenerateWIB which will change just your time zone into +0700 WIB with same datetime. Second is GenerateUTC which will change given time's timezone into UTC. GenerateUTC works perfectly while another is not.
expect := time.Date(2016, 12, 12, 1, 2, 3, 4, wib)
t1 := time.Date(2016, 12, 12, 1, 2, 3, 4, time.UTC)
res := GenerateWIB(t1)
if res != expect {
fmt.Printf("WIB Expect %+v, but get %+v", expect, res)
}
The res != expect always fullfilled with this result.
WIB Expect 2016-12-12 01:02:03.000000004 +0700 WIB, but get 2016-12-12 01:02:03.000000004 +0700 WIB
But it is the same time right? Did i miss something?
There is an .Equal() method to compare dates :
if !res.Equal(expect) {
...
Quoting the doc :
Note that the Go == operator compares not just the time instant but also the Location and the monotonic clock reading. Therefore, Time values should not be used as map or database keys without first guaranteeing that the identical Location has been set for all values, which can be achieved through use of the UTC or Local method, and that the monotonic clock reading has been stripped by setting t = t.Round(0). In general, prefer t.Equal(u) to t == u, since t.Equal uses the most accurate comparison available and correctly handles the case when only one of its arguments has a monotonic clock reading.
If you look at the code for the time.Time(*) struct, you can see that this struct has three private fields :
type Time struct {
...
wall uint64
ext int64
...
loc *Location
}
and the comments about those fields clearly indicate that, depending on how the Time struct was built, two Time describing the same point in time may have different values for these fields.
Running res == expect compares the values of these inner fields,
running res.Equal(expect) tries to do the thing you expect.
(*) time/time.go source code on master branch as of oct 27th, 2020
Dates in golang must be compared with Equal method. Method Date returns Time type.
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
and Time type have Equal method.
func (t Time) Equal(u Time) bool
Equal reports whether t and u represent the same time instant. Two times can be equal even if they are in different locations. For example, 6:00 +0200 CEST and 4:00 UTC are Equal. See the documentation on the Time type for the pitfalls of using == with Time values; most code should use Equal instead.
Example
package main
import (
"fmt"
"time"
)
func main() {
secondsEastOfUTC := int((8 * time.Hour).Seconds())
beijing := time.FixedZone("Beijing Time", secondsEastOfUTC)
// Unlike the equal operator, Equal is aware that d1 and d2 are the
// same instant but in different time zones.
d1 := time.Date(2000, 2, 1, 12, 30, 0, 0, time.UTC)
d2 := time.Date(2000, 2, 1, 20, 30, 0, 0, beijing)
datesEqualUsingEqualOperator := d1 == d2
datesEqualUsingFunction := d1.Equal(d2)
fmt.Printf("datesEqualUsingEqualOperator = %v\n", datesEqualUsingEqualOperator)
fmt.Printf("datesEqualUsingFunction = %v\n", datesEqualUsingFunction)
}
datesEqualUsingEqualOperator = false
datesEqualUsingFunction = true
resources
Time type documentation
Equal method documentation
time.Date

In Scala 2.8, how to access a substring by its length and starting index?

I've got date and time in separate fields, in yyyyMMdd and HHmmss formats respectively. To parse them I think to construct a yyyy-MM-ddTHH:mm:ss string and feed this to joda-time constructor. So I am looking to get 1-st 4 digits, then 2 digits starting from the index 5, etc. How to achieve this? List.fromString(String) (which I found here) seems to be broken.
The substring method certainly can get you there but String in Scala 2.8 also supports all other methods on sequences. The ScalaDoc for class StringOps gives a complete list.
In particular, the splitAt method comes in handly. Here's a REPL interaction which shows how.
scala> val ymd = "yyyyMMdd"
ymd: java.lang.String = yyyyMMdd
scala> val (y, md) = ymd splitAt 4
y: String = yyyy
md: String = MMdd
scala> val (m, d) = md splitAt 2
m: String = MM
d: String = dd
scala> y+"-"+m+"-"+d
res3: java.lang.String = yyyy-MM-dd
Just use the substring() method on the string. Note that Scala strings behave like Java strings (with some extra methods), so anything that's in java.lang.String can also be used on Scala strings.
val s = "20100903"
val t = s.substring(0, 4) // t will contain "2010"
(Note that the arguments are not length and starting index, but starting index (inclusive) and ending index (exclusive)).
But if this is about parsing dates, why don't you just use java.text.SimpleDateFormat, like you would in Java?
val s = "20100903"
val fmt = new SimpleDateFormat("yyyyMMdd")
val date = fmt.parse(s) // will give you a java.util.Date object
If you're using Joda Time, you should be able to use
val date = DateTimeFormat.forPattern("yyyyMMdd, HHmmss")
.parseDateTime(field1 + ", " + field2)
For the more general problem of parsing Strings like this, it can be helpful to use a Regex (although I wouldn't recommend it in this case):
scala> val Date = "(\\d\\d\\d\\d)(\\d\\d)(\\d\\d)".r
Date: scala.util.matching.Regex = (\d\d\d\d)(\d\d)(\d\d)
scala> "20100903" match {
| case Date(year, month, day) => year + "-" + month + "-" + day
| }
res1: java.lang.String = 2010-09-03
val field1="20100903"
val field2="100925"
val year = field1.substring(1,5)
val month = field1.substring(5,7)
val day = ...
...
val toYodaTime = year + "-" + month+"-"+day+ ...

Resources