The following code does not seem to work correctly when comparing DateTimes under certain circumstances.
For example, when the comparison DateTime is less than 24 hours, the function will return the incorrect minutes. Is there a better way to approach this?
Public Function GetElapsedTimeAsString(givenDate As DateTime) As String
Return ConvertTimeSpanToTotalAgo(DateTime.Now.Subtract(givenDate))
End Function
Private Shared Function ConvertTimeSpanToTotalAgo(diffDate As TimeSpan) As String
Dim d As New StringBuilder()
If diffDate.Days > 0 Then
d.AppendFormat("{0} Day ago ", diffDate.Days)
ElseIf diffDate.Minutes > 0 Then
d.AppendFormat("{0} Minutes ago ", diffDate.Minutes)
ElseIf diffDate.Seconds > 0 Then
d.AppendFormat("{0} Seconds ago ", diffDate.Seconds)
ElseIf diffDate.Milliseconds > 0 Then
d.AppendFormat("Just Now", diffDate.Milliseconds)
End If
Return d.ToString()
End Function
If the number of days, minutes, seconds and milliseconds is zero, and the number of hours is nonzero, it'll report nothing, i.e. an empty string. You need to add
If diffDate.Hours > 0 Then
d.AppendFormat("{0} Hours ago ", diffDate.Hours
at the very least.
Maybe deal with pluralisation as well: you're mixing plural and singular, e.g.
If diffDate.Hours > 0 Then
Dim text = If (diffDate.Hours > 1, "Hours", "Hour")
d.AppendFormat("{0} {1} ago ", diffDate.Hours,text
Apologies if this doesn't build - I'm from a C# background, and was looking for a VB.NET equivalent to the ternary operator.
The following function will properly display the elapsed Days, Hours, Minutes, Seconds, and optionally Milliseconds between two DateTimes.
MessageBox.Show("Completed in " & GetElapedTimeDescription(DateTime.Now, DateTime.Now.AddDays(5).AddHours(1).AddMinutes(3).AddSeconds(1)) & ".")
displays: "Completed in 5 Days, 1 Hour, 3 Minutes, 1 Second."
Private Shared Function GetElapedTimeDescription(ByVal startTime As DateTime, ByVal endTime As DateTime, Optional ByVal includeMilliseconds As Boolean = False) As String
Dim ts As TimeSpan = endTime - startTime
Dim elapsed As New System.Text.StringBuilder(100)
If startTime > endTime Then
ts = startTime - endTime
End If
If ts.Days > 0 Then
elapsed.AppendFormat("{0} Day{1}, ", ts.Days, If(ts.Days > 1, "s", ""))
End If
If ts.Hours > 0 Then
elapsed.AppendFormat("{0} Hour{1}, ", ts.Hours, If(ts.Hours > 1, "s", ""))
End If
If ts.Minutes > 0 Then
elapsed.AppendFormat("{0} Minute{1}, ", ts.Minutes, If(ts.Minutes > 1, "s", ""))
End If
If ts.Seconds > 0 Then
elapsed.AppendFormat("{0} Second{1}, ", ts.Seconds, If(ts.Seconds > 1, "s", ""))
End If
If includeMilliseconds Then
If ts.Milliseconds > 0 Then
elapsed.AppendFormat("{0} Millisecond{1}, ", ts.Milliseconds, If(ts.Milliseconds > 1, "s", ""))
End If
End If
If elapsed.ToString() = "" Then
elapsed.Append("Less than 1 Second")
End If
If elapsed.ToString().EndsWith(", ") Then
elapsed.Remove(elapsed.Length - 2, 2)
End If
Return elapsed.ToString()
End Function
Related
I have a series of dates, which are recorded in both BST and GMT (depending upon the time of year). I want them to all be in GMT but I can't work out how to get Access to return the last Sunday in March (when we switch from GMT to BST) and the last Sunday in October (when we switch back).
All help appreciated!
I'm working in Access 2010.
Thank you in advance.
You can use this generic function:
' Calculates the date of the occurrence of Weekday in the month of DateInMonth.
'
' If Occurrence is 0 or negative, the first occurrence of Weekday in the month is assumed.
' If Occurrence is 5 or larger, the last occurrence of Weekday in the month is assumed.
'
' If Weekday is invalid or not specified, the weekday of DateInMonth is used.
'
' 2016-06-09. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function DateWeekdayInMonth( _
ByVal DateInMonth As Date, _
Optional ByVal Occurrence As Integer, _
Optional ByVal Weekday As VbDayOfWeek = -1) _
As Date
Const DaysInWeek As Integer = 7
Dim Offset As Integer
Dim Month As Integer
Dim Year As Integer
Dim ResultDate As Date
' Validate Weekday.
Select Case Weekday
Case _
vbMonday, _
vbTuesday, _
vbWednesday, _
vbThursday, _
vbFriday, _
vbSaturday, _
vbSunday
Case Else
' Zero, none or invalid value for VbDayOfWeek.
Weekday = VBA.Weekday(DateInMonth)
End Select
' Validate Occurence.
If Occurrence <= 0 Then
Occurrence = 1
ElseIf Occurrence > 5 Then
Occurrence = 5
End If
' Start date.
Month = VBA.Month(DateInMonth)
Year = VBA.Year(DateInMonth)
ResultDate = DateSerial(Year, Month, 1)
' Find offset of Weekday from first day of month.
Offset = DaysInWeek * (Occurrence - 1) + (Weekday - VBA.Weekday(ResultDate) + DaysInWeek) Mod DaysInWeek
' Calculate result date.
ResultDate = DateAdd("d", Offset, ResultDate)
If Occurrence = 5 Then
' The latest occurrency of Weekday is requested.
' Check if there really is a fifth occurrence of Weekday in this month.
If VBA.Month(ResultDate) <> Month Then
' There are only four occurrencies of Weekday in this month.
' Return the fourth as the latest.
ResultDate = DateAdd("d", -DaysInWeek, ResultDate)
End If
End If
DateWeekdayInMonth = ResultDate
End Function
Then:
LastSundayMarch = DateWeekdayInMonth(DateSerial(Year(SomeDateOfYear), 3, 1), 5, vbSunday)
LastSundayOctober = DateWeekdayInMonth(DateSerial(Year(SomeDateOfYear), 10, 1), 5, vbSunday)
or as functions:
Public Function DateLastSundayMarch(ByVal DateOfYear As Date) As Date
DateLastSundayMarch = DateWeekdayInMonth(DateSerial(Year(DateOfYear), 3, 1), 5, vbSunday)
End Function
Public Function DateLastSundayOctober(ByVal DateOfYear As Date) As Date
DateLastSundayOctober = DateWeekdayInMonth(DateSerial(Year(DateOfYear), 10, 1), 5, vbSunday)
End Function
Now you can have expressions like:
=DateLastSundayOctober([SomeDateField])
to be used as ControlSource for a control or in the GUI query designer.
This will work:
Option Explicit
Function getLastSundayOfMarchOfThisYear() As Date
getLastSundayOfMarchOfThisYear = getLastSundayOfMonthOfThisYear(3)
End Function
Function getLastSundayOfOctoberOfThisYear() As Date
getLastSundayOfOctoberOfThisYear = getLastSundayOfMonthOfThisYear(10)
End Function
Private Function getLastSundayOfMonthOfThisYear(month As Long) As Date
Debug.Assert month >= 1 And month <= 12
Dim i As Long, _
tmpDate As Date, _
daysInMonth As Long
daysInMonth = Day(DateSerial(year(Date), month + 1, 1) - 1)
For i = daysInMonth To 1 Step -1
tmpDate = CDate(year(Date) & "-" & month & "-" & i)
If Weekday(tmpDate) = vbSunday Then
getLastSundayOfMonthOfThisYear = tmpDate
Exit Function
End If
Next
End Function
Does anyone know if there's an actual equivalent method in VB6 for .NET's DateTime.ParseExact() method? I've tried using DateSerial(), IsDate() and CDate(), but, due to all of VB6's "helpfulness", I'm coming up with some unexpected results.
To be more specific, I'm trying to parse a text string from user input to validate whether or not it's an actual date. As an example, I'll be using the date 8/25/16. The usual expected input may or may not have delimiters between the month, day and year, so it may be entered as 82516.
Here's a sample of the code that's not working as intended (the value of 82516 is stored in the TempStr variable):
If IsDate(Format(TempStr, "#/##/##")) And IsDate(Format(TempStr, "##/#/##")) Then
TempDate = #12:00:00 AM#
ElseIf IsDate(Format(TempStr, "#/##/##")) Then
TempDate = CDate(Format(Tempstr, "#/##/##"))
ElseIf IsDate(Format(TempStr, "##/#/##")) Then
TempDate = CDate(Format(TempStr, "##/#/##"))
End If
Using the stated value, the first condition triggers. Knowing how it works, I understand why it's happening (it's "rearranging" the month, day and year to try to match a valid date), but I'm really trying to get it to parse the date in a specific order. I know that .NET's DateTime.ParseExact() method would get me there, but I have to do this in VB6 (maintaining some legacy code).
I tried using DateSerial():
If DateSerial(CInt(Right(TempStr, 2)), CInt(Left(TempStr, 1)), CInt(Mid(TempStr, 2, 2))) > #12:00:00 AM# Then
If DateSerial(CInt(Right(TempStr, 2)), CInt(Left(TempStr, 2)), CInt(Mid(TempStr, 3, 1))) > #12:00:00 AM# Then
TempDate = #12:00:00 AM#
Else
TempDate = DateSerial(CInt(Right(TempStr, 2)), CInt(Left(TempStr, 1)), CInt(Mid(TempStr, 2, 2)))
End If
Else
If DateSerial(CInt(Right(TempStr, 2)), CInt(Left(TempStr, 2)), CInt(Mid(TempStr, 3, 1))) > #12:00:00 AM# Then
TempDate = DateSerial(CInt(Right(TempStr, 2)), CInt(Left(TempStr, 2)), CInt(Mid(TempStr, 3, 1)))
Else
TempDate = #12:00:00 AM#
End If
End If
But that also comes along with an automatic correction if the values for any of the parameters fall outside of the acceptable ranges.
I also tried the following variation of the above code:
If IsDate(Format(TempStr, "m/dd/yy")) And IsDate(Format(TempStr, "mm/d/yy")) Then
...
But the first test results in an entirely different value of 3/12/26, which is WAY off from the original input.
Is there any way to accurately emulate the .NET DateTime.ParseExact() method in VB6, or am I just going to have to toss these types of user input values out as invalid/ambiguous?
I will personally write a function for ensuring the correct date is returned -
First get the string/integer, break it down into chunks and add values to those chunks and return a combined date...
Option Explicit
Public Function MakeCorrectDate()
Dim xMakeDate As Long, xDay As Integer, xMonth As Integer, xYear As Integer, xCentury As Integer, strCorrectDate As String
''xMake as long because of size, strCorrectDate as string to allow the /...
xMakeDate = txtInput.Text
''Assuming the format will ALWAYS be the same days, months and year (12/20/16) and length is ALWAYS 6...
xDay = Left$(xMakeDate, 2)
xMonth = Mid$(xMakeDate, 3, 2)
xYear = Right(xMakeDate, 2)
''First get the correct part to 1900 or 2000...
If xYear = "00" Then
xCentury = 20
ElseIf xYear < 99 And xYear > 20 Then ''Year 2000 and year 2020
xCentury = 19
Else
xCentury = 20
End If
strCorrectDate = xDay & "/" & xMonth & "/" & xCentury & xYear
txtYear.Text = strCorrectDate
End Function
Private Sub cmdGetCorrectDate_Click()
If Not Len(txtInput.Text) = 6 Then
MsgBox "Incorrect information, must be 6 or more characters."
Exit Sub
Else
Call MakeCorrectDate
End If
End Sub
Private Sub txtInput_Change()
''Ensure the user adds only numerical text...
Dim WshShell As Object
Set WshShell = CreateObject("WScript.Shell")
If Not IsNumeric(txtInput.Text) Then
WshShell.SendKeys "{BackSpace}"
End If
End Sub
Okay, so here's what I've come up with as a solution for my current needs. Similar to what #Andre-Oosthuizen posted above, I've decided to drastically simplify the validation from what I was doing before. This series of functions requires the user to enter a six-digit date (two-digit month, two-digit day, and two-digit year). I don't believe the century is going to be a factor in this specific application, so I'm going to leave that test out.
This should be acceptable to our users as they've had similar restrictions in other systems. While I'd personally prefer a more "bullet-proof" solution (such as using a DatePicker or other manipulation of the UI), I think this is going to be the most effective for our environment.
'----------------------------------------------------------------------
' LostFocus event handler for the txtEffectiveDate TextBox.
' Test for a valid date when the user attempts to leave the field.
'----------------------------------------------------------------------
Private Sub txtEffectiveDate_LostFocus()
' *********************************************************************
' ** Don't have the event handler try to do any parsing. Just pass **
' ** the .Text value to the validation function. If a date comes **
' ** back, reformat it to "look" like a date and move on. Otherwise **
' ** pop up an "error" message and return focus to the TextBox for **
' ** the user to correct their input. **
' *********************************************************************
Dim TempDate As Date
TempDate = CheckForValidDate(Me.txtEffectiveDate.Text)
If TempDate > #12:00:00 AM# Then
' If a valid Date is returned, put the formatted String value
' into the TextBox and move on.
Me.txtEffectiveDate.Text = Format(TempDate, "mm/dd/yy")
Else
' If the Date value is not valid (#12:00:00 AM#), notify the
' user and refocus on the TextBox to force the user to
' correct the input before continuing.
MsgBox "The date you entered was not valid." & vbCrLf & vbCrLf & _
"Please enter two digits for the month, two digits for the day and" & vbCrLf & _
"two digits for the year." & vbCrLf & vbCrLf & _
"For example, today's date should be entered as either " & Format(Now, "mmddyy") & vbCrLf & _
" or " & Format(Now, "mm/dd/yy") & ".", _
vbOKOnly + vbExclamation, "INVALID INPUT FORMAT"
Me.txtEffectiveDate.SetFocus
Me.txtEffectiveDate.SelStart = 0
Me.txtEffectiveDate.SelLength = Len(Me.txtEffectiveDate.Text)
End If
End Sub
'----------------------------------------------------------------------
' Attempts to convert the String input to a Date value. If the String
' value is already a Date (i.e., "1/1/16" or similar), go ahead and
' assume that the user wants that date and return it as a Date value.
' Otherwise, strip any non-numeric characters and break apart the input
' to pass along for further validation.
'----------------------------------------------------------------------
Private Function CheckForValidDate(ByVal DateStr As String) As Date
Dim TempDate As Date
If IsDate(DateStr) Then
' If the String value is already in a date format,
' just return the Date value of the String.
TempDate = CDate(DateStr)
Else
Dim TempStr As String
Dim CurrentChar As String
Dim TempYear As Integer
Dim TempMonth As Integer
Dim TempDay As Integer
Dim I As Integer
' Strip all non-numeric characters to get a purely numeric string.
For I = 1 To Len(DateStr)
CurrentChar = Mid(DateStr, I, 1)
If IsNumeric(CurrentChar) Then
TempStr = TempStr & CurrentChar
End If
Next I
' The all-numeric string should be exactly six characters
' (for this application).
If Len(Trim(TempStr)) = 6 Then
Dim NewDateStr As String
' Break the numeric string into the component parts -
' Month, Day, and Year. At six characters, there should
' be two characters for each element.
TempMonth = CInt(Left(TempStr, 2))
TempDay = CInt(Mid(TempStr, 3, 2))
TempYear = CInt(Right(TempStr, 2))
' Now pass the individual values to the second part of
' the validation to ensure each of the individual values
' falls within acceptable ranges.
NewDateStr = GetValidDateString(TempMonth, TempDay, TempYear)
' If the returned String value is not empty, then convert
' it to a Date value for returning to the calling method
If Len(Trim(NewDateStr)) > 0 Then
TempDate = CDate(NewDateStr)
End If
End If
End If
CheckForValidDate = TempDate
End Function
'----------------------------------------------------------------------
' Using numeric values for Month, Day, and Year, attempt to build a
' valid Date in mm/dd/yy format.
'----------------------------------------------------------------------
Private Function GetValidDateString(ByVal intMonth As Integer, ByVal intDay As Integer, ByVal intYear As Integer) As String
Dim ReturnStr As String
ReturnStr = ""
If intMonth >= 1 And intMonth <= 12 Then
Select Case intMonth
Case 1, 3, 5, 7, 8, 10, 12
' January, March, May, July, August, October and December
' have 31 days.
If intDay >= 1 And intDay <= 31 Then
ReturnStr = intMonth & "/" & intDay & "/" & intYear
End If
Case 4, 6, 9, 11
' April, June, September and November
' have 30 days
If intDay >= 1 And intDay <= 30 Then
ReturnStr = intMonth & "/" & intDay & "/" & intYear
End If
Case 2
' Depending on whether it is a Leap Year (every four years),
' February may have 28 or 29 days.
If intYear Mod 4 = 0 Then
If intDay >= 1 And intDay <= 29 Then
ReturnStr = intMonth & "/" & intDay & "/" & intYear
End If
Else
If intDay >= 1 And intDay <= 28 Then
ReturnStr = intMonth & "/" & intDay & "/" & intYear
End If
End If
End Select
End If
' Return the recombined string to the calling function.
GetValidDateString = ReturnStr
End Function
There's still obviously going to be some room for error, but I believe this will solve the issue for now. It's not perfect, but hopefully we'll be able to move away from this VB6 system soon. Thank you for all of the ideas and suggestions. They were very helpful in narrowing down the best solution for this specific implementation.
I have the following function which does fine at converting current time to UTC time.
Function toUtc(byVal dDate)
Dim oShell : Set oShell = CreateObject("WScript.Shell")
toUtc = dateadd("n", oShell.RegRead("HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias"), cDate(dDate))
End Function
However, I am thinking that this does not adequately handle conversion of future or historical dates to UTC, since the function would need to know the offset of the server's timezone at the time of the date that is being converted, and whether or not it was during daylight savings time or not.
How can I get around this?
I am using vbScript/Classic ASP on windows server with IIS7.5
To clarify, this has nothing to do with formatting the date. It is all about converting to UTC from the server timezone for historical dates. During Daylight savings time, the offset will be off by 60 minutes if I try to convert a datetime which occurred in standard time.
For example:
Lets say that today, 2013-02-19 (non daylight-savings time), I want to covert a timestamp from say, 2008-06-05 (during daylight savings period) from PDT (my server timezone) to UTC. using the method in my function will give me a value which is 60 minutes off from the correct value (because the CURRENT time is PST (not PDT), the offset will be incorrect for that historical date).
In other words, the timezone offset of the input date could be UTC-7 or UTC-8 depending on whether or not DST was observed on that particular date. I need to calculate the correct UTC date for the input date and I need the code to work whether or not DST is observed on current date.
Here is the solution I ended up implementing at the suggestion of #AardVark71.
I put this in my func.asp file:
<script language="javascript" runat="server">
function toUtcString(d) {
var dDate = new Date(d);
return Math.round(dDate.getTime() / 1000);
};
</script>
It outputs a timestamp value. Then from anywhere in the classic asp code, I can do this:
response.Write DateAdd("s", toUtcString(cDate("11/11/2012 06:25 PM")), "01/01/1970 00:00:00") 'expect 11/11/2012 10:25:00 PM gmt/utc time
response.Write DateAdd("s", toUtcString(cDate("06/11/2012 06:25 PM")), "01/01/1970 00:00:00") 'expect 6/11/2012 9:25:00 PM gmt/utc time
This (I believe) is simple, and it produces the expected values and fully factors in the DST.
In ASP classic you can mix VBScript and JScript code inside one page. The JScript Date object could be used for local ⇄ UTC date conversion. Complete example:
<%# language="vbscript" %>
<%
Option Explicit
Dim local_date
Dim utc_date
For Each local_date In Array("11/11/2012 06:25 PM", "06/11/2012 06:25 PM")
utc_date = local_to_utc(local_date)
Response.Write "Local: " & local_date & ", UTC: " & utc_date & vbNewLine
Next
For Each utc_date In Array("2012-11-12T02:25:00Z", "2012-06-12T01:25:00+00:00")
local_date = utc_to_local(utc_date)
Response.Write "UTC: " & utc_date & ", Local: " & local_date & vbNewLine
Next
%>
<script language="jscript" runat="server">
function local_to_utc(datestr) {
// note that this function lets JScript engine parse the date, correctly or otherwise
var date = new Date(datestr);
var result = date.getUTCFullYear() + "-" + (date.getUTCMonth() + 1) + "-" + date.getUTCDate() + "T" + date.getUTCHours() + ":" + date.getUTCMinutes() + ":" + date.getUTCSeconds();
return result.replace(/(\D)(\d)(?!\d)/g, "$10$2") + "Z";
}
function utc_to_local(datestr) {
// note that this function parses only a subset of ISO 8601 date format
var match = datestr.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?(?:Z|([-+])(\d{2}):(\d{2}))$/);
var offset = match[8] ? (match[8] === "+" ? 1 : -1) * (match[9] * 60 + match[10] * 1) : 0;
var date = new Date(Date.UTC(match[1], match[2] - 1, match[3], match[4], match[5] - offset, match[6], match[7] || 0));
var result = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
return result.replace(/(\D)(\d)(?!\d)/g, "$10$2");
}
</script>
Expected result:
Nov 11 2012 18:25 PST = Nov 12 2012 02:25 UTC
Jun 11 2012 18:25 PDT = Jun 12 2012 01:25 UTC
And the output when executed on a server using PST/PDT time:
Local: 11/11/2012 06:25 PM, UTC: 2012-11-12T02:25:00Z
Local: 06/11/2012 06:25 PM, UTC: 2012-06-12T01:25:00Z
UTC: 2012-11-12T02:25:00Z, Local: 2012-11-11 18:25:00
UTC: 2012-06-12T01:25:00+00:00, Local: 2012-06-11 18:25:00
If I not miss something, you need to get local time on remote machine and it time zone offset, right?
Here I see self-answer based on WMI Win32_TimeZone Bias property. But according to this MSDN example: "Converting Local Time to UTC Time", quote:
Use Win32_ComputerSystem CurrentTimeZone property, because it
automatically adjusts the Time Zone bias for daylight saving time
Win32_TimeZone Bias property does not.
So, next example function get current date time, and I'll leave to you to modify it for concrete date time measuring.
' "." mean local computer
WScript.Echo FormatDateTime(UTCDate("."), 0)
Function UTCDate(strComputer)
Dim objWMIService, ColDate, ColCS
On Error Resume Next
Set objWMIService = _
GetObject("winmgmts:{impersonationLevel=impersonate}!\\" _
& strComputer & "\root\cimv2")
Set ColDate = objWMIService.ExecQuery("Select * From Win32_LocalTime")
UTCDate = DateSerial(100, 1, 1)
For Each objDate In ColDate
UTCDate = DateAdd("yyyy", ObjDate.Year - 100, UTCDate)
UTCDate = DateAdd("m", ObjDate.Month - 1, UTCDate)
UTCDate = DateAdd("d", ObjDate.Day - 1, UTCDate)
UTCDate = DateAdd("h", ObjDate.Hour, UTCDate)
UTCDate = DateAdd("n", ObjDate.Minute, UTCDate)
UTCDate = DateAdd("s", ObjDate.Second, UTCDate)
Next
' http://msdn.microsoft.com/en-us/library/windows/desktop/ms696015(v=vs.85).aspx
Set ColCS = objWMIService.ExecQuery("Select * From Win32_ComputerSystem")
Dim TimeZoneOffset, LocalTimeZone
For Each LocalTimeZone In ColCS
TimeZoneOffset = LocalTimeZone.CurrentTimeZone
Next
If TimeZoneOffset < 0 Then
TimeZoneOffset = Abs(TimeZoneOffset)
Else
TimeZoneOffset = -Abs(TimeZoneOffset)
End If
UTCDate = DateAdd("n", TimeZoneOffset, UTCDate)
If Err Then UTCDate = vbNull
Set objWMIService = Nothing
End Function
[EDIT] Well, looks like should not trust on MSDN then (or maybe this work only for current time). However, I see it's not the answer you looking for. If you not find better idea, you may automate this online calculator.
P.S. Ok, strike out above code and lets try another "iteration".
Just to note that I made a code compatible with my time zone (Bulgaria), where PDT start at last Sunday of March and end at last Sunday of October. This allow me to set easy my PDT range. Shortly, the idea is to make algorithm applicable to your region. The rest is clearly, I hope.
With New DateDrill
Debug.WriteLine .UTCDate("2008-6-28")
Debug.WriteLine .UTCDate("2014-1-21")
End With
Class DateDrill
Public Function UTCDate(ByVal dtDate)
If Not IsDate(dtDate) Then Err.Raise 5
dtDate = CDate(dtDate)
Dim ZoneBias: ZoneBias = TimeZoneBias()
If IsPDT(Now) <> IsPDT(dtDate) Then
ZoneBias = ZoneBias - 60
End If
UTCDate = DateAdd("n", ZoneBias, dtDate)
End Function
Private Function IsPDT(ByVal dtDate)
If Not IsDate(dtDate) Then Err.Raise 5
dtDate = CDate(dtDate)
Dim pdtLow, pdtUpr, nDaysBack
pdtLow = DateSerial(Year(dtDate), 3, 31)
pdtUpr = DateSerial(Year(dtDate), 10, 31)
pdtLow = DateAdd("h", 2, pdtLow)
pdtUpr = DateAdd("h", 2, pdtUpr)
nDaysBack = Weekday(pdtLow) - 1
If nDaysBack <> 0 Then
pdtLow = DateAdd("d", -nDaysBack, pdtLow)
End If
nDaysBack = Weekday(pdtUpr) - 1
If nDaysBack <> 0 Then
pdtUpr = DateAdd("d", -nDaysBack, pdtUpr)
End If
IsPDT = (dtDate >= pdtLow And dtDate <= pdtUpr)
End Function
Private Function TimeZoneBias()
Dim LTZone
With GetObject("winmgmts:" & _
"{impersonationLevel=impersonate}!\\.\root\cimv2")
For Each LTZone In .ExecQuery(_
"Select * From Win32_ComputerSystem")
TimeZoneBias = LTZone.CurrentTimeZone
Next
End With
TimeZoneBias = TimeZoneBias * -1
End Function
End Class
This little collection of ASP VBScript functions may do what you need. I'm drawing a Unix Timestamp from a MySQL database. Depending on you database it may be a slightly different query, but most DBs can return a Unix Timestamp. On my server the query only takes one or two milliseconds.
Note: pquery is my own function for running SQL queries. Obviously substitute in your own method.
I'm calculating the offset each time unixTime() runs. You could of course calculate it once in a "bootstrap" file and set a global variable if you prefer.
Function unixTimestamp()
' Unix Timestamp, drawn from MySQL because ASP doesn't natively handle time zones
rs = pquery( "SELECT unix_timestamp() AS `ts`", null )
unixTimestamp = cLng( rs("ts") )
End Function
Function utc_offset()
' Offset of Server time from UTC, in seconds
dim unixNow : unixNow = unixTimestamp()
dim aspNow : aspNow = cLng( DateDiff( "s", "01/01/1970 00:00:00", now() ) )
utc_offset = cLng( ( aspNow - unixNow ))
End Function
Function unixTime( byVal dt )
dim offset : offset = utc_offset()
if( VarType( dt ) = vbDate ) then
unixTime = cLng( DateDiff( "s", "01/01/1970 00:00:00", dt ) - offset )
else
if( isDate( dt ) ) then
dt = cDate( dt )
unixTime = cLng( DateDiff( "s", "01/01/1970 00:00:00", dt ) - offset )
else
unixTime = null
end if
end if
End Function
Function datetimeFromUnix( byVal udt, offset )
datetimeFromUnix = cDate( DateAdd( "s", udt + offset , "01/01/1970 00:00:00" ) )
End Function
Now run:
utcTimestamp = unixTime( dDate )
utcDate = datetimeFromUnix( utcTimestamp, 0 ) ' UTC
localDate = datetimeFromUnix( utcTimestamp, utc_offset() ) ' back where we started
I want to find out how long time there is till a specific Weekday from Now.
I have searched everywhere, but can't really find anything.
I THINK I have to use the DateDiff-function together with the WeekDay-function somehow.
The scenario is:
I have variable varWeekDay with the day of the week, ex: 1 / 2 / 3 / 4 / 5 / 6 / 7
And another variable varStartTime with a time of the day: hh:mm
And the last variable varStopTime also with a time of the day: hh:mm
if varWeekday = Weekday(now, 2) and varStartTime < formatdatetime(now, 4) then
.... Write how long time till start in hours / minutes
elseif varWeekday = Weekday(now, 2) and varStartTime >= formatdatetime(now, 4) and varStopTime < formatdatetime(now, 4) then
response.write("Already started!")
else
.... Write how long time till start in days / hours / minutes
end if
"How long time" could be like: "2 days, 3 hours and 27 minutes from now"
The same output should be generated from a specific datetime. Ex: 06/08/2012 23:55 is "1 day and 13 minutes from now"
I really hope you guys can help! :)
I didn't fully understand what you needed with the start time and end time, but this script will tell you how much time there is between now and the start of a specific day of the week.
<%
Dim varNow : varNow = Now()
Dim varWeekday : varWeekday = 7 'This is the weekday to look for (1 is Sunday, 7 is Saturday)
'This next line sets the time to the start of the day
Dim varThisDate : varThisDate = DateSerial(Year(varNow), Month(varNow), Day(varNow))
Dim varThisWeekday
Do
varThisDate = DateAdd("d", 1, varThisDate)
varThisWeekday = Weekday(varThisDate)
If varThisWeekday = varWeekday Then
Exit Do
End If
Loop
'These next lines just convert and display the remaining time into the different units
Response.Write("Until next " & WeekdayName(varWeekday) & "<br />")
Dim varSeconds : varSeconds = DateDiff("s", varNow, varThisDate)
Dim varDays : varDays = Int(varSeconds / 60 / 60 / 24)
varSeconds = varSeconds - (varDays * 24 * 60 * 60)
Dim varHours : varHours = Int(varSeconds / 60 / 60)
varSeconds = varSeconds - (varHours * 60 * 60)
Dim varMinutes : varMinutes = Int(varSeconds / 60)
varSeconds = varSeconds - (varMinutes * 60)
Response.Write("Days: " & varDays & "<br />")
Response.Write("Hours: " & varHours & "<br />")
Response.Write("Minutes: " & varMinutes & "<br />")
Response.Write("Seconds: " & varSeconds & "<br />")
%>
I want to calculate the difference between two dates and want to convert it like 2 years, 5 months or only 3 months, or 2 days according to the difference considering all months are 30 days...
For example;
From and including: Mar 12, 2009
To, but not including : Nov 26, 2011
The output must be : 2 years, 8 months, 14 days excluding the end date.
Another example;
Start: Jan 26, 2010
End: Feb 15, 2010
Output: 20 days from the start date to the end date, but not including the end date
I can calculate the difference as month, day or hour with Datediff but the question is how to convert it to years, months and dates. It's quite complicated actually as we don't know how many days there are between two months (30,31 maybe 28 days)
I use this Classic ASP code to convert the difference but there are lot's of disadvantages.
Function Convert_Date_to_Text(tarih1,tarih2,useDates)
if (tarih1<>"" AND tarih2<>"") then
if Tarih_Araligi_Belirle(tarih1,tarih2,"day")>0 then
Date1_Year = Year(tarih1)
Date1_Month = Month(tarih1)
Date1_Day = Day(tarih1)
Date2_Year = Year(tarih2)
Date2_Month = Month(tarih2)
Date2_Day = Day(tarih2)
If (Date1_Month = 12) and (Date1_Day = 31) and
(Date2_Month = 1) and (Date2_Day = 1) Then
NoOfyears = Date2_Year - Date1_Year - 1
NoOfmonths = 0
NoOfdays = 1
Else
NoOfyears = Date2_Year - Date1_Year
NoOfmonths = Date2_Month - Date1_Month
NoOfdays = Date2_Day - Date1_Day
End If
If NoOfyears = 1 Then
FormatString = "1 year "
Else If NoOfyears <= 0 then
FormatString = ""
Else
FormatString = CStr(NoOfyears) & " years "
End If:End If
If NoOfmonths = 1 Then
FormatString = FormatString & "1 month"
Else If NoOfmonths <= 0 then
FormatString = FormatString
Else
FormatString = FormatString & CStr(NoOfmonths) & " months "
End If:End If
if useDates=1 then
If NoOfdays = 1 Then
FormatString = FormatString & "1 day"
Else If NoOfdays <= 0 Then
FormatString = FormatString
Else
FormatString = FormatString & CStr(NoOfdays) & " days"
End If:End If
end if
end if
end if
Convert_Date_to_Text = FormatString
End Function
This web site calculates the difference perfectly. TimeAndDate.Com
Note: I'm using Classic ASP for several reasons (Company limitations). Sorry for this but I need an ASP function. It looks like TimeSpan doesn't exist in ASP :(
Kind Regards
If you can convert the input strings to DateTime variables, you can try something like this:
DateTime starTime = //something;
DateTime endTime = //something;
TimeSpan oneDay = new TimeSpan(1, 0, 0, 0); //creates a timespan of 1 day
TimeSpan deltaTime = (endTime - startTime) - oneDay;
I would asume asp has the DateTime and TimeSpan variable types.
Here's a function I have used in the past. If you test it, I think you'll find it accurate. Here's where I got it from.
Function YearsMonthsDays(Date1 As Date, Date2 As Date, Optional ShowAll As _
Boolean = False, Optional Grammar As Boolean = True)
' This function returns a string "X years, Y months, Z days" showing the time
' between two dates. This function may be used in any VBA or VB project
' Date1 and Date2 must either be dates, or strings that can be implicitly
' converted to dates. If these arguments have time portions, the time portions
' are ignored. If Date1 > Date2 (after ignoring time portions), the function
' returns an empty string
' ShowAll indicates whether all portions of the string "X years, Y months, Z days"
' are included in the output. If ShowAll = True, all portions of the string are
' always included. If ShowAll = False, then if the year portion is zero the year
' part of the string is omitted, and if the year portion and month portion are both
' zero, than both year and month portions are omitted. The day portion is always
' included, and if at least one year has passed then the month portion is always
' included
' Grammar indicates whether to test years/months/days for singular or plural
' By definition, a "full month" means that the day number in Date2 is >= the day
' number in Date1, or Date1 and Date2 occur on the last days of their respective
' months. A "full year" means that 12 "full months" have passed.
' In Excel, this function is an alternative to the little-known DATEDIF. DATEDIF
' usually works well, but can create strange results when a date is at month end.
' Thus, this formula:
' =DATEDIF(A1,B1,"y") & " years, " & DATEDIF(A1,B1,"ym") & " months, " &
' DATEDIF(A1,B1,"md") & " days"
' will return "0 years, 1 months, -2 days" for 31-Jan-2006 and 1-Mar-2006.
' This function will return "0 years, 1 month, 1 day"
Dim TestYear As Long, TestMonth As Long, TestDay As Long
Dim TargetDate As Date, Last1 As Date, Last2 As Date
' Strip time portions
Date1 = Int(Date1)
Date2 = Int(Date2)
' Test for invalid dates
If Date1 > Date2 Then
YearsMonthsDays = ""
Exit Function
End If
' Test for whether the calendar year is the same
If Year(Date2) > Year(Date1) Then
' Different calendar year.
' Test to see if calendar month is the same. If it is, we have to look at the
' day to see if a full year has passed
If Month(Date2) = Month(Date1) Then
If Day(Date2) >= Day(Date1) Then
TestYear = DateDiff("yyyy", Date1, Date2)
Else
TestYear = DateDiff("yyyy", Date1, Date2) - 1
End If
' In this case, a full year has definitely passed
ElseIf Month(Date2) > Month(Date1) Then
TestYear = DateDiff("yyyy", Date1, Date2)
' A full year has not passed
Else
TestYear = DateDiff("yyyy", Date1, Date2) - 1
End If
' Calendar year is the same, so a full year has not passed
Else
TestYear = 0
End If
' Test to see how many full months have passed, in excess of the number of full
' years
TestMonth = (DateDiff("m", DateSerial(Year(Date1), Month(Date1), 1), _
DateSerial(Year(Date2), Month(Date2), 1)) + IIf(Day(Date2) >= _
Day(Date1), 0, -1)) Mod 12
' See how many days have passed, in excess of the number of full months. If the day
' number for Date2 is >= that for Date1, it's simple
If Day(Date2) >= Day(Date1) Then
TestDay = Day(Date2) - Day(Date1)
' If not, we have to test for end of the month
Else
Last1 = DateSerial(Year(Date2), Month(Date2), 0)
Last2 = DateSerial(Year(Date2), Month(Date2) + 1, 0)
TargetDate = DateSerial(Year(Date2), Month(Date2) - 1, Day(Date1))
If Last2 = Date2 Then
If TestMonth = 11 Then
TestMonth = 0
TestYear = TestYear + 1
Else
TestMonth = TestMonth + 1
End If
Else
TestDay = DateDiff("d", IIf(TargetDate > Last1, Last1, TargetDate), Date2)
End If
End If
If ShowAll Or TestYear >= 1 Then
YearsMonthsDays = TestYear & IIf(TestYear = 1 And Grammar, " year, ", _
" years, ") & TestMonth & IIf(TestMonth = 1 And Grammar, " month, ", _
" months, ") & TestDay & IIf(TestDay = 1 And Grammar, " day", " days")
Else
If TestMonth >= 1 Then
YearsMonthsDays = TestMonth & IIf(TestMonth = 1 And Grammar, " month, ", _
" months, ") & TestDay & IIf(TestDay = 1 And Grammar, " day", " days")
Else
YearsMonthsDays = TestDay & IIf(TestDay = 1 And Grammar, " day", " days")
End If
End If
End Function
How about this? (no TimeSpan but not sure if classic asp compatible)
DateTime dateTime1 = new DateTime(2003,2,2);
DateTime dateTime2 = new DateTime(2001,1,1);
int daysDiff = dateTime1.Day - dateTime2.Day;
int monthsDiff = dateTime1.Month - dateTime2.Month;
int yearsDiff = dateTime1.Year - dateTime2.Year;
if (daysDiff < 0)
{
daysDiff += DateTime.DaysInMonth(dateTime1.Year, dateTime1.Month);
monthsDiff--;
}
if (monthsDiff < 0)
{
monthsDiff += 12;
yearsDiff--;
}
Console.WriteLine(daysDiff);
Console.WriteLine(monthsDiff);
Console.WriteLine(yearsDiff);
You can subtract DateTime objects to get a TimeSpan object:
DateTime startDate = GetStartDate();
DateTime endDate = GetEndDate();
TimeSpan duration = endDate - startDate;
This article includes a DateDiff class:
// ----------------------------------------------------------------------
public void DateDiffSample()
{
DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
Console.WriteLine( "Date1: {0}", date1 );
// > Date1: 08.11.2009 07:13:59
DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
Console.WriteLine( "Date2: {0}", date2 );
// > Date2: 20.03.2011 19:55:28
DateDiff dateDiff = new DateDiff( date1, date2 );
// differences
Console.WriteLine( "DateDiff.Years: {0}", dateDiff.Years );
// > DateDiff.Years: 1
Console.WriteLine( "DateDiff.Quarters: {0}", dateDiff.Quarters );
// > DateDiff.Quarters: 5
Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
// > DateDiff.Months: 16
Console.WriteLine( "DateDiff.Weeks: {0}", dateDiff.Weeks );
// > DateDiff.Weeks: 70
Console.WriteLine( "DateDiff.Days: {0}", dateDiff.Days );
// > DateDiff.Days: 497
Console.WriteLine( "DateDiff.Weekdays: {0}", dateDiff.Weekdays );
// > DateDiff.Weekdays: 71
Console.WriteLine( "DateDiff.Hours: {0}", dateDiff.Hours );
// > DateDiff.Hours: 11940
Console.WriteLine( "DateDiff.Minutes: {0}", dateDiff.Minutes );
// > DateDiff.Minutes: 716441
Console.WriteLine( "DateDiff.Seconds: {0}", dateDiff.Seconds );
// > DateDiff.Seconds: 42986489
// elapsed
Console.WriteLine( "DateDiff.ElapsedYears: {0}", dateDiff.ElapsedYears );
// > DateDiff.ElapsedYears: 1
Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
// > DateDiff.ElapsedMonths: 4
Console.WriteLine( "DateDiff.ElapsedDays: {0}", dateDiff.ElapsedDays );
// > DateDiff.ElapsedDays: 12
Console.WriteLine( "DateDiff.ElapsedHours: {0}", dateDiff.ElapsedHours );
// > DateDiff.ElapsedHours: 12
Console.WriteLine( "DateDiff.ElapsedMinutes: {0}", dateDiff.ElapsedMinutes );
// > DateDiff.ElapsedMinutes: 41
Console.WriteLine( "DateDiff.ElapsedSeconds: {0}", dateDiff.ElapsedSeconds );
// > DateDiff.ElapsedSeconds: 29
// description
Console.WriteLine( "DateDiff.GetDescription(1): {0}", dateDiff.GetDescription( 1 ) );
// > DateDiff.GetDescription(1): 1 Year
Console.WriteLine( "DateDiff.GetDescription(2): {0}", dateDiff.GetDescription( 2 ) );
// > DateDiff.GetDescription(2): 1 Year 4 Months
Console.WriteLine( "DateDiff.GetDescription(3): {0}", dateDiff.GetDescription( 3 ) );
// > DateDiff.GetDescription(3): 1 Year 4 Months 12 Days
Console.WriteLine( "DateDiff.GetDescription(4): {0}", dateDiff.GetDescription( 4 ) );
// > DateDiff.GetDescription(4): 1 Year 4 Months 12 Days 12 Hours
Console.WriteLine( "DateDiff.GetDescription(5): {0}", dateDiff.GetDescription( 5 ) );
// > DateDiff.GetDescription(5): 1 Year 4 Months 12 Days 12 Hours 41 Mins
Console.WriteLine( "DateDiff.GetDescription(6): {0}", dateDiff.GetDescription( 6 ) );
// > DateDiff.GetDescription(6): 1 Year 4 Months 12 Days 12 Hours 41 Mins 29 Secs
} // DateDiffSample
Dim intYears
Dim intMonths
Dim intDays
Dim strDate1
Dim strDate2
Dim strAnswer
strDate1 = "01/26/2010"
strDate2 = "02/15/2010"
intYears = DateDiff("yyyy",strDate1,strDate2)
intMonths = DateDiff("m",strDate1,strDate2)
intDays = DateDiff("d",strDate1,strDate2)
strAnswer = ""
if intYears > 0 then
strAnswer = strAnswer & CStr(intYears) & "years "
end if
if intMonths > 0 then
strAnswer = strAnswer & CStr(intMonths) & "months"
end if
if intDays > 0 then
strAnswer = strAnswer & CStr(intDays) & "days"
end if
Response.Write("The difference between these two dates is " & strAnswer)