How Can I Subtract a Specified Number of Days from a UTC Value?

ScriptingGuy1

Hey, Scripting Guy! Question

Hey, Scripting Guy! Suppose I get a date-time value in UTC format. How can I subtract x number of days from that value?

— MW

SpacerHey, Scripting Guy! AnswerScript Center

Hey, MW. First of all, we’d like to thank you for your question; in addition, we’d like to welcome you and everyone else to today’s Open House in honor of the 500th Hey, Scripting Guy! column. We know that a few of you are wondering why we’re so excited about the 500th column; isn’t 500 just another number? Well, to be honest, we’re pretty proud of this achievement. In fact, the consensus among the Scripting Guys is that producing 500 daily columns on system administration scripting just might be the greatest feat in literary history.

But don’t take our word for it. Let’s check the mail and see what some of our loyal readers have to say about this:

Hey, Scripting Guy! Congratulations on column number 500; that’s pretty good. But did you know that a South African novelist named Mary Faulkner wrote 904 books in her lifetime? Now that’s impressive.

— RS

Um, no, RS, we didn’t know about Mary Faulkner. But, then again, we write about scripting, you know, highly-technical stuff. Mary Faulkner was a novelist; how hard could it be to write 904 novels?

Let’s try another letter:

Hey, Scripting Guy! Wow: 500 columns, and all of them on highly-technical stuff like scripting. Congratulations; I can’t think of anyone who’s been able to top that achievement.

Well, OK, maybe Isaac Asimov. Did you know that, in addition to 463 books, he wrote over 1600 full-length essays on highly-technical topics? In fact, Asimov is the only writer to have published books that encompass all 10 of the major Dewey Decimal categories. On top of that, he wrote an estimated 90,000 letters and postcards during his lifetime!

— SB

Gee, that’s … great; good for him. Let’s try one more:

Hey, Scripting Guy! Although everyone is focusing on the 500th column, I happened to notice that you guys are approaching your second anniversary (August 2, 2006 to be exact). Wow: writing a column every day for the past two years is really something. Did anyone ever tell you that Dear Abby wrote her column every day for 46 years, from 1956 through 2002?

— CM

You know, rather than spend any more time on letters maybe we should get right to today’s question.

By the way, what is today’s question?

Oh, that’s right: subtracting x number of days from a UTC date-time value. For those of you who aren’t familiar with the term, UTC is short of Universal Time Coordinate; when you get a UTC value (as you often will when working with WMI) you end up with a date that looks something like this:

20060720132442.000000-420

Note. True. But many of us have had dates that looked even scarier than that. And a few of us were the dates that looked even scarier than that.

We won’t bother explaining how to decipher this value; if you have a burning desire to know how to do that then take a peek at the Microsoft Windows 2000 Scripting Guide. Instead, let’s focus on the topic at hand, subtracting a specified number of days from this UTC value. How do we do that? To tell you the truth, we have no idea.

But that’s OK: if we can’t subtract a specified number of days from our UTC value then we’ll simply turn that UTC value into something that we can subtract a specified number of days from. What we’re going to do is convert the UTC value into a regular date-time value, subtract the desired number of days, and then convert the answer back to a UTC value. This will have the desired effect, and no one will ever know that we cheated:

Const CONVERT_TO_LOCAL_TIME = True

Set dtmStart = CreateObject(“WbemScripting.SWbemDateTime”) dtmStart.SetVarDate Now, CONVERT_TO_LOCAL_TIME Wscript.Echo dtmStart

dtmRegular = dtmStart.GetVarDate(CONVERT_TO_LOCAL_TIME) Wscript.Echo dtmRegular

dtmNew = DateAdd(“d”, -37, dtmRegular) Wscript.Echo dtmNew

Set dtmEnd = CreateObject(“WbemScripting.SWbemDateTime”) dtmEnd.SetVarDate dtmNew, CONVERT_TO_LOCAL_TIME Wscript.Echo dtmEnd

A couple of notes here. First, we opted to use the SWbemDateTime object for this script. The good news is that this makes it very easy to switch back and forth between UTC dates and regular dates. The bad news? This limits this script to working on Windows XP and Windows Server 2003. Could we modify this script to work on, say, Windows 2000? Yes, but there’s quite a bit of explanation involved in doing so. For now we’ll stick with this version; if anyone needs a Windows 2000 version let us know and we’ll see what we can come up with.

Note. In the meantime, you can find functions for converting between date-time formats here. Oh: and over here, too.

Second, we should also point out that the script is a tiny bit longer than it needs to be. That’s because we periodically pause for a moment and echo back a date-time value; we do that simply so you can watch what happens as we switch from one format to another. That seemed a little more educational, and we Scripting Guys are all about education. Along the same lines, we create an extra variable or two, just so you don’t get confused as to which variable is holding which information.

Unlike people (and we aren’t mentioning any names, Mary Faulkner) who just like to show off how prolific they are. Besides, if you add up all the books the Scripting Guys have written, well, we’re only 903 behind Ms. Faulkner. We’re right behind you and gaining fast, Mary!

But back to the script. We start out by defining a constant named CONVERT_TO_LOCAL_TIME and setting the value to True; we’ll use this constant to tell the SWbemDateTime object that we want to convert our date-time value to local time (as opposed to Greenwich Mean Time).

Note. You know, we never thought about this before, but if you live in Greenwich, England it probably doesn’t matter whether you convert to local time or not. The Scripting Guy who writes this column was actually in Greenwich, England last summer. Oddly enough, he never once thought about UTC date-time values while he was there. Go figure.

After defining the constant we create an instance of the WbemScripting.SWbemDateTime object, then call the SetVarDate method to convert the current date and time to a UTC date-time value:

dtmStart.SetVarDate Now, CONVERT_TO_LOCAL_TIME

Notice that we pass SetVarDate two parameters: the date-time value to be converted, and the constant CONVERT_TO_LOCAL_TIME. We then echo back the date-time value we just created, a value that should look something like this:

20060720134533.000000-420

Of course, now that we have a UTC value we want to get rid of it; we’ve already decided that we don’t know how to subtract x number of days from something like that. Therefore, we take the object reference dtmStart and call the GetVarDate method:

dtmRegular = dtmStart.GetVarDate(CONVERT_TO_LOCAL_TIME)

This simply converts our UTC value back to a more human-friendly date-time value, a value we store in a variable named dtmRegular. We then echo back the value of dtmRegular, which should look similar to this:

7/20/2006 1:45:33 PM

Still with us? Good. Now that we have a “real” date and time to work with we can use VBScript’s DateAdd method to subtract a specified number of days (say, 37) from that value. That’s what we do here:

dtmNew = DateAdd(“d”, -37, dtmRegular)

As you can see, this isn’t especially complicated. We simply call DateAdd, passing it three parameters:

“d”, which tells the function to work with days as the unit of measure. (As opposed to hours, minutes, years, etc. For a complete list of allowed units see the Scripting Guide.)

-37, the number of days to subtract. If we wanted to add 37 days we’d simply leave off the minus sign.

dtmRegular, the variable containing our base date, the date we’re going to add our subtract time from.

The results of this function get stored in yet another variable (dtmNew), a variable which will contain a value similar to this:

6/13/2006 1:45:33 PM

And there you go: June 13, 2006 occurred 37 days prior to July 20, 2006. Not bad, huh?

Just for the heck of it, we then convert the June 13th value back to a UTC value. That’s what this line of code does:

dtmEnd.SetVarDate dtmNew, CONVERT_TO_LOCAL_TIME

In turn, that gives us a UTC value just like this one:

20060613134533.000000-420

The circle of life is complete.

If you’d just as soon forego all that echoing of intermediate stages along the way, well, here’s a condensed script that simply reports back the desired date and time in UTC format:

Const CONVERT_TO_LOCAL_TIME = True

Set dtmStart = CreateObject(“WbemScripting.SWbemDateTime”) dtmStart.SetVarDate Now, CONVERT_TO_LOCAL_TIME dtmRegular = dtmStart.GetVarDate(CONVERT_TO_LOCAL_TIME) dtmRegular = DateAdd(“d”, -37, dtmRegular) dtmStart.SetVarDate dtmRegular, CONVERT_TO_LOCAL_TIME

Wscript.Echo dtmStart

There you have it, MW: column number 500 is officially in the books. Just think: 1,101 more and we can claim to have passed Isaac Asimov. And then, 54 years from now, we’ll set our sights on Dear Abby.

You know, on second thought, maybe we’ll just try and catch Isaac Asimov and then call it good. You win, Dear Abby: you beat us fair and square.

0 comments

Discussion is closed.

Feedback usabilla icon