Android Tutorial [B4X] DateUtils - Simplifies Date and Time Calcuations

Discussion in 'Tutorials & Examples' started by Erel, Feb 12, 2013.

  1. Erel

    Erel Administrator Staff Member Licensed User

    DateUtils is a code module with a set of useful date and time related methods.
    It complements DateTime api, it doesn't replace it.

    The methods included in DateUtils:

    Code:
    'Calculates the period between two date instances. 
    'This method returns a Period type with years, months, days, hours, minutes, seconds.
    Sub PeriodBetween(Start As Long, EndTime As Long) As Period

    'Calculates the period between two date instances.
    'This method returns a Period type with days, hours, minutes, seconds
    Sub PeriodBetweenInDays (Start As Long, EndTime As Long) As Period

    'Returns the month name of the given date.
    'Similar To DateTime.GetMonth which returns the month as an integer.
    Sub GetMonthName(Ticks As Long) As String

    'Returns the day of week name.
    'Similar to DateTime.GetDayOfWeek which returns the day of week as an integer.
    Sub GetDayOfWeekName(Ticks As Long) As String

    'Returns a list with the week days names, using the device set locale.
    Sub GetDaysNames As List

    'Returns a list with the months names, using the device set locale.
    Sub GetMonthsNames As List

    'Returns the ticks value of the given date (the time will be 00:00:00).
    Sub SetDate(Years As Int, Months As Int, Days As Int) As Long

    'Returns the ticks value of the given date and time
    Sub SetDateAndTime(Years As Int, Months As Int, Days As Int, Hours As Int, Minutes As Int, Seconds As Int) As Long

    'Returns the ticks value of the given date and time with the specified time zone offset.
    'The last parameter is the time zone offset measured in hours.
    Public Sub SetDateAndTime2(Years As Int, Months As Int, Days As Int, Hours As Int, Minutes As Int, Seconds As Int, _
          TimeZone 
    As Double) As Long

    'Returns the number of days in the given month
    Sub NumberOfDaysInMonth(Month As Int, Year As Int) As Int

    'Adds a Period to the given date instance. Do not forget to assign the result.
    Sub AddPeriod(Ticks As Long, Per As PeriodAs Long

    'Tests whether the two ticks values represent the same day.
    Sub IsSameDay(Ticks1 As Long, Ticks2 As Long) As Boolean

    'Converts ticks value to unix time.
    Sub TicksToUnixTime(Ticks As Long) As Long

    'Converts unix time to ticks value.
    Sub UnixTimeToTicks(UnixTime As Long) As Long
    DateUtils includes a type named Period:
    Code:
    Type Period (Years As Int, Months As Int, _
          Days 
    As Int, Hours As Int, Minutes As Int, Seconds As Int)
    You can use PeriodBetween method to get the Period between two dates.
    AddPeriod method adds a Period to a given date instance.

    The DateUtils module is included in the attached example.

    v1.05: Adds SetDateAndTime2. Allows you to explicitly set the time zone.

    v1.03: Fixes a bug in NumberOfDaysInMonth method.

    v1.02: Fixes a locale related bug (SetDate and SetDateAndTime).

    v1.01: Adds PeriodBetweenInDays - Similar to PeriodBetween however the years and months will be zero (days field will be larger as needed).
    This version also fixes a bug in SetDateAndTime related to DST

    Starting from B4A v2.70, DateUtils is included as a library in the IDE.
     

    Attached Files:

    pesquera, DonManfred and JOTHA like this.
  2. BPak

    BPak Active Member Licensed User

    I use Days between two dates in a lot of my apps.

    Code:
    ' Get the days difference between two dates...
       
    ' y - m - d - Most recent date
    Dim newDate As Long = DateUtils.SetDate(201322)

    ' y - m - d - Earlier date
    Dim oldDate As Long = DateUtils.SetDate(2013119)

    ' = 14 days difference
    Dim daysDiff As Int = DateUtils.DaysBetween(oldDate, newDate)

    Log(daysDiff)
    :sign0142:
     
  3. Erel

    Erel Administrator Staff Member Licensed User

    PeriodBetween will return this information. The question is whether you prefer to calculate the period in days, even if the period is longer than a month?
     
  4. BPak

    BPak Active Member Licensed User

    I have to have it in DAYS as it is used to calculate values in the program.

    Thank you.
     
  5. gregwa

    gregwa Member Licensed User

    BPak,

    You can do it this way...

    Assume a "start" date of Jan 1 2012
    Assume an "end" date of Dec 31 2012
    Tics for start date would come out to 1325401200000
    Tics for end date would come out to 1356937200000
    There are 86400000 ticks per day (DateTime.TicsPerDay)
    If you subtract end date ticks from start date you get 31536000000
    Divide Tick Difference by ticks per day, you get 365 days

    startdate = 7/18/1953
    enddate = 2/15/2013
    should come up to 21,762 days not including 2/15/2013 (add one if you want that)

    Using your example code above...


    Code:
    Dim newdate As Long = dateutils.SetDate(2013,2,15)
       
    ' y - m - d - Earlier date
       Dim olddate As Long = dateutils.SetDate(1953,7,18)

       
    Log("Ticks for newDate = " & newdate)
       
    Log("Ticks for OldDate = " & olddate)
       
    Log("ticks per day = " & DateTime.ticksperday)
       
    Log("Diff between old and new date in ticks = " & (newdate-olddate))
       
       
    Dim dayspast As Int
       dayspast = (newdate-olddate)/ 
    DateTime.TicksPerDay
       
    Log(dayspast)
    So a quick sub would be:

    Code:
    Sub DaysOut(startyear, startmonth, startday, endyear, endmonth, endday As Int, IncludeEndDay As Boolean) As Int
       
    Dim newdate As Long
       
    Dim olddate As Long
       
    Dim dayspast As Int
       
    '============================
       newdate = DateUtils.SetDate(startyear,startmonth,startday)
       olddate = DateUtils.SetDate(endyear,endmonth,endday)
       dayspast = (newdate-olddate)/ 
    DateTime.TicksPerDay
       
    If IncludeEndDay = True Then
          
    If dayspast < 0 Then
             
    Return dayspast - 1
          
    Else
             
    Return dayspast + 1
          
    End If
       
    Else
          
    Return dayspast
       
    End If
    End Sub
    and your example calls would be...
    Code:
    Log("Return from DaysOut is " & DaysOut(2013,2,15,1953,7,18,False)) ' 21762
       Log("Return from second call is " & DaysOut(1953,7,18,2013,2,15,False)) '-21762
       Log("Return from third is " & DaysOut(2013,2,15,1953,7,18,True)) ' 21763
       Log("Return from final call is " & DaysOut(1953,7,18,2013,2,15,True)) '-21763
     
    Last edited: Feb 15, 2013
  6. BPak

    BPak Active Member Licensed User

    Gregwa

    Thanks for the solution.
    :eek:
     
  7. gregwa

    gregwa Member Licensed User

    No Problem
     
  8. IanMc

    IanMc Well-Known Member Licensed User

    Wow! Thanks Erel!

    :sign0142:

    Looky here:

    new Sub Activity_Create(FirstTime As Boolean)
    Code:
    Sub Activity_Create(FirstTime As Boolean)
       
    ToastMessageShow("Check the logs..."True)
       
    Log("Today is " & DateUtils.GetDayOfWeekName(DateTime.Now))
       
    Log("There are " & DateUtils.NumberOfDaysInMonth(DateTime.GetMonth(DateTime.Now), _
          
    DateTime.GetYear(DateTime.Now)) & " days in this month.")
       
    Dim targetDate As Long = DateUtils.SetDate(201411)
       
    Dim timeToDate As Period = DateUtils.PeriodBetween(DateTime.Now, targetDate)
       
    Log(timeToDate)
       
    Log("Time left to 2014: " & timeToDate.Months & " months, " & timeToDate.Days & " days")
       
    'Ian Code
       ' y - m - d - Target date
       Dim expiryDate As Long = DateUtils.SetDate(2013314'I want the expiry date for this app to be halfway through March
       Dim betweenNowAndThen As Period = DateUtils.PeriodBetween(DateTime.Now, expiryDate)
       
    Log(" ")
       
    Log("Have fun with this app but know that it will expire at " & ampmTime(expiryDate) & " on " & DateUtils.GetDayOfWeekName(expiryDate) & _ 
       
    " the " & daySuffix(DateTime.GetDayOfMonth(expiryDate)) & " of " & DateUtils.GetMonthName(expiryDate) & ", " & DateTime.GetYear(expiryDate))

       
    Log("Although you can cheat and keep on using it if you are willing to set the Android device time to any date/time before this one.")
       
    Log("But only loo, hooo, hoooosers would do that :)")
       
    Log(" ")
       
    Log("Now Int = " & DateTime.Now)
       
    Log("expiryDate Int = " & expiryDate)
       
    Log("Period Between Now and expiryDate = " & betweenNowAndThen)
       
    Log(" ")

       
    If DateTime.Now >= expiryDate Then
          
    Log("Time's up suckers!")
          
    Log("Show Sorry, this app has expired Activity")
          
    Log("Quit app")
          
    Log(" ")
       
    Else
          
    Log("You have " & betweenNowAndThen.Days & " days of your trial period left!")
          
    Log(" ")   
       
    End If   
    End Sub
    and IanCode:
    Code:
    Sub ampmTime(dt As Long) As String
            
    Dim h As Int
        
    Dim i As Double
            i = 
    DateTime.GetMinute(dt)
            h = 
    DateTime.GetHour(dt)
            
    If h <= 11 Then
                    
    If h = 0 Then h = 12 'coz we can't have hours as zero :)
                    Return h & ":" & NumberFormat(i, 20) & "am"
            
    End If
            h = h - 
    12
            
    If h = 0 Then h = 12 'coz we can't have hours as zero :)
            Return h & ":" & NumberFormat(i, 20) & "pm"
    End Sub

    Sub daySuffix(i As Int) As String 'add suffix to integer such as 10th 3rd 1st 2022nd etc.
            Dim s As String
            
    Dim j As Int
            s = i 
    'change the int to a string
            j = i 'make a copy of i that we can chop up
            If s.Length > 2 Then s = s.SubString(s.Length - 2)
            j = s 
    'j is now a number that is no bigger than 2 digits
            If j >= 4 AND j <= 20 Then Return i & "th" 'these last few
            If s.EndsWith("1"Then Return i & "st"    'lines work out
            If s.EndsWith("2"Then Return i & "nd"    'the correct suffix
            If s.EndsWith("3"Then Return i & "rd"    'and send back the
            Return i & "th" 'original number as a string with the correct suffix after it.
    End Sub
    :sign0087:
     
  9. IanMc

    IanMc Well-Known Member Licensed User

    oh darn

    Oh :signOops:

    If I increase the expiry date by one month:

    Dim expiryDate As Long = DateUtils.SetDate(2013, 4, 14)

    It tells me I have 20 days left .... hmmmm... what am I doing wrong here?
     
  10. IanMc

    IanMc Well-Known Member Licensed User

    Still not resolved.

    Can anyone spot the error?
    Code:
    Sub Activity_Create(FirstTime As Boolean)
       
    ToastMessageShow("Check the logs..."True)
       
    Log("Today is " & DateUtils.GetDayOfWeekName(DateTime.Now))
       
    Log("There are " & DateUtils.NumberOfDaysInMonth(DateTime.GetMonth(DateTime.Now), _
          
    DateTime.GetYear(DateTime.Now)) & " days in this month.")
       
    Dim targetDate As Long = DateUtils.SetDate(201411)
       
    Dim timeToDate As Period = DateUtils.PeriodBetween(DateTime.Now, targetDate)
       
    Log(timeToDate)
       
    Log("Time left to 2014: " & timeToDate.Months & " months, " & timeToDate.Days & " days")
       
    Log(" ")
       
    Log(" ")
       
    'Ian Code
       ' y - m - d - Target date
       Dim expiryDate As Long = DateUtils.SetDate(2013414'I want the expiry date for this app to be halfway through April
       Dim betweenNowAndThen As Period = DateUtils.PeriodBetween(DateTime.Now, expiryDate)
       
    Log(" ")
       
    Log("According to this code, the number of days between now "

       
    Log(DateUtils.GetDayOfWeekName(DateTime.Now) & " the " & daySuffix(DateTime.GetDayOfMonth(DateTime.Now)) & " of " & _
       DateUtils.GetMonthName(
    DateTime.Now) & ", " & DateTime.GetYear(DateTime.Now))
       
    Log("and my expiry date:")

       
    Log(DateUtils.GetDayOfWeekName(expiryDate) & " the " & daySuffix(DateTime.GetDayOfMonth(expiryDate)) & " of " & _
       DateUtils.GetMonthName(expiryDate) & 
    ", " & DateTime.GetYear(expiryDate))
       
    Log("is " & betweenNowAndThen.Days)
       
    Log("???? huh? .... we have the whole month of March before April, what about those 31 days?")
          
    End Sub

    Sub Activity_Resume

    End Sub

    Sub Activity_Pause (UserClosed As Boolean)

    End Sub

    Sub daySuffix(i As Int) As String 'add suffix to integer such as 10th 3rd 1st 2022nd etc.
            Dim s As String
            
    Dim j As Int
            s = i 
    'change the int to a string
            j = i 'make a copy of i that we can chop up
            If s.Length > 2 Then s = s.SubString(s.Length - 2)
            j = s 
    'j is now a number that is no bigger than 2 digits
            If j >= 4 AND j <= 20 Then Return i & "th" 'these last few
            If s.EndsWith("1"Then Return i & "st"    'lines work out
            If s.EndsWith("2"Then Return i & "nd"    'the correct suffix
            If s.EndsWith("3"Then Return i & "rd"    'and send back the
            Return i & "th" 'original number as a string with the correct suffix after it.
    End Sub
    My part of the code prints out:

    According to this code, the number of days between now
    Monday the 25th of February, 2013
    and my expiry date:
    Sunday the 14th of April, 2013
    is 19
    ???? huh? .... we have the whole month of March before April, what about those 31 days?

    Can anyone see what I'm doing wrong here?
     
  11. Erel

    Erel Administrator Staff Member Licensed User

    Check the value of betweenNowAndThen.Months. It should be 1.
     
  12. IanMc

    IanMc Well-Known Member Licensed User

    Ah.... so we have to check the value of betweenNowAndThen.Months and add the number of days to that then?

    I think it will be a much used function because of 90 day trials etc.

    So to get the number of days I will first have to figure out if there are one or more months, and then if there are I will have to then figure out which months these are and then I will need to use DateUtils.NumberOfDaysInMonth to figure out how many days are in each of these months? Oh boy :)

    Or is there something I'm missing?

    Is there an easy way to use this to find out how many days there are between any two dates?
     
  13. Erel

    Erel Administrator Staff Member Licensed User

    It depends on your needs. If you want to show only days (without months and years) then you can use the code in post #5.
     
  14. IanMc

    IanMc Well-Known Member Licensed User

    Yes I'm just having a look at gregwa's code now.

    Thanks gregwa!

    and of course Thanks Erel!
     
  15. IanMc

    IanMc Well-Known Member Licensed User

    Yoohooo! Success!

    so, we have:
    Code:
    Sub Activity_Create(FirstTime As Boolean)
       
    ToastMessageShow("Check the logs..."True)
       
    Log("Today is " & DateUtils.GetDayOfWeekName(DateTime.Now))
       
    Log("There are " & DateUtils.NumberOfDaysInMonth(DateTime.GetMonth(DateTime.Now), _
          
    DateTime.GetYear(DateTime.Now)) & " days in this month.")
       
    Dim targetDate As Long = DateUtils.SetDate(201411)
       
    Dim timeToDate As Period = DateUtils.PeriodBetween(DateTime.Now, targetDate)
       
    Log(timeToDate)
       
    Log("Time left to 2014: " & timeToDate.Months & " months, " & timeToDate.Days & " days")
       
    Log(" ")
       
    Log(" ")
       
    'Ian Code
       ' y - m - d - Target date
       Dim expiryDate As Long = DateUtils.SetDate(2013414'I want the expiry date for this app to be halfway through March
       Dim betweenNowAndThen As Period = DateUtils.PeriodBetween(DateTime.Now, expiryDate)
       
    Log(" ")
       
    Log("According to this code, the number of days between now "

       
    Log(DateUtils.GetDayOfWeekName(DateTime.Now) & " the " & daySuffix(DateTime.GetDayOfMonth(DateTime.Now)) & " of " & _
       DateUtils.GetMonthName(
    DateTime.Now) & ", " & DateTime.GetYear(DateTime.Now))
       
    Log("and my expiry date:")

       
    Log(DateUtils.GetDayOfWeekName(expiryDate) & " the " & daySuffix(DateTime.GetDayOfMonth(expiryDate)) & " of " & _
       DateUtils.GetMonthName(expiryDate) & 
    ", " & DateTime.GetYear(expiryDate))
       
    Log("is " & DaysBetween(DateTime.Now, expiryDate, True))
       
    Log("Alrighty then!!! Thanks gregwa!!!")
          
    End Sub
    and these:
    Code:
    Sub daySuffix(i As Int) As String 'add suffix to integer such as 10th 3rd 1st 2022nd etc.
            Dim s As String
            
    Dim j As Int
            s = i 
    'change the int to a string
            j = i 'make a copy of i that we can chop up
            If s.Length > 2 Then s = s.SubString(s.Length - 2)
            j = s 
    'j is now a number that is no bigger than 2 digits
            If j >= 4 AND j <= 20 Then Return i & "th" 'these last few
            If s.EndsWith("1"Then Return i & "st"    'lines work out
            If s.EndsWith("2"Then Return i & "nd"    'the correct suffix
            If s.EndsWith("3"Then Return i & "rd"    'and send back the
            Return i & "th" 'original number as a string with the correct suffix after it.
    End Sub

    Sub DaysOut(startyear, startmonth, startday, endyear, endmonth, endday As Int, IncludeEndDay As Boolean) As Int
       
    Dim newdate As Long    
       
    Dim olddate As Long    
       
    Dim dayspast As Int    
       
    '============================    
       newdate = DateUtils.SetDate(startyear,startmonth,startday)    
       olddate = DateUtils.SetDate(endyear,endmonth,endday)    
       dayspast = (newdate-olddate)/ 
    DateTime.TicksPerDay   'I think you need to switch newdate & olddate around here. 
       If IncludeEndDay = True Then                         'so that you subtract newdate from olddate ?
          If dayspast < 0 Then            
             
    Return dayspast - 1        
                
    Else            
             
    Return dayspast + 1        
          
    End If    
       
    Else        
          
    Return dayspast    
       
    End If
    End Sub

    Sub DaysBetween(startDate, endDate As Long, IncludeEndDay As Boolean) As Int 'gregwa's code just using Longs 
       Dim dayspast As Int    
       
    '============================    
       dayspast = (endDate-startDate) / DateTime.TicksPerDay    
       
    If IncludeEndDay = True Then
          
    If dayspast < 0 Then            
             
    Return dayspast - 1        
          
    Else            
             
    Return dayspast + 1        
          
    End If    
       
    Else        
          
    Return dayspast    
       
    End If
    End Sub
    which giveth this:
    ** Activity (main) Create, isFirst = true **


    Today is Monday


    There are 28 days in this month.
    [Days=6, Hours=6, IsInitialized=true
    , Minutes=21, Months=10, Seconds=40
    , Years=0]
    Time left to 2014: 10 months, 6 days



    According to this code, the number of days between now
    Monday the 25th of February, 2013
    and my expiry date:
    Sunday the 14th of April, 2013
    is 48
    Alrighty then!!! Thanks gregwa!!!
    ** Activity (main) Resume **

    However, the website http://http://www.timeanddate.com/ begs to differ a little bit in that this part of it: Do the same calculation reckons its 'not' including the last day.... and it must be right coz its on the internet :) .... but this is close enough!
     
    Last edited: Feb 25, 2013
  16. Erel

    Erel Administrator Staff Member Licensed User

    v1.01 uploaded to first post: Adds PeriodBetweenInDays - Similar to PeriodBetween however the years and months will be zero (days field will be larger as needed).
    This version also fixes a bug in SetDateAndTime related to DST
     
  17. gregwa

    gregwa Member Licensed User

    Thanks Erel! :sign0060:
     
  18. IanMc

    IanMc Well-Known Member Licensed User

    Cool! Thanks Erel!

    :icon_clap:
     
  19. kostas3001

    kostas3001 Member Licensed User

    Hi Erel

    When I use the emulator, all is OK, but when I use my phone (Sony Experia Neo V, Android 2.3.4), there is an error in SetDateAndTime:[ Return "invalid date" + 1 'hack to throw an error]


    Data entry:

    targetDate: 24/01/1954 (dd/mm/yy)
    fechaDate: 18/03/2013 (dd/mm/yy)

    My Code:
    ---------------------------------------------------------------------------
    Sub Process_Globals

    End Sub

    Sub Globals

    Dim dias As Long
    Dim nac_anio As EditText
    Dim nac_mes As EditText
    Dim nac_dia As EditText
    Dim fecha_anio As EditText
    Dim fecha_mes As EditText
    Dim fecha_dia As EditText

    Dim targetDate As Long
    Dim fechaDate As Long
    Dim x As Int

    Dim timeToDate As Period

    End Sub

    Sub Activity_Create(FirstTime As Boolean)

    Activity.LoadLayout("prueba2")

    fecha_dia.Text=DateTime.GetDayOfMonth(DateTime.Now)
    fecha_mes.Text=DateTime.GetMonth(DateTime.Now)
    fecha_anio.Text=DateTime.GetYear(DateTime.Now)

    End Sub

    Sub Activity_Resume

    End Sub

    Sub Activity_Pause (UserClosed As Boolean)

    End Sub

    Sub Calcular_Click

    ToastMessageShow("Check the logs...", True)

    targetDate = DateUtils.SetDate(nac_anio.Text, nac_mes.Text, nac_dia.Text)
    fechaDate = DateUtils.SetDate(fecha_anio.Text, fecha_mes.Text, fecha_dia.Text)

    timeToDate = DateUtils.PeriodBetweenInDays(targetDate, fechaDate)

    dias=timeToDate.Days

    End Sub
    --------------------------------------------------------------------
     
  20. kostas3001

    kostas3001 Member Licensed User

    Hi Erel

    With [MENU] [Select language] = English, all works fine, but with Spanish language, neither the mobile phone nor the emulator are working...

    There is a problem with the library.
     
Loading...